Improve push / remove push registration

This commit is contained in:
Mario Danic 2017-04-28 22:21:40 +02:00
parent c9a53cb042
commit 7f1739f5f7
9 changed files with 237 additions and 67 deletions

View file

@ -197,8 +197,8 @@ dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
compile 'com.googlecode.ez-vcard:ez-vcard:0.10.2'
// uncomment for gplay, modified
// compile 'com.google.android.gms:play-services:10.2.1'
// uncomment for gplay, modified
//compile 'com.google.android.gms:play-services:10.2.4'
compile 'org.parceler:parceler-api:1.1.6'
annotationProcessor 'org.parceler:parceler:1.1.6'

View file

@ -36,8 +36,6 @@ public class NCFirebaseInstanceIDService extends FirebaseInstanceIdService {
//You can implement this method to store the token on your server
if (!TextUtils.isEmpty(getResources().getString(R.string.push_server_url))) {
PreferenceManager.setPushToken(MainApp.getAppContext(), FirebaseInstanceId.getInstance().getToken());
PreferenceManager.setPushTokenUpdateTime(MainApp.getAppContext(), System.currentTimeMillis());
PushUtils.pushRegistrationToServer();
}
}

View file

@ -20,9 +20,12 @@
package com.owncloud.android.ui.activity;
import android.os.Bundle;
import com.owncloud.android.ui.events.TokenPushEvent;
import com.owncloud.android.utils.PushUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@ -33,4 +36,11 @@ public class ModifiedFileDisplayActivity extends FileDisplayActivity {
PushUtils.pushRegistrationToServer();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// see if there's stuff to push every time we start the app
EventBus.getDefault().post(new TokenPushEvent());
}
}

View file

@ -27,9 +27,12 @@ import android.content.Context;
import android.text.TextUtils;
import android.util.Base64;
import com.google.gson.Gson;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.PushArbitraryData;
import com.owncloud.android.db.PreferenceManager;
import com.owncloud.android.lib.common.OwnCloudAccount;
import com.owncloud.android.lib.common.OwnCloudClient;
@ -39,8 +42,12 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForNotificationsOperation;
import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForProxyOperation;
import com.owncloud.android.lib.resources.notifications.UnregisterAccountDeviceForNotificationsOperation;
import com.owncloud.android.lib.resources.notifications.UnregisterAccountDeviceForProxyOperation;
import com.owncloud.android.lib.resources.notifications.models.PushResponse;
import org.apache.commons.httpclient.HttpStatus;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@ -65,7 +72,11 @@ public class PushUtils {
private static final String KEYPAIR_PRIV_EXTENSION = ".priv";
private static final String KEYPAIR_PUB_EXTENSION = ".pub";
public static String generateSHA512Hash(String pushToken) {
public static final String KEY_PUSH = "push";
private static ArbitraryDataProvider arbitraryDataProvider;
private static String generateSHA512Hash(String pushToken) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("SHA-512");
@ -86,7 +97,7 @@ public class PushUtils {
return result.toString();
}
public static int generateRsa2048KeyPair() {
private static int generateRsa2048KeyPair() {
String keyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator
+ KEYPAIR_FOLDER;
@ -124,8 +135,58 @@ public class PushUtils {
return -2;
}
private static void deleteRegistrationForAccount(Account account) {
Context context = MainApp.getAppContext();
OwnCloudAccount ocAccount = null;
arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().getContentResolver());
try {
ocAccount = new OwnCloudAccount(account, context);
OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, context);
RemoteOperation unregisterAccountDeviceForNotificationsOperation = new
UnregisterAccountDeviceForNotificationsOperation();
RemoteOperationResult remoteOperationResult = unregisterAccountDeviceForNotificationsOperation.
execute(mClient);
if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) {
String arbitraryValue;
if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account, KEY_PUSH))) {
Gson gson = new Gson();
PushArbitraryData pushArbitraryData = gson.fromJson(arbitraryValue, PushArbitraryData.class);
RemoteOperation unregisterAccountDeviceForProxyOperation =
new UnregisterAccountDeviceForProxyOperation(context.getResources().
getString(R.string.push_server_url),
pushArbitraryData.getDeviceIdentifier(),
pushArbitraryData.getDeviceIdentifierSignature(),
pushArbitraryData.getUserPublicKey());
remoteOperationResult = unregisterAccountDeviceForProxyOperation.execute(mClient);
if (remoteOperationResult.isSuccess()) {
arbitraryDataProvider.deleteKeyForAccount(account, KEY_PUSH);
}
}
}
} catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
Log_OC.d(TAG, "Failed to find an account");
} catch (AuthenticatorException e) {
Log_OC.d(TAG, "Failed via AuthenticatorException");
} catch (IOException e) {
Log_OC.d(TAG, "Failed via IOException");
} catch (OperationCanceledException e) {
Log_OC.d(TAG, "Failed via OperationCanceledException");
}
}
public static void pushRegistrationToServer() {
String token = PreferenceManager.getPushToken(MainApp.getAppContext());
arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext().getContentResolver());
if (!TextUtils.isEmpty(MainApp.getAppContext().getResources().getString(R.string.push_server_url)) &&
!TextUtils.isEmpty(token)) {
PushUtils.generateRsa2048KeyPair();
@ -139,42 +200,56 @@ public class PushUtils {
publicKey = "-----BEGIN PUBLIC KEY-----\n" + publicKey + "\n-----END PUBLIC KEY-----\n";
Context context = MainApp.getAppContext();
String providerValue;
Gson gson = new Gson();
for (Account account : AccountUtils.getAccounts(context)) {
try {
OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, context);
if (!TextUtils.isEmpty(providerValue = arbitraryDataProvider.getValue(account, KEY_PUSH))) {
PushArbitraryData accountPushData = gson.fromJson(providerValue, PushArbitraryData.class);
if (!accountPushData.getPushToken().equals(token) && !accountPushData.isShouldBeDeleted()) {
try {
OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, context);
RemoteOperation registerAccountDeviceForNotificationsOperation =
new RegisterAccountDeviceForNotificationsOperation(pushTokenHash,
publicKey,
context.getResources().getString(R.string.push_server_url));
RemoteOperation registerAccountDeviceForNotificationsOperation =
new RegisterAccountDeviceForNotificationsOperation(pushTokenHash,
publicKey,
context.getResources().getString(R.string.push_server_url));
RemoteOperationResult remoteOperationResult = registerAccountDeviceForNotificationsOperation.
execute(mClient);
RemoteOperationResult remoteOperationResult = registerAccountDeviceForNotificationsOperation.
execute(mClient);
if (remoteOperationResult.isSuccess()) {
PushResponse pushResponse = remoteOperationResult.getPushResponseData();
if (remoteOperationResult.isSuccess()) {
PushResponse pushResponse = remoteOperationResult.getPushResponseData();
RemoteOperation registerAccountDeviceForProxyOperation = new
RegisterAccountDeviceForProxyOperation(
context.getResources().getString(R.string.push_server_url),
token, pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
pushResponse.getPublicKey());
RemoteOperation registerAccountDeviceForProxyOperation = new
RegisterAccountDeviceForProxyOperation(
context.getResources().getString(R.string.push_server_url),
token, pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
pushResponse.getPublicKey());
remoteOperationResult = registerAccountDeviceForProxyOperation.execute(mClient);
PreferenceManager.setPushTokenLastSentTime(MainApp.getAppContext(),
System.currentTimeMillis());
remoteOperationResult = registerAccountDeviceForProxyOperation.execute(mClient);
if (remoteOperationResult.isSuccess()) {
PushArbitraryData pushArbitraryData = new PushArbitraryData(token,
pushResponse.getDeviceIdentifier(), pushResponse.getSignature(),
pushResponse.getPublicKey(), false);
arbitraryDataProvider.storeOrUpdateKeyValue(account, KEY_PUSH,
gson.toJson(pushArbitraryData));
}
}
} catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
Log_OC.d(TAG, "Failed to find an account");
} catch (AuthenticatorException e) {
Log_OC.d(TAG, "Failed via AuthenticatorException");
} catch (IOException e) {
Log_OC.d(TAG, "Failed via IOException");
} catch (OperationCanceledException e) {
Log_OC.d(TAG, "Failed via OperationCanceledException");
}
} else if (accountPushData.isShouldBeDeleted()) {
deleteRegistrationForAccount(account);
}
} catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) {
Log_OC.d(TAG, "Failed to find an account");
} catch (AuthenticatorException e) {
Log_OC.d(TAG, "Failed via AuthenticatorException");
} catch (IOException e) {
Log_OC.d(TAG, "Failed via IOException");
} catch (OperationCanceledException e) {
Log_OC.d(TAG, "Failed via OperationCanceledException");
}
}
}

View file

@ -47,9 +47,19 @@ public class ArbitraryDataProvider {
this.contentResolver = contentResolver;
}
public int deleteKeyForAccount(Account account, String key) {
int result = contentResolver.delete(
ProviderMeta.ProviderTableMeta.CONTENT_URI_ARBITRARY_DATA,
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_CLOUD_ID + " = ? AND " +
ProviderMeta.ProviderTableMeta.ARBITRARY_DATA_KEY + "= ?",
new String[]{account.name, key}
);
return result;
}
public void storeOrUpdateKeyValue(Account account, String key, String newValue) {
ArbitraryDataSet data = getArbitraryDataSet(account, key);
if (data == null) {
Log_OC.v(TAG, "Adding arbitrary data with cloud id: " + account.name + " key: " + key

View file

@ -0,0 +1,81 @@
/**
* Nextcloud Android client application
*
* @author Mario Danic
* Copyright (C) 2017 Mario Danic
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.owncloud.android.datamodel;
public class PushArbitraryData {
public String pushToken;
public String deviceIdentifier;
public String deviceIdentifierSignature;
public String userPublicKey;
public boolean shouldBeDeleted;
public PushArbitraryData() {
}
public PushArbitraryData(String pushToken, String deviceIdentifier, String deviceIdentifierSignature,
String userPublicKey, boolean shouldBeDeleted) {
this.pushToken = pushToken;
this.deviceIdentifier = deviceIdentifier;
this.deviceIdentifierSignature = deviceIdentifierSignature;
this.userPublicKey = userPublicKey;
this.shouldBeDeleted = shouldBeDeleted;
}
public String getPushToken() {
return pushToken;
}
public void setPushToken(String pushToken) {
this.pushToken = pushToken;
}
public String getDeviceIdentifier() {
return deviceIdentifier;
}
public void setDeviceIdentifier(String deviceIdentifier) {
this.deviceIdentifier = deviceIdentifier;
}
public String getDeviceIdentifierSignature() {
return deviceIdentifierSignature;
}
public void setDeviceIdentifierSignature(String deviceIdentifierSignature) {
this.deviceIdentifierSignature = deviceIdentifierSignature;
}
public String getUserPublicKey() {
return userPublicKey;
}
public void setUserPublicKey(String userPublicKey) {
this.userPublicKey = userPublicKey;
}
public boolean isShouldBeDeleted() {
return shouldBeDeleted;
}
public void setShouldBeDeleted(boolean shouldBeDeleted) {
this.shouldBeDeleted = shouldBeDeleted;
}
}

View file

@ -47,33 +47,15 @@ public abstract class PreferenceManager {
private static final String PREF__LEGACY_CLEAN = "legacyClean";
private static final String PREF__AUTO_UPLOAD_UPDATE_PATH = "autoUploadPathUpdate";
private static final String PREF__PUSH_TOKEN = "pushToken";
private static final String PREF__PUSH_TOKEN_UPDATE_TIME = "pushTokenLastUpdated";
private static final String PREF__PUSH_TOKEN_LAST_REGISTRATION_TIME = "pushTokenLastSent";
public static void setPushTokenLastSentTime(Context context, long time) {
saveLongPreference(context, PREF__PUSH_TOKEN_LAST_REGISTRATION_TIME, time);
}
public static long getPushTokenLastSentTime(Context context) {
return getDefaultSharedPreferences(context).getLong(PREF__PUSH_TOKEN_LAST_REGISTRATION_TIME, -1);
}
public static void setPushToken(Context context, String pushToken) {
saveStringPreference(context, PREF__PUSH_TOKEN, pushToken);
saveStringPreferenceNow(context, PREF__PUSH_TOKEN, pushToken);
}
public static String getPushToken(Context context) {
return getDefaultSharedPreferences(context).getString(PREF__PUSH_TOKEN, "");
}
public static void setPushTokenUpdateTime(Context context, long time) {
saveLongPreference(context, PREF__PUSH_TOKEN_UPDATE_TIME, time);
}
public static long getPushTokenUpdateTime(Context context) {
return getDefaultSharedPreferences(context).getLong(PREF__PUSH_TOKEN_UPDATE_TIME, -1);
}
public static boolean instantPictureUploadEnabled(Context context) {
return getDefaultSharedPreferences(context).getBoolean(PREF__INSTANT_UPLOADING, false);
}
@ -289,6 +271,12 @@ public abstract class PreferenceManager {
appPreferences.apply();
}
private static void saveStringPreferenceNow(Context context, String key, String value) {
SharedPreferences.Editor appPreferences = getDefaultSharedPreferences(context.getApplicationContext()).edit();
appPreferences.putString(key, value);
appPreferences.commit();
}
private static void saveIntPreference(Context context, String key, int value) {
SharedPreferences.Editor appPreferences = getDefaultSharedPreferences(context.getApplicationContext()).edit();
appPreferences.putInt(key, value);

View file

@ -162,8 +162,6 @@ public abstract class FileActivity extends DrawerActivity
setAccount(account, savedInstanceState != null);
checkContactsBackupJob();
mOperationsServiceConnection = new OperationsServiceConnection();
bindService(new Intent(this, OperationsService.class), mOperationsServiceConnection,
Context.BIND_AUTO_CREATE);
@ -243,16 +241,6 @@ public abstract class FileActivity extends DrawerActivity
}
}
private void checkContactsBackupJob() {
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
if (getAccount() != null && arbitraryDataProvider.getBooleanValue(getAccount(),
ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP)) {
ContactsPreferenceActivity.startContactBackupJob(getAccount());
}
}
/**
* Getter for the main {@link OCFile} handled by the activity.
*

View file

@ -47,17 +47,22 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.gson.Gson;
import com.owncloud.android.R;
import com.owncloud.android.authentication.AccountUtils;
import com.owncloud.android.authentication.AuthenticatorActivity;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
import com.owncloud.android.datamodel.PushArbitraryData;
import com.owncloud.android.lib.common.UserInfo;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation;
import com.owncloud.android.ui.events.TokenPushEvent;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.PushUtils;
import org.greenrobot.eventbus.EventBus;
import org.parceler.Parcels;
import butterknife.BindString;
@ -349,6 +354,21 @@ public class UserInfoActivity extends FileActivity {
ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,
"false");
String arbitraryDataPushString;
if (!TextUtils.isEmpty(arbitraryDataPushString = arbitraryDataProvider.getValue(
account, PushUtils.KEY_PUSH))) {
Gson gson = new Gson();
PushArbitraryData pushArbitraryData = gson.fromJson(arbitraryDataPushString,
PushArbitraryData.class);
pushArbitraryData.setShouldBeDeleted(true);
arbitraryDataProvider.storeOrUpdateKeyValue(account, PushUtils.KEY_PUSH,
gson.toJson(pushArbitraryData));
EventBus.getDefault().post(new TokenPushEvent());
}
if (getActivity() != null && !removeDirectly) {
Bundle bundle = new Bundle();
bundle.putParcelable(KEY_ACCOUNT, Parcels.wrap(account));