From 7f1739f5f763d67721bac9a4e0f70c8e7f4fdde9 Mon Sep 17 00:00:00 2001 From: Mario Danic Date: Fri, 28 Apr 2017 22:21:40 +0200 Subject: [PATCH] Improve push / remove push registration --- build.gradle | 4 +- .../firebase/NCFirebaseInstanceIDService.java | 2 - .../activity/ModifiedFileDisplayActivity.java | 10 ++ .../com/owncloud/android/utils/PushUtils.java | 135 ++++++++++++++---- .../datamodel/ArbitraryDataProvider.java | 14 +- .../android/datamodel/PushArbitraryData.java | 81 +++++++++++ .../android/db/PreferenceManager.java | 26 +--- .../android/ui/activity/FileActivity.java | 12 -- .../android/ui/activity/UserInfoActivity.java | 20 +++ 9 files changed, 237 insertions(+), 67 deletions(-) create mode 100644 src/main/java/com/owncloud/android/datamodel/PushArbitraryData.java diff --git a/build.gradle b/build.gradle index 3490f070fb..cf7b8dd07a 100644 --- a/build.gradle +++ b/build.gradle @@ -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' diff --git a/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java b/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java index a5ab4d409a..c5f39a5070 100644 --- a/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java +++ b/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseInstanceIDService.java @@ -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(); } } diff --git a/src/gplay/java/com/owncloud/android/ui/activity/ModifiedFileDisplayActivity.java b/src/gplay/java/com/owncloud/android/ui/activity/ModifiedFileDisplayActivity.java index a2d17ca8ab..71ecfaf958 100644 --- a/src/gplay/java/com/owncloud/android/ui/activity/ModifiedFileDisplayActivity.java +++ b/src/gplay/java/com/owncloud/android/ui/activity/ModifiedFileDisplayActivity.java @@ -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()); + } + } diff --git a/src/gplay/java/com/owncloud/android/utils/PushUtils.java b/src/gplay/java/com/owncloud/android/utils/PushUtils.java index dd689c1d2a..7336c55ec8 100644 --- a/src/gplay/java/com/owncloud/android/utils/PushUtils.java +++ b/src/gplay/java/com/owncloud/android/utils/PushUtils.java @@ -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"); } } } diff --git a/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java b/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java index 9ea88704dc..7f12e445da 100644 --- a/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/ArbitraryDataProvider.java @@ -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 diff --git a/src/main/java/com/owncloud/android/datamodel/PushArbitraryData.java b/src/main/java/com/owncloud/android/datamodel/PushArbitraryData.java new file mode 100644 index 0000000000..fa5c86b78c --- /dev/null +++ b/src/main/java/com/owncloud/android/datamodel/PushArbitraryData.java @@ -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 . + */ + +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; + } +} diff --git a/src/main/java/com/owncloud/android/db/PreferenceManager.java b/src/main/java/com/owncloud/android/db/PreferenceManager.java index 182ef0a708..78c6402b3c 100644 --- a/src/main/java/com/owncloud/android/db/PreferenceManager.java +++ b/src/main/java/com/owncloud/android/db/PreferenceManager.java @@ -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); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 14d77fe7db..7d181d7772 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -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. * diff --git a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index a00c56007a..8075cf37b6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -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));