diff --git a/src/main/java/com/nextcloud/client/account/UserAccountManager.java b/src/main/java/com/nextcloud/client/account/UserAccountManager.java index 0f695f2f75ba..4f4d35ecc689 100644 --- a/src/main/java/com/nextcloud/client/account/UserAccountManager.java +++ b/src/main/java/com/nextcloud/client/account/UserAccountManager.java @@ -48,6 +48,14 @@ public interface UserAccountManager extends CurrentAccountProvider { */ void removeAllAccounts(); + /** + * Remove registered user. + * + * @param user user to remove + * @return true if account was removed successfully, false otherwise + */ + boolean removeUser(User user); + /** * Get configured NextCloud's user accounts. * diff --git a/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java b/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java index 0d26b02a6930..49d695a2f7d5 100644 --- a/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java +++ b/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java @@ -23,6 +23,9 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.app.Activity; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; @@ -43,6 +46,7 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; +import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.List; @@ -81,6 +85,18 @@ public void removeAllAccounts() { } } + @Override + public boolean removeUser(User user) { + try { + AccountManagerFuture result = accountManager.removeAccount(user.toPlatformAccount(), + null, + null); + return result.getResult(); + } catch (OperationCanceledException| AuthenticatorException| IOException ex) { + return false; + } + } + @Override @NonNull public Account[] getAccounts() { diff --git a/src/main/java/com/nextcloud/client/di/AppModule.java b/src/main/java/com/nextcloud/client/di/AppModule.java index 64c96123b448..033199561d53 100644 --- a/src/main/java/com/nextcloud/client/di/AppModule.java +++ b/src/main/java/com/nextcloud/client/di/AppModule.java @@ -52,6 +52,8 @@ import com.owncloud.android.ui.activities.data.files.FilesServiceApiImpl; import com.owncloud.android.ui.activities.data.files.RemoteFilesRepository; +import org.greenrobot.eventbus.EventBus; + import java.io.File; import javax.inject.Singleton; @@ -164,4 +166,10 @@ NotificationManager notificationManager(Context context) { AudioManager audioManager(Context context) { return (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); } + + @Provides + @Singleton + EventBus eventBus() { + return EventBus.getDefault(); + } } diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 5acc9ea4580d..fe020d438ca9 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -84,6 +84,7 @@ import com.owncloud.android.utils.SecurityUtils; import org.conscrypt.Conscrypt; +import org.greenrobot.eventbus.EventBus; import java.lang.reflect.Method; import java.security.NoSuchAlgorithmException; @@ -166,6 +167,9 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector { @Inject Clock clock; + @Inject + EventBus eventBus; + private PassCodeManager passCodeManager; @SuppressWarnings("unused") @@ -274,7 +278,8 @@ public void onCreate() { uploadsStorageManager, connectivityService, powerManagementService, - clock + clock, + eventBus ) ); diff --git a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java index ab1816122dcc..2e36c39f2d45 100644 --- a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java +++ b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java @@ -26,8 +26,6 @@ import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; import android.content.Context; import android.net.Uri; import android.os.Build; @@ -37,9 +35,11 @@ import com.evernote.android.job.Job; import com.evernote.android.job.util.support.PersistableBundleCompat; import com.google.gson.Gson; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.core.Clock; import com.nextcloud.client.preferences.AppPreferencesImpl; +import com.nextcloud.java.util.Optional; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -49,8 +49,8 @@ import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; -import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientManager; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.users.RemoteWipeSuccessRemoteOperation; @@ -67,7 +67,6 @@ import java.util.List; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import static android.content.Context.ACCOUNT_SERVICE; import static com.owncloud.android.ui.activity.ManageAccountsActivity.PENDING_FOR_REMOVAL; @@ -75,7 +74,7 @@ /** * Removes account and all local files */ -public class AccountRemovalJob extends Job implements AccountManagerCallback { +public class AccountRemovalJob extends Job { public static final String TAG = "AccountRemovalJob"; public static final String ACCOUNT = "account"; public static final String REMOTE_WIPE = "remote_wipe"; @@ -83,11 +82,16 @@ public class AccountRemovalJob extends Job implements AccountManagerCallback optionalUser = userAccountManager.getUser(accountName); + if (!optionalUser.isPresent()) { + // trying to delete non-existing user + return Result.FAILURE; + } AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE); + if (accountManager == null) { + return Result.FAILURE; + } boolean remoteWipe = bundle.getBoolean(REMOTE_WIPE, false); - if (account != null && accountManager != null) { - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); - // disable contact backup job - ContactsPreferenceActivity.cancelContactBackupJobForAccount(context, account); + User user = optionalUser.get(); + // disable contact backup job + ContactsPreferenceActivity.cancelContactBackupJobForAccount(context, user); - removeAccount(account, accountManager); + final boolean userRemoved = userAccountManager.removeUser(user); + if (userRemoved) { + eventBus.post(new AccountRemovedEvent()); + } - FileDataStorageManager storageManager = new FileDataStorageManager(account, context.getContentResolver()); + FileDataStorageManager storageManager = new FileDataStorageManager(user.toPlatformAccount(), context.getContentResolver()); - // remove all files - removeFiles(account, storageManager); + // remove all files + removeFiles(user, storageManager); - // delete all database entries - storageManager.deleteAllFiles(); + // delete all database entries + storageManager.deleteAllFiles(); - // remove contact backup job - ContactsPreferenceActivity.cancelContactBackupJobForAccount(context, account); + // remove contact backup job + ContactsPreferenceActivity.cancelContactBackupJobForAccount(context, user); - // disable daily backup - arbitraryDataProvider.storeOrUpdateKeyValue(account.name, - ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, - "false"); + // disable daily backup + arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), + ContactsPreferenceActivity.PREFERENCE_CONTACTS_AUTOMATIC_BACKUP, + "false"); - // unregister push notifications - unregisterPushNotifications(context, account, arbitraryDataProvider); + // unregister push notifications + unregisterPushNotifications(context, user, arbitraryDataProvider); - // remove pending account removal - arbitraryDataProvider.deleteKeyForAccount(account.name, PENDING_FOR_REMOVAL); + // remove pending account removal + arbitraryDataProvider.deleteKeyForAccount(user.getAccountName(), PENDING_FOR_REMOVAL); - // remove synced folders set for account - remoceSyncedFolders(context, account, clock); + // remove synced folders set for account + remoceSyncedFolders(context, user.toPlatformAccount(), clock); - // delete all uploads for account - uploadsStorageManager.removeAccountUploads(account); + // delete all uploads for account + uploadsStorageManager.removeAccountUploads(user.toPlatformAccount()); - // delete stored E2E keys - arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.PRIVATE_KEY); - arbitraryDataProvider.deleteKeyForAccount(account.name, EncryptionUtils.PUBLIC_KEY); + // delete stored E2E keys + arbitraryDataProvider.deleteKeyForAccount(user.getAccountName(), EncryptionUtils.PRIVATE_KEY); + arbitraryDataProvider.deleteKeyForAccount(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); - OwnCloudClient client = createClient(account); - if (remoteWipe && client != null) { + if (remoteWipe) { + Optional optionalClient = createClient(user); + if (optionalClient.isPresent()) { + OwnCloudClient client = optionalClient.get(); String authToken = client.getCredentials().getAuthToken(); new RemoteWipeSuccessRemoteOperation(authToken).execute(client); } + } - // notify Document Provider - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - String authority = context.getResources().getString(R.string.document_provider_authority); - Uri rootsUri = DocumentsContract.buildRootsUri(authority); - context.getContentResolver().notifyChange(rootsUri, null); - } - - return Result.SUCCESS; - } else { - return Result.FAILURE; + // notify Document Provider + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + String authority = context.getResources().getString(R.string.document_provider_authority); + Uri rootsUri = DocumentsContract.buildRootsUri(authority); + context.getContentResolver().notifyChange(rootsUri, null); } - } - private void unregisterPushNotifications(Context context, Account account, ArbitraryDataProvider arbitraryDataProvider) { - String arbitraryDataPushString; + return Result.SUCCESS; + } - if (!TextUtils.isEmpty(arbitraryDataPushString = arbitraryDataProvider.getValue( - account, PushUtils.KEY_PUSH)) && - !TextUtils.isEmpty(context.getResources().getString(R.string.push_server_url))) { + private void unregisterPushNotifications(Context context, + User user, + ArbitraryDataProvider arbitraryDataProvider) { + final String arbitraryDataPushString = arbitraryDataProvider.getValue(user.toPlatformAccount(), + PushUtils.KEY_PUSH); + final String pushServerUrl = context.getResources().getString(R.string.push_server_url); + if (!TextUtils.isEmpty(arbitraryDataPushString) && !TextUtils.isEmpty(pushServerUrl)) { Gson gson = new Gson(); PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryDataPushString, PushConfigurationState.class); pushArbitraryData.setShouldBeDeleted(true); - arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PushUtils.KEY_PUSH, + arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PushUtils.KEY_PUSH, gson.toJson(pushArbitraryData)); PushUtils.pushRegistrationToServer(userAccountManager, pushArbitraryData.getPushToken()); @@ -198,44 +218,23 @@ private void remoceSyncedFolders(Context context, Account account, Clock clock) } } - private void removeFiles(Account account, FileDataStorageManager storageManager) { - File tempDir = new File(FileStorageUtils.getTemporalPath(account.name)); - File saveDir = new File(FileStorageUtils.getSavePath(account.name)); + private void removeFiles(User user, FileDataStorageManager storageManager) { + File tempDir = new File(FileStorageUtils.getTemporalPath(user.getAccountName())); + File saveDir = new File(FileStorageUtils.getSavePath(user.getAccountName())); FileStorageUtils.deleteRecursively(tempDir, storageManager); FileStorageUtils.deleteRecursively(saveDir, storageManager); } - private void removeAccount(Account account, AccountManager accountManager) { - try { - AccountManagerFuture accountRemoval = accountManager.removeAccount(account, this, null); - boolean removal = accountRemoval.getResult(); - - if (!removal) { - Log_OC.e(this, "Account removal of " + account.name + " failed!"); - } - } catch (Exception e) { - Log_OC.e(this, "Account removal of " + account.name + " failed!", e); - } - } - - @Nullable - private OwnCloudClient createClient(Account account) { - OwnCloudClient client = null; + private Optional createClient(User user) { try { - OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext()); - client = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, - MainApp.getAppContext()); + Context context = MainApp.getAppContext(); + OwnCloudClientManager factory = OwnCloudClientManagerFactory.getDefaultSingleton(); + OwnCloudClient client = factory.getClientFor(user.toOwnCloudAccount(), context); + return Optional.of(client); } catch (Exception e) { Log_OC.e(this, "Could not create client", e); - } - return client; - } - - @Override - public void run(AccountManagerFuture future) { - if (future.isDone()) { - EventBus.getDefault().post(new AccountRemovedEvent()); + return Optional.empty(); } } } diff --git a/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java b/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java index 48ece7c43ea3..1036be620c90 100644 --- a/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java +++ b/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java @@ -21,7 +21,6 @@ package com.owncloud.android.jobs; -import android.accounts.Account; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; @@ -29,11 +28,14 @@ import android.net.Uri; import android.os.IBinder; import android.provider.ContactsContract; +import android.text.TextUtils; import android.text.format.DateFormat; import com.evernote.android.job.Job; import com.evernote.android.job.util.support.PersistableBundleCompat; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.java.util.Optional; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -80,14 +82,20 @@ public ContactsBackupJob(UserAccountManager accountManager) { protected Result onRunJob(@NonNull Params params) { PersistableBundleCompat bundle = params.getExtras(); - final Account account = accountManager.getAccountByName(bundle.getString(ACCOUNT, "")); - - if (account == null) { + final String accountName = bundle.getString(ACCOUNT, ""); + if (TextUtils.isEmpty(accountName)) { + // no account provided + return Result.FAILURE; + } + final Optional optionalUser = accountManager.getUser(accountName); + if (!optionalUser.isPresent()) { return Result.FAILURE; } + User user = optionalUser.get(); ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContext().getContentResolver()); - Long lastExecution = arbitraryDataProvider.getLongValue(account, PREFERENCE_CONTACTS_LAST_BACKUP); + Long lastExecution = arbitraryDataProvider.getLongValue(user.toPlatformAccount(), + PREFERENCE_CONTACTS_LAST_BACKUP); boolean force = bundle.getBoolean(FORCE, false); if (force || (lastExecution + 24 * 60 * 60 * 1000) < Calendar.getInstance().getTimeInMillis()) { @@ -97,16 +105,18 @@ protected Result onRunJob(@NonNull Params params) { OCFile.PATH_SEPARATOR; Integer daysToExpire = getContext().getResources().getInteger(R.integer.contacts_backup_expire); - backupContact(account, backupFolder); + backupContact(user, backupFolder); // bind to Operations Service - operationsServiceConnection = new OperationsServiceConnection(daysToExpire, backupFolder, account); + operationsServiceConnection = new OperationsServiceConnection(daysToExpire, + backupFolder, + user); getContext().bindService(new Intent(getContext(), OperationsService.class), operationsServiceConnection, OperationsService.BIND_AUTO_CREATE); // store execution date - arbitraryDataProvider.storeOrUpdateKeyValue(account.name, + arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), PREFERENCE_CONTACTS_LAST_BACKUP, Calendar.getInstance().getTimeInMillis()); } else { @@ -116,7 +126,7 @@ protected Result onRunJob(@NonNull Params params) { return Result.SUCCESS; } - private void backupContact(Account account, String backupFolder) { + private void backupContact(User user, String backupFolder) { ArrayList vCard = new ArrayList<>(); Cursor cursor = getContext().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, @@ -162,7 +172,7 @@ private void backupContact(Account account, String backupFolder) { FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); requester.uploadNewFile( getContext(), - account, + user.toPlatformAccount(), file.getAbsolutePath(), backupFolder + filename, FileUploader.LOCAL_BEHAVIOUR_MOVE, @@ -174,10 +184,11 @@ private void backupContact(Account account, String backupFolder) { ); } - private void expireFiles(Integer daysToExpire, String backupFolderString, Account account) { + private void expireFiles(Integer daysToExpire, String backupFolderString, User account) { // -1 disables expiration if (daysToExpire > -1) { - FileDataStorageManager storageManager = new FileDataStorageManager(account, getContext().getContentResolver()); + FileDataStorageManager storageManager = new FileDataStorageManager(account.toPlatformAccount(), + getContext().getContentResolver()); OCFile backupFolder = storageManager.getFileByPath(backupFolderString); Calendar cal = Calendar.getInstance(); cal.add(Calendar.DAY_OF_YEAR, -daysToExpire); @@ -261,12 +272,12 @@ private String getContactFromCursor(Cursor cursor) { private class OperationsServiceConnection implements ServiceConnection { private Integer daysToExpire; private String backupFolder; - private Account account; + private User user; - OperationsServiceConnection(Integer daysToExpire, String backupFolder, Account account) { + OperationsServiceConnection(Integer daysToExpire, String backupFolder, User user) { this.daysToExpire = daysToExpire; this.backupFolder = backupFolder; - this.account = account; + this.user = user; } @Override @@ -276,7 +287,7 @@ public void onServiceConnected(ComponentName component, IBinder service) { if (component.equals(new ComponentName(getContext(), OperationsService.class))) { operationsServiceBinder = (OperationsService.OperationsServiceBinder) service; - expireFiles(daysToExpire, backupFolder, account); + expireFiles(daysToExpire, backupFolder, user); } } diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index 32b3002d7f9c..0268bec03813 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -23,7 +23,6 @@ package com.owncloud.android.jobs; -import android.accounts.Account; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; @@ -33,11 +32,13 @@ import com.evernote.android.job.Job; import com.evernote.android.job.util.support.PersistableBundleCompat; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; +import com.nextcloud.java.util.Optional; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -167,7 +168,12 @@ private void syncFolder(Context context, Resources resources, boolean lightVersi boolean needsWifi; File file; ArbitraryDataProvider arbitraryDataProvider; - Account account = userAccountManager.getAccountByName(syncedFolder.getAccount()); + String accountName = syncedFolder.getAccount(); + Optional optionalUser = userAccountManager.getUser(accountName); + if (!optionalUser.isPresent()) { + return; + } + User user = optionalUser.get(); if (lightVersion) { arbitraryDataProvider = new ArbitraryDataProvider(context.getContentResolver()); @@ -183,13 +189,11 @@ private void syncFolder(Context context, Resources resources, boolean lightVersi if (lightVersion) { needsCharging = resources.getBoolean(R.bool.syncedFolder_light_on_charging); - needsWifi = account == null || arbitraryDataProvider.getBooleanValue(account.name, - SettingsActivity.SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI); + needsWifi = arbitraryDataProvider.getBooleanValue(accountName, + SettingsActivity.SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI); String uploadActionString = resources.getString(R.string.syncedFolder_light_upload_behaviour); uploadAction = getUploadAction(uploadActionString); - subfolderByDate = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders); - remotePath = resources.getString(R.string.syncedFolder_remote_folder); } else { needsCharging = syncedFolder.isChargingOnly(); @@ -201,7 +205,7 @@ private void syncFolder(Context context, Resources resources, boolean lightVersi requester.uploadFileWithOverwrite( context, - account, + user.toPlatformAccount(), file.getAbsolutePath(), FileStorageUtils.getInstantUploadFilePath( file, diff --git a/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java b/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java index e421b9365a7d..b2ea7d619b08 100644 --- a/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java +++ b/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java @@ -23,8 +23,6 @@ */ package com.owncloud.android.jobs; - -import android.accounts.Account; import android.app.Activity; import android.app.NotificationManager; import android.app.PendingIntent; @@ -38,6 +36,7 @@ import com.evernote.android.job.Job; import com.google.gson.Gson; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.core.Clock; import com.nextcloud.client.preferences.AppPreferences; @@ -47,6 +46,7 @@ import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.MediaFoldersModel; import com.owncloud.android.datamodel.MediaProvider; +import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.ManageAccountsActivity; @@ -138,32 +138,41 @@ protected Result onRunJob(@NonNull Params params) { videoMediaFolderPaths.removeAll(mediaFoldersModel.getVideoMediaFolders()); if (!imageMediaFolderPaths.isEmpty() || !videoMediaFolderPaths.isEmpty()) { - Account[] accounts = userAccountManager.getAccounts(); - List accountList = new ArrayList<>(); - for (Account account : accounts) { - if (!arbitraryDataProvider.getBooleanValue(account, ManageAccountsActivity.PENDING_FOR_REMOVAL)) { - accountList.add(account); + List allUsers = userAccountManager.getAllUsers(); + List activeUsers = new ArrayList<>(); + for (User account : allUsers) { + if (!arbitraryDataProvider.getBooleanValue(account.toPlatformAccount(), + ManageAccountsActivity.PENDING_FOR_REMOVAL)) { + activeUsers.add(account); } } - for (Account account : accountList) { + for (User user : activeUsers) { for (String imageMediaFolder : imageMediaFolderPaths) { - if (syncedFolderProvider.findByLocalPathAndAccount(imageMediaFolder, account) == null) { - sendNotification(String.format(context.getString(R.string.new_media_folder_detected), - context.getString(R.string.new_media_folder_photos)), - imageMediaFolder.substring(imageMediaFolder.lastIndexOf('/') + 1 - ), - account, imageMediaFolder, 1); + final SyncedFolder folder = syncedFolderProvider.findByLocalPathAndAccount(imageMediaFolder, + user.toPlatformAccount()); + if (folder == null) { + String contentTitle = String.format(context.getString(R.string.new_media_folder_detected), + context.getString(R.string.new_media_folder_photos)); + sendNotification(contentTitle, + imageMediaFolder.substring(imageMediaFolder.lastIndexOf('/') + 1), + user, + imageMediaFolder, + 1); } } for (String videoMediaFolder : videoMediaFolderPaths) { - if (syncedFolderProvider.findByLocalPathAndAccount(videoMediaFolder, account) == null) { - sendNotification(String.format(context.getString(R.string.new_media_folder_detected), - context.getString(R.string.new_media_folder_videos)), - videoMediaFolder.substring(videoMediaFolder.lastIndexOf('/') + 1 - ), - account, videoMediaFolder, 2); + final SyncedFolder folder = syncedFolderProvider.findByLocalPathAndAccount(videoMediaFolder, + user.toPlatformAccount()); + if (folder == null) { + String contentTitle = String.format(context.getString(R.string.new_media_folder_detected), + context.getString(R.string.new_media_folder_videos)); + sendNotification(contentTitle, + videoMediaFolder.substring(videoMediaFolder.lastIndexOf('/') + 1), + user, + videoMediaFolder, + 2); } } } @@ -179,14 +188,14 @@ protected Result onRunJob(@NonNull Params params) { return Result.SUCCESS; } - private void sendNotification(String contentTitle, String subtitle, Account account, String path, int type) { + private void sendNotification(String contentTitle, String subtitle, User user, String path, int type) { int notificationId = randomId.nextInt(); Context context = getContext(); Intent intent = new Intent(getContext(), SyncedFoldersActivity.class); intent.putExtra(NOTIFICATION_ID, notificationId); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(NotificationJob.KEY_NOTIFICATION_ACCOUNT, account.name); + intent.putExtra(NotificationJob.KEY_NOTIFICATION_ACCOUNT, user.getAccountName()); intent.putExtra(KEY_MEDIA_FOLDER_PATH, path); intent.putExtra(KEY_MEDIA_FOLDER_TYPE, type); intent.putExtra(SyncedFoldersActivity.EXTRA_SHOW_SIDEBAR, true); @@ -197,7 +206,7 @@ private void sendNotification(String contentTitle, String subtitle, Account acco .setSmallIcon(R.drawable.notification_icon) .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.notification_icon)) .setColor(ThemeUtils.primaryColor(getContext())) - .setSubText(account.name) + .setSubText(user.getAccountName()) .setContentTitle(contentTitle) .setContentText(subtitle) .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) diff --git a/src/main/java/com/owncloud/android/jobs/NCJobCreator.java b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java index 010ae266897a..901e192b7e2d 100644 --- a/src/main/java/com/owncloud/android/jobs/NCJobCreator.java +++ b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java @@ -35,6 +35,8 @@ import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.datamodel.UploadsStorageManager; +import org.greenrobot.eventbus.EventBus; + import androidx.annotation.NonNull; /** @@ -50,6 +52,7 @@ public class NCJobCreator implements JobCreator { private final ConnectivityService connectivityService; private final PowerManagementService powerManagementService; private final Clock clock; + private final EventBus eventBus; public NCJobCreator( Context context, @@ -58,7 +61,8 @@ public NCJobCreator( UploadsStorageManager uploadsStorageManager, ConnectivityService connectivityServices, PowerManagementService powerManagementService, - Clock clock + Clock clock, + EventBus eventBus ) { this.context = context; this.accountManager = accountManager; @@ -67,6 +71,7 @@ public NCJobCreator( this.connectivityService = connectivityServices; this.powerManagementService = powerManagementService; this.clock = clock; + this.eventBus = eventBus; } @Override @@ -77,7 +82,7 @@ public Job create(@NonNull String tag) { case ContactsImportJob.TAG: return new ContactsImportJob(); case AccountRemovalJob.TAG: - return new AccountRemovalJob(uploadsStorageManager, accountManager, clock); + return new AccountRemovalJob(uploadsStorageManager, accountManager, clock, eventBus); case FilesSyncJob.TAG: return new FilesSyncJob(accountManager, preferences, diff --git a/src/main/java/com/owncloud/android/jobs/NotificationJob.java b/src/main/java/com/owncloud/android/jobs/NotificationJob.java index 353e08148696..332e9477f314 100644 --- a/src/main/java/com/owncloud/android/jobs/NotificationJob.java +++ b/src/main/java/com/owncloud/android/jobs/NotificationJob.java @@ -22,7 +22,6 @@ package com.owncloud.android.jobs; -import android.accounts.Account; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.app.Activity; @@ -42,11 +41,12 @@ import com.evernote.android.job.Job; import com.evernote.android.job.util.support.PersistableBundleCompat; import com.google.gson.Gson; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.java.util.Optional; import com.owncloud.android.R; import com.owncloud.android.datamodel.DecryptedPushMessage; import com.owncloud.android.datamodel.SignatureVerification; -import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.operations.RemoteOperation; @@ -140,7 +140,9 @@ protected Result onRunJob(@NonNull Params params) { } else if (decryptedPushMessage.deleteAll) { notificationManager.cancelAll(); } else { - fetchCompleteNotification(signatureVerification.getAccount(), decryptedPushMessage); + final User user = accountManager.getUser(signatureVerification.getAccount().name) + .orElseThrow(RuntimeException::new); + fetchCompleteNotification(user, decryptedPushMessage); } } } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e1) { @@ -154,7 +156,7 @@ protected Result onRunJob(@NonNull Params params) { return Result.SUCCESS; } - private void sendNotification(Notification notification, Account account) { + private void sendNotification(Notification notification, User user) { SecureRandom randomId = new SecureRandom(); RichObject file = notification.subjectRichParameters.get("file"); @@ -166,7 +168,7 @@ private void sendNotification(Notification notification, Account account) { intent.setAction(Intent.ACTION_VIEW); intent.putExtra(FileDisplayActivity.KEY_FILE_ID, file.id); } - intent.putExtra(KEY_NOTIFICATION_ACCOUNT, account.name); + intent.putExtra(KEY_NOTIFICATION_ACCOUNT, user.getAccountName()); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT); @@ -176,9 +178,9 @@ private void sendNotification(Notification notification, Account account) { new NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_PUSH) .setSmallIcon(R.drawable.notification_icon) .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.notification_icon)) - .setColor(ThemeUtils.primaryColor(account, false, context)) + .setColor(ThemeUtils.primaryColor(user.toPlatformAccount(), false, context)) .setShowWhen(true) - .setSubText(account.name) + .setSubText(user.getAccountName()) .setContentTitle(notification.getSubject()) .setContentText(notification.getMessage()) .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) @@ -191,7 +193,7 @@ private void sendNotification(Notification notification, Account account) { Intent disableDetection = new Intent(context, NotificationJob.NotificationReceiver.class); disableDetection.putExtra(NUMERIC_NOTIFICATION_ID, notification.getNotificationId()); disableDetection.putExtra(PUSH_NOTIFICATION_ID, pushNotificationId); - disableDetection.putExtra(KEY_NOTIFICATION_ACCOUNT, account.name); + disableDetection.putExtra(KEY_NOTIFICATION_ACCOUNT, user.getAccountName()); PendingIntent disableIntent = PendingIntent.getBroadcast(context, pushNotificationId, disableDetection, PendingIntent.FLAG_CANCEL_CURRENT); @@ -204,7 +206,7 @@ private void sendNotification(Notification notification, Account account) { Intent actionIntent = new Intent(context, NotificationJob.NotificationReceiver.class); actionIntent.putExtra(NUMERIC_NOTIFICATION_ID, notification.getNotificationId()); actionIntent.putExtra(PUSH_NOTIFICATION_ID, pushNotificationId); - actionIntent.putExtra(KEY_NOTIFICATION_ACCOUNT, account.name); + actionIntent.putExtra(KEY_NOTIFICATION_ACCOUNT, user.getAccountName()); actionIntent.putExtra(KEY_NOTIFICATION_ACTION_LINK, action.link); actionIntent.putExtra(KEY_NOTIFICATION_ACTION_TYPE, action.type); @@ -228,9 +230,9 @@ private void sendNotification(Notification notification, Account account) { new NotificationCompat.Builder(context, NotificationUtils.NOTIFICATION_CHANNEL_PUSH) .setSmallIcon(R.drawable.notification_icon) .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.notification_icon)) - .setColor(ThemeUtils.primaryColor(account, false, context)) + .setColor(ThemeUtils.primaryColor(user.toPlatformAccount(), false, context)) .setShowWhen(true) - .setSubText(account.name) + .setSubText(user.getAccountName()) .setContentTitle(context.getString(R.string.new_notification)) .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) .setAutoCancel(true) @@ -241,18 +243,18 @@ private void sendNotification(Notification notification, Account account) { notificationManager.notify(notification.getNotificationId(), notificationBuilder.build()); } - private void fetchCompleteNotification(Account account, DecryptedPushMessage decryptedPushMessage) { - Account currentAccount = accountManager.getAccountByName(account.name); + private void fetchCompleteNotification(User account, DecryptedPushMessage decryptedPushMessage) { + Optional optionalUser = accountManager.getUser(account.getAccountName()); - if (currentAccount == null) { + if (!optionalUser.isPresent()) { Log_OC.e(this, "Account may not be null"); return; } + User user = optionalUser.get(); try { - OwnCloudAccount ocAccount = new OwnCloudAccount(currentAccount, context); OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton() - .getClientFor(ocAccount, context); + .getClientFor(user.toOwnCloudAccount(), context); RemoteOperationResult result = new GetNotificationRemoteOperation(decryptedPushMessage.nid) .execute(client); @@ -295,16 +297,14 @@ public void onReceive(Context context, Intent intent) { } try { - Account currentAccount = accountManager.getAccountByName(accountName); - - if (currentAccount == null) { + Optional optionalUser = accountManager.getUser(accountName); + if (!optionalUser.isPresent()) { Log_OC.e(this, "Account may not be null"); return; } - - OwnCloudAccount ocAccount = new OwnCloudAccount(currentAccount, context); + User user = optionalUser.get(); OwnCloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton() - .getClientFor(ocAccount, context); + .getClientFor(user.toOwnCloudAccount(), context); String actionType = intent.getStringExtra(KEY_NOTIFICATION_ACTION_TYPE); String actionLink = intent.getStringExtra(KEY_NOTIFICATION_ACTION_LINK); @@ -325,8 +325,7 @@ public void onReceive(Context context, Intent intent) { } else if (notificationManager != null) { notificationManager.notify(numericNotificationId, oldNotification); } - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException | - IOException | OperationCanceledException | AuthenticatorException e) { + } catch (IOException | OperationCanceledException | AuthenticatorException e) { Log_OC.e(TAG, "Error initializing client", e); } }).start(); diff --git a/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java b/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java index 6030fad70d3d..9499e9ddf0e7 100644 --- a/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/OfflineSyncJob.java @@ -21,7 +21,6 @@ */ package com.owncloud.android.jobs; -import android.accounts.Account; import android.content.Context; import android.os.Build; import android.os.PowerManager; @@ -29,6 +28,7 @@ import com.evernote.android.job.Job; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; @@ -42,6 +42,7 @@ import com.owncloud.android.utils.FileStorageUtils; import java.io.File; +import java.util.List; import java.util.Set; import androidx.annotation.NonNull; @@ -89,10 +90,10 @@ protected Result onRunJob(@NonNull Params params) { } } - Account[] accounts = userAccountManager.getAccounts(); + List users = userAccountManager.getAllUsers(); - for (Account account : accounts) { - FileDataStorageManager storageManager = new FileDataStorageManager(account, + for (User user : users) { + FileDataStorageManager storageManager = new FileDataStorageManager(user.toPlatformAccount(), getContext().getContentResolver()); OCFile ocRoot = storageManager.getFileByPath(ROOT_PATH); @@ -101,7 +102,7 @@ protected Result onRunJob(@NonNull Params params) { break; } - recursive(new File(ocRoot.getStoragePath()), storageManager, account); + recursive(new File(ocRoot.getStoragePath()), storageManager, user); } if (wakeLock != null) { @@ -112,8 +113,8 @@ protected Result onRunJob(@NonNull Params params) { return Result.SUCCESS; } - private void recursive(File folder, FileDataStorageManager storageManager, Account account) { - String downloadFolder = FileStorageUtils.getSavePath(account.name); + private void recursive(File folder, FileDataStorageManager storageManager, User user) { + String downloadFolder = FileStorageUtils.getSavePath(user.getAccountName()); String folderName = folder.getAbsolutePath().replaceFirst(downloadFolder, "") + PATH_SEPARATOR; Log_OC.d(TAG, folderName + ": enter"); @@ -128,7 +129,7 @@ private void recursive(File folder, FileDataStorageManager storageManager, Accou // check for etag change, if false, skip CheckEtagRemoteOperation checkEtagOperation = new CheckEtagRemoteOperation(ocFolder.getRemotePath(), ocFolder.getEtagOnServer()); - RemoteOperationResult result = checkEtagOperation.execute(account, getContext()); + RemoteOperationResult result = checkEtagOperation.execute(user.toPlatformAccount(), getContext()); // eTag changed, sync file switch (result.getCode()) { @@ -156,7 +157,7 @@ private void recursive(File folder, FileDataStorageManager storageManager, Accou for (File file : files) { OCFile ocFile = storageManager.getFileByLocalPath(file.getPath()); SynchronizeFileOperation synchronizeFileOperation = new SynchronizeFileOperation(ocFile.getRemotePath(), - account, true, getContext()); + user.toPlatformAccount(), true, getContext()); synchronizeFileOperation.execute(storageManager, getContext()); } @@ -167,7 +168,7 @@ private void recursive(File folder, FileDataStorageManager storageManager, Accou if (subfolders != null) { for (File subfolder : subfolders) { - recursive(subfolder, storageManager, account); + recursive(subfolder, storageManager, user); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java b/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java index f33eb1ac8be1..277fdca593e3 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ContactsPreferenceActivity.java @@ -29,6 +29,7 @@ import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.support.PersistableBundleCompat; +import com.nextcloud.client.account.User; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; @@ -133,15 +134,15 @@ public static void cancelPreviousContactBackupJobForAccount(Context context, Acc } } - public static void cancelContactBackupJobForAccount(Context context, Account account) { - Log_OC.d(TAG, "disabling contacts backup job for account: " + account.name); + public static void cancelContactBackupJobForAccount(Context context, User user) { + Log_OC.d(TAG, "disabling contacts backup job for account: " + user.getAccountName()); JobManager jobManager = JobManager.create(context); Set jobs = jobManager.getAllJobRequestsForTag(ContactsBackupJob.TAG); for (JobRequest jobRequest : jobs) { PersistableBundleCompat extras = jobRequest.getExtras(); - if (extras.getString(ContactsBackupJob.ACCOUNT, "").equalsIgnoreCase(account.name)) { + if (extras.getString(ContactsBackupJob.ACCOUNT, "").equalsIgnoreCase(user.getAccountName())) { jobManager.cancel(jobRequest.getJobId()); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java index 9e6c7077dc6b..6d2a60ed3e34 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactsBackupFragment.java @@ -40,6 +40,8 @@ import com.evernote.android.job.util.support.PersistableBundleCompat; import com.google.android.material.button.MaterialButton; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.client.account.User; +import com.nextcloud.java.util.Optional; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -344,8 +346,12 @@ private void setAutomaticBackup(final boolean bool) { if (bool) { ContactsPreferenceActivity.startContactBackupJob(contactsPreferenceActivity.getAccount()); } else { - ContactsPreferenceActivity.cancelContactBackupJobForAccount(contactsPreferenceActivity, - contactsPreferenceActivity.getAccount()); + Optional user = contactsPreferenceActivity.getUser(); + + if (user.isPresent()) { + ContactsPreferenceActivity.cancelContactBackupJobForAccount(contactsPreferenceActivity, + user.get()); + } } arbitraryDataProvider.storeOrUpdateKeyValue(account.name, PREFERENCE_CONTACTS_AUTOMATIC_BACKUP,