From 26f2d52a5ad6eb50d303c7d0a681840f5394f961 Mon Sep 17 00:00:00 2001 From: Chris Narkiewicz Date: Wed, 25 Dec 2019 14:05:49 +0000 Subject: [PATCH] Extract account logic from BaseActivity into a mixin Signed-off-by: Chris Narkiewicz --- .../client/account/UserAccountManager.java | 10 + .../account/UserAccountManagerImpl.java | 12 ++ .../com/nextcloud/client/core/AsyncRunner.kt | 9 + .../com/nextcloud/client/core/Cancellable.kt | 14 ++ .../nextcloud/client/di/ActivityInjector.java | 2 - .../nextcloud/client/mixins/ActivityMixin.kt | 40 ++++ .../nextcloud/client/mixins/MixinRegistry.kt | 88 +++++++++ .../com/nextcloud/client/mixins/Package.md | 10 + .../nextcloud/client/mixins/SessionMixin.kt | 133 +++++++++++++ .../client/onboarding/FirstRunActivity.java | 2 - .../android/ui/activity/BaseActivity.java | 174 +++--------------- .../android/ui/activity/DrawerActivity.java | 10 +- .../ui/activity/FileDisplayActivity.java | 2 - .../ui/activity/ManageAccountsActivity.java | 2 +- .../ui/adapter/AccountListAdapter.java | 4 +- .../client/mixins/MixinRegistryTest.kt | 68 +++++++ .../client/mixins/SessionMixinTest.kt | 67 +++++++ 17 files changed, 478 insertions(+), 169 deletions(-) create mode 100644 src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt create mode 100644 src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt create mode 100644 src/main/java/com/nextcloud/client/mixins/Package.md create mode 100644 src/main/java/com/nextcloud/client/mixins/SessionMixin.kt create mode 100644 src/test/java/com/nextcloud/client/mixins/MixinRegistryTest.kt create mode 100644 src/test/java/com/nextcloud/client/mixins/SessionMixinTest.kt diff --git a/src/main/java/com/nextcloud/client/account/UserAccountManager.java b/src/main/java/com/nextcloud/client/account/UserAccountManager.java index cf4acc4d8578..0f695f2f75ba 100644 --- a/src/main/java/com/nextcloud/client/account/UserAccountManager.java +++ b/src/main/java/com/nextcloud/client/account/UserAccountManager.java @@ -20,6 +20,8 @@ package com.nextcloud.client.account; import android.accounts.Account; +import android.app.Activity; +import android.content.Intent; import com.nextcloud.java.util.Optional; import com.owncloud.android.datamodel.OCFile; @@ -141,4 +143,12 @@ static String getUsername(Account account) { } } + /** + * Launch account registration activity. + * + * This method returns immediately. Authenticator activity will be launched asynchronously. + * + * @param activity Activity used to launch authenticator flow via {@link Activity#startActivity(Intent)} + */ + void startAccountCreation(Activity activity); } diff --git a/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java b/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java index a94eba334271..0d26b02a6930 100644 --- a/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java +++ b/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java @@ -22,6 +22,7 @@ import android.accounts.Account; import android.accounts.AccountManager; +import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; @@ -376,4 +377,15 @@ public boolean migrateUserId() { private String getAccountType() { return context.getString(R.string.account_type); } + + @Override + public void startAccountCreation(final Activity activity) { + accountManager.addAccount(getAccountType(), + null, + null, + null, + activity, + null, + null); + } } diff --git a/src/main/java/com/nextcloud/client/core/AsyncRunner.kt b/src/main/java/com/nextcloud/client/core/AsyncRunner.kt index 143b782ca3fd..31e2cefe3314 100644 --- a/src/main/java/com/nextcloud/client/core/AsyncRunner.kt +++ b/src/main/java/com/nextcloud/client/core/AsyncRunner.kt @@ -28,5 +28,14 @@ typealias OnErrorCallback = (Throwable) -> Unit * It is provided as an alternative for heavy, platform specific and virtually untestable [android.os.AsyncTask] */ interface AsyncRunner { + + /** + * Post a background task and return immediately returning task cancellation interface. + * + * @param task Task function returning result T; error shall be signalled by throwing an exception. + * @param onResult Callback called when task function returns a result + * @param onError Callback called when task function throws an exception + * @return Cancellable interface, allowing to cancel running task. + */ fun post(task: () -> T, onResult: OnResultCallback? = null, onError: OnErrorCallback? = null): Cancellable } diff --git a/src/main/java/com/nextcloud/client/core/Cancellable.kt b/src/main/java/com/nextcloud/client/core/Cancellable.kt index 2e3a5ad2959a..49c9598f3a53 100644 --- a/src/main/java/com/nextcloud/client/core/Cancellable.kt +++ b/src/main/java/com/nextcloud/client/core/Cancellable.kt @@ -19,6 +19,20 @@ */ package com.nextcloud.client.core +/** + * Interface allowing cancellation of a running task. + * Once must be careful when cancelling a non-idempotent task, + * as cancellation does not guarantee a task termination. + * One trivial case would be a task finished and cancelled + * before result delivery. + * + * @see [com.nextcloud.client.core.AsyncRunner] + */ interface Cancellable { + + /** + * Cancel running task. Task termination is not guaranteed, but the result + * shall not be delivered. + */ fun cancel() } diff --git a/src/main/java/com/nextcloud/client/di/ActivityInjector.java b/src/main/java/com/nextcloud/client/di/ActivityInjector.java index e146af52ded2..b3f1bed0565e 100644 --- a/src/main/java/com/nextcloud/client/di/ActivityInjector.java +++ b/src/main/java/com/nextcloud/client/di/ActivityInjector.java @@ -23,7 +23,6 @@ import android.app.Activity; import android.app.Application; import android.os.Bundle; - import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import dagger.android.AndroidInjection; @@ -72,4 +71,3 @@ public final void onActivityDestroyed(Activity activity) { // not needed } } - diff --git a/src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt b/src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt new file mode 100644 index 000000000000..68f3f5f9ddfc --- /dev/null +++ b/src/main/java/com/nextcloud/client/mixins/ActivityMixin.kt @@ -0,0 +1,40 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2020 Nextcloud GmbH + * + * 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.nextcloud.client.mixins + +import android.content.Intent +import android.os.Bundle + +/** + * Interface allowing to implement part of [android.app.Activity] logic as + * a mix-in. + */ +interface ActivityMixin { + fun onNewIntent(intent: Intent) { /* no-op */ } + fun onSaveInstanceState(outState: Bundle) { /* no-op */ } + fun onCreate(savedInstanceState: Bundle?) { /* no-op */ } + fun onRestart() { /* no-op */ } + fun onStart() { /* no-op */ } + fun onResume() { /* no-op */ } + fun onPause() { /* no-op */ } + fun onStop() { /* no-op */ } + fun onDestroy() { /* no-op */ } +} diff --git a/src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt b/src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt new file mode 100644 index 000000000000..2fc01bf9e35a --- /dev/null +++ b/src/main/java/com/nextcloud/client/mixins/MixinRegistry.kt @@ -0,0 +1,88 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2020 Nextcloud GmbH + * + * 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.nextcloud.client.mixins + +import android.content.Intent +import android.os.Bundle + +/** + * Mix-in registry allows forwards lifecycle calls to all + * registered mix-ins. + * + * Once instantiated, all [android.app.Activity] lifecycle methods + * must call relevant registry companion methods. + * + * Calling the registry from [android.app.Application.ActivityLifecycleCallbacks] is + * not possible as not all callbacks are supported by this interface. + */ +class MixinRegistry : ActivityMixin { + + private val mixins = mutableListOf() + + fun add(vararg mixins: ActivityMixin) { + mixins.forEach { this.mixins.add(it) } + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + mixins.forEach { it.onNewIntent(intent) } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + mixins.forEach { it.onSaveInstanceState(outState) } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mixins.forEach { it.onCreate(savedInstanceState) } + } + + override fun onRestart() { + super.onRestart() + mixins.forEach { it.onRestart() } + } + + override fun onStart() { + super.onStart() + mixins.forEach { it.onStart() } + } + + override fun onResume() { + super.onResume() + mixins.forEach { it.onResume() } + } + + override fun onPause() { + super.onPause() + mixins.forEach { it.onPause() } + } + + override fun onStop() { + super.onStop() + mixins.forEach { it.onStop() } + } + + override fun onDestroy() { + super.onDestroy() + mixins.forEach { it.onDestroy() } + } +} diff --git a/src/main/java/com/nextcloud/client/mixins/Package.md b/src/main/java/com/nextcloud/client/mixins/Package.md new file mode 100644 index 000000000000..d88d0704bf58 --- /dev/null +++ b/src/main/java/com/nextcloud/client/mixins/Package.md @@ -0,0 +1,10 @@ +# Package com.nextcloud.client.mixins + +This package provides utilities and interfaces +allowing implementation of UI logic as mix-ins. + +Mix-ins allow encapsulation of non-visual logic +as classes facilitating composition over inheritance. + +For more information about mix-in concept, please +refer to [article on Wikipedia](https://en.wikipedia.org/wiki/Mixin). diff --git a/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt b/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt new file mode 100644 index 000000000000..b69688d79596 --- /dev/null +++ b/src/main/java/com/nextcloud/client/mixins/SessionMixin.kt @@ -0,0 +1,133 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2020 Nextcloud GmbH + * + * 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.nextcloud.client.mixins + +import android.accounts.Account +import android.app.Activity +import android.content.ContentResolver +import android.content.Intent +import android.os.Bundle +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.java.util.Optional +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.resources.status.OCCapability +import com.owncloud.android.ui.activity.BaseActivity + +/** + * Session mixin collects all account / user handling logic currently + * spread over various activities. + * + * It is an intermediary step facilitating comprehensive rework of + * account handling logic. + */ +class SessionMixin constructor( + private val activity: Activity, + private val contentResolver: ContentResolver, + private val accountManager: UserAccountManager +) : ActivityMixin { + + private companion object { + private val TAG = BaseActivity::class.java.simpleName + } + + var currentAccount: Account? = null + private set + var storageManager: FileDataStorageManager? = null + private set + var capabilities: OCCapability? = null + private set + + fun setAccount(account: Account?) { + val validAccount = account != null && accountManager.setCurrentOwnCloudAccount(account.name) + if (validAccount) { + currentAccount = account + } else { + swapToDefaultAccount() + } + + currentAccount?.let { + val storageManager = FileDataStorageManager(currentAccount, contentResolver) + this.storageManager = storageManager + this.capabilities = storageManager.getCapability(it.name) + } + } + + fun setUser(user: User) { + setAccount(user.toPlatformAccount()) + } + + fun getUser(): Optional = when (val it = this.currentAccount) { + null -> Optional.empty() + else -> accountManager.getUser(it.name) + } + + /** + * Tries to swap the current ownCloud [Account] for other valid and existing. + * + * If no valid ownCloud [Account] exists, then the user is requested + * to create a new ownCloud [Account]. + */ + private fun swapToDefaultAccount() { + // default to the most recently used account + val newAccount = accountManager.currentAccount + if (newAccount == null) { + // no account available: force account creation + startAccountCreation() + } else { + currentAccount = newAccount + } + } + + /** + * Launches the account creation activity. + */ + fun startAccountCreation() { + accountManager.startAccountCreation(activity) + } + + override fun onNewIntent(intent: Intent) { + super.onNewIntent(intent) + val current = accountManager.currentAccount + val currentAccount = this.currentAccount + if (current != null && currentAccount != null && !currentAccount.name.equals(current.name)) { + this.currentAccount = current + } + } + + /** + * Since ownCloud {@link Account} can be managed from the system setting menu, the existence of the {@link + * Account} associated to the instance must be checked every time it is restarted. + */ + override fun onRestart() { + super.onRestart() + val validAccount = currentAccount != null && accountManager.exists(currentAccount) + if (!validAccount) { + swapToDefaultAccount() + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val account = accountManager.currentAccount + setAccount(account) + } +} diff --git a/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java b/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java index 8eb68e5aea22..8790115ebe73 100644 --- a/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java +++ b/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java @@ -72,8 +72,6 @@ public class FirstRunActivity extends BaseActivity implements ViewPager.OnPageCh @Override protected void onCreate(Bundle savedInstanceState) { - enableAccountHandling = false; - super.onCreate(savedInstanceState); setContentView(R.layout.first_run_activity); diff --git a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java index a3c6b2145afc..df314c685951 100644 --- a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java @@ -1,21 +1,17 @@ package com.owncloud.android.ui.activity; import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AccountManagerCallback; -import android.accounts.AccountManagerFuture; -import android.accounts.OperationCanceledException; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.mixins.MixinRegistry; +import com.nextcloud.client.mixins.SessionMixin; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.DarkMode; import com.nextcloud.java.util.Optional; -import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.utils.Log_OC; @@ -33,27 +29,14 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab private static final String TAG = BaseActivity.class.getSimpleName(); - /** - * ownCloud {@link Account} where the main {@link OCFile} handled by the activity is located. - */ - private Account currentAccount; - - /** - * Capabilities of the server where {@link #currentAccount} lives. - */ - private OCCapability capabilities; - - /** - * Access point to the cached database for the current ownCloud {@link Account}. - */ - private FileDataStorageManager storageManager; - /** * Tracks whether the activity should be recreate()'d after a theme change */ private boolean themeChangePending; private boolean paused; - protected boolean enableAccountHandling = true; + + private MixinRegistry mixinRegistry = new MixinRegistry(); + private SessionMixin sessionMixin; @Inject UserAccountManager accountManager; @Inject AppPreferences preferences; @@ -72,11 +55,11 @@ public UserAccountManager getUserAccountManager() { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - if (enableAccountHandling) { - Account account = accountManager.getCurrentAccount(); - setAccount(account, false); - } + sessionMixin = new SessionMixin(this, + getContentResolver(), + accountManager); + mixinRegistry.add(sessionMixin); + mixinRegistry.onCreate(savedInstanceState); } @Override @@ -88,18 +71,21 @@ protected void onPostCreate(@Nullable Bundle savedInstanceState) { @Override protected void onDestroy() { super.onDestroy(); + mixinRegistry.onDestroy(); preferences.removeListener(onPreferencesChanged); } @Override protected void onPause() { super.onPause(); + mixinRegistry.onPause(); paused = true; } @Override protected void onResume() { super.onResume(); + mixinRegistry.onResume(); paused = false; if (themeChangePending) { @@ -110,28 +96,14 @@ protected void onResume() { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - - Log_OC.v(TAG, "onNewIntent() start"); - Account current = accountManager.getCurrentAccount(); - if (current != null && currentAccount != null && !currentAccount.name.equals(current.name)) { - currentAccount = current; - } - Log_OC.v(TAG, "onNewIntent() stop"); + mixinRegistry.onNewIntent(intent); } - /** - * Since ownCloud {@link Account}s can be managed from the system setting menu, the existence of the {@link - * Account} associated to the instance must be checked every time it is restarted. - */ @Override protected void onRestart() { Log_OC.v(TAG, "onRestart() start"); super.onRestart(); - boolean validAccount = currentAccount != null && accountManager.exists(currentAccount); - if (!validAccount) { - swapToDefaultAccount(); - } - Log_OC.v(TAG, "onRestart() end"); + mixinRegistry.onRestart(); } private void onThemeSettingsModeChanged() { @@ -152,56 +124,18 @@ private void onThemeSettingsModeChanged() { */ @Deprecated protected void setAccount(Account account, boolean savedAccount) { - boolean validAccount = account != null && accountManager.setCurrentOwnCloudAccount(account.name); - if (validAccount) { - currentAccount = account; - } else { - swapToDefaultAccount(); - } - - if(currentAccount != null) { - storageManager = new FileDataStorageManager(currentAccount, getContentResolver()); - capabilities = storageManager.getCapability(currentAccount.name); - } + sessionMixin.setAccount(account); } protected void setUser(User user) { - setAccount(user.toPlatformAccount(), false); - } - - /** - * Tries to swap the current ownCloud {@link Account} for other valid and existing. - * - * If no valid ownCloud {@link Account} exists, then the user is requested - * to create a new ownCloud {@link Account}. - */ - protected void swapToDefaultAccount() { - // default to the most recently used account - Account newAccount = accountManager.getCurrentAccount(); - - if (newAccount == null) { - /// no account available: force account creation - createAccount(true); - } else { - currentAccount = newAccount; - } + sessionMixin.setUser(user); } /** * Launches the account creation activity. - * - * @param mandatoryCreation When 'true', if an account is not created by the user, the app will be closed. - * To use when no ownCloud account is available. */ - protected void createAccount(boolean mandatoryCreation) { - AccountManager am = AccountManager.get(getApplicationContext()); - am.addAccount(MainApp.getAccountType(this), - null, - null, - null, - this, - new AccountCreationCallback(mandatoryCreation), - new Handler()); + protected void startAccountCreation() { + sessionMixin.startAccountCreation(); } /** @@ -211,7 +145,7 @@ protected void createAccount(boolean mandatoryCreation) { * set yet. */ public OCCapability getCapabilities() { - return capabilities; + return sessionMixin.getCapabilities(); } /** @@ -222,76 +156,14 @@ public OCCapability getCapabilities() { * is located. */ public Account getAccount() { - return currentAccount; + return sessionMixin.getCurrentAccount(); } public Optional getUser() { - if (currentAccount != null) { - return accountManager.getUser(currentAccount.name); - } else { - return Optional.empty(); - } + return sessionMixin.getUser(); } public FileDataStorageManager getStorageManager() { - return storageManager; - } - - /** - * Method that gets called when a new account has been successfully created. - * - * @param future - */ - protected void onAccountCreationSuccessful(AccountManagerFuture future) { - // no special handling in base activity - Log_OC.d(TAG,"onAccountCreationSuccessful"); - } - - /** - * Helper class handling a callback from the {@link AccountManager} after the creation of - * a new ownCloud {@link Account} finished, successfully or not. - */ - public class AccountCreationCallback implements AccountManagerCallback { - - boolean mMandatoryCreation; - - /** - * Constructor - * - * @param mandatoryCreation When 'true', if an account was not created, the app is closed. - */ - public AccountCreationCallback(boolean mandatoryCreation) { - mMandatoryCreation = mandatoryCreation; - } - - @Override - public void run(AccountManagerFuture future) { - boolean accountWasSet = false; - if (future != null) { - try { - Bundle result; - result = future.getResult(); - String name = result.getString(AccountManager.KEY_ACCOUNT_NAME); - String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE); - if (accountManager.setCurrentOwnCloudAccount(name)) { - setAccount(new Account(name, type), false); - accountWasSet = true; - } - - onAccountCreationSuccessful(future); - } catch (OperationCanceledException e) { - Log_OC.d(TAG, "Account creation canceled"); - - } catch (Exception e) { - Log_OC.e(TAG, "Account creation finished in exception: ", e); - } - - } else { - Log_OC.e(TAG, "Account creation callback with null bundle"); - } - if (mMandatoryCreation && !accountWasSet) { - moveTaskToBack(true); - } - } + return sessionMixin.getStorageManager(); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index b8b4ccde1815..a1196ccb69d2 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -28,7 +28,6 @@ import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountManagerFuture; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -516,7 +515,7 @@ private void handleAccountItemClick(MenuItem menuItem) { firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true); startActivity(firstRunIntent); } else { - createAccount(false); + startAccountCreation(); } break; @@ -1359,13 +1358,6 @@ private View findQuotaViewById(int id) { */ protected abstract void restart(); - @Override - protected void onAccountCreationSuccessful(AccountManagerFuture future) { - super.onAccountCreationSuccessful(future); - updateAccountList(); - restart(); - } - /** * Get list of users suitable for displaying in navigation drawer header. * First item is always current {@link User}. Remaining items are other diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 053926f197e8..d9a2c4735926 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -230,12 +230,10 @@ public class FileDisplayActivity extends FileActivity @Override protected void onCreate(Bundle savedInstanceState) { Log_OC.v(TAG, "onCreate() start"); - // Set the default theme to replace the launch screen theme. setTheme(R.style.Theme_ownCloud_Toolbar_Drawer); super.onCreate(savedInstanceState); - /// Load of saved instance state if (savedInstanceState != null) { mWaitingToPreview = savedInstanceState.getParcelable(FileDisplayActivity.KEY_WAITING_TO_PREVIEW); diff --git a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 531f448900e7..0fc849aca3b0 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -266,7 +266,7 @@ public void showFirstRunActivity() { } @Override - public void createAccount() { + public void startAccountCreation() { AccountManager am = AccountManager.get(getApplicationContext()); am.addAccount(MainApp.getAccountType(this), null, diff --git a/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java index 25e83d85688c..32472220a47b 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/AccountListAdapter.java @@ -158,7 +158,7 @@ private void setupAddAccountListItem(AddAccountViewHolderItem holder) { if (isProviderOrOwnInstallationVisible) { actionView.setOnClickListener(v -> accountListAdapterListener.showFirstRunActivity()); } else { - actionView.setOnClickListener(v -> accountListAdapterListener.createAccount()); + actionView.setOnClickListener(v -> accountListAdapterListener.startAccountCreation()); } } @@ -284,7 +284,7 @@ public interface AccountListAdapterListener { void showFirstRunActivity(); - void createAccount(); + void startAccountCreation(); } /** diff --git a/src/test/java/com/nextcloud/client/mixins/MixinRegistryTest.kt b/src/test/java/com/nextcloud/client/mixins/MixinRegistryTest.kt new file mode 100644 index 000000000000..531e971b47f9 --- /dev/null +++ b/src/test/java/com/nextcloud/client/mixins/MixinRegistryTest.kt @@ -0,0 +1,68 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2020 Nextcloud GmbH + * + * 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.nextcloud.client.mixins + +import android.os.Bundle +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.same +import org.junit.Test +import org.mockito.Mockito + +class MixinRegistryTest { + + @Test + fun `callbacks are invoked in order of calls and mixin registration`() { + // GIVEN + // registry has 2 mixins registered + val registry = MixinRegistry() + val firstMixin = mock() + val secondMixin = mock() + registry.add(firstMixin, secondMixin) + + // WHEN + // all lifecycle callbacks are invoked + val bundle = mock() + registry.onCreate(bundle) + registry.onStart() + registry.onResume() + registry.onPause() + registry.onStop() + registry.onDestroy() + + // THEN + // callbacks are invoked in order of mixin registration + // callbacks are invoked in order of registry calls + Mockito.inOrder(firstMixin, secondMixin).apply { + verify(firstMixin).onCreate(same(bundle)) + verify(secondMixin).onCreate(same(bundle)) + verify(firstMixin).onStart() + verify(secondMixin).onStart() + verify(firstMixin).onResume() + verify(secondMixin).onResume() + verify(firstMixin).onPause() + verify(secondMixin).onPause() + verify(firstMixin).onStop() + verify(secondMixin).onStop() + verify(firstMixin).onDestroy() + verify(secondMixin).onDestroy() + } + } +} diff --git a/src/test/java/com/nextcloud/client/mixins/SessionMixinTest.kt b/src/test/java/com/nextcloud/client/mixins/SessionMixinTest.kt new file mode 100644 index 000000000000..0e6d4ad545c7 --- /dev/null +++ b/src/test/java/com/nextcloud/client/mixins/SessionMixinTest.kt @@ -0,0 +1,67 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2020 Chris Narkiewicz + * Copyright (C) 2020 Nextcloud GmbH + * + * 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.nextcloud.client.mixins + +import android.app.Activity +import android.content.ContentResolver +import com.nextcloud.client.account.UserAccountManager +import com.nhaarman.mockitokotlin2.verify +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.same +import org.mockito.MockitoAnnotations + +class SessionMixinTest { + + @Mock + private lateinit var activity: Activity + + @Mock + private lateinit var contentResolver: ContentResolver + + @Mock + private lateinit var userAccountManager: UserAccountManager + + private lateinit var session: SessionMixin + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + session = SessionMixin( + activity, + contentResolver, + userAccountManager + ) + } + + @Test + fun `start account creation`() { + // WHEN + // start account creation flow + session.startAccountCreation() + + // THEN + // start is delegated to account manager + // account manager receives parent activity + verify(userAccountManager).startAccountCreation(same(activity)) + } +}