Extract account logic from BaseActivity into a mixin

Signed-off-by: Chris Narkiewicz <hello@ezaquarii.com>
This commit is contained in:
Chris Narkiewicz 2019-12-25 14:05:49 +00:00
parent e03b819ef3
commit 26f2d52a5a
No known key found for this signature in database
GPG key ID: 30D28CA4CCC665C6
17 changed files with 478 additions and 169 deletions

View file

@ -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 @@ public interface UserAccountManager extends CurrentAccountProvider {
}
}
/**
* 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);
}

View file

@ -22,6 +22,7 @@ package com.nextcloud.client.account;
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 class UserAccountManagerImpl implements UserAccountManager {
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);
}
}

View file

@ -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 <T> post(task: () -> T, onResult: OnResultCallback<T>? = null, onError: OnErrorCallback? = null): Cancellable
}

View file

@ -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()
}

View file

@ -23,7 +23,6 @@ package com.nextcloud.client.di;
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 class ActivityInjector implements Application.ActivityLifecycleCallbacks
// not needed
}
}

View file

@ -0,0 +1,40 @@
/*
* Nextcloud Android client application
*
* @author Chris Narkiewicz
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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 */ }
}

View file

@ -0,0 +1,88 @@
/*
* Nextcloud Android client application
*
* @author Chris Narkiewicz
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<ActivityMixin>()
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() }
}
}

View file

@ -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).

View file

@ -0,0 +1,133 @@
/*
* Nextcloud Android client application
*
* @author Chris Narkiewicz
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<User> = 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)
}
}

View file

@ -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);

View file

@ -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 abstract class BaseActivity extends AppCompatActivity implements Injectab
@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 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
@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 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
@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 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
*/
@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 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
* set yet.
*/
public OCCapability getCapabilities() {
return capabilities;
return sessionMixin.getCapabilities();
}
/**
@ -222,76 +156,14 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
* is located.
*/
public Account getAccount() {
return currentAccount;
return sessionMixin.getCurrentAccount();
}
public Optional<User> 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<Bundle> 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<Bundle> {
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<Bundle> 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();
}
}

View file

@ -28,7 +28,6 @@ package com.owncloud.android.ui.activity;
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 @@ public abstract class DrawerActivity extends ToolbarActivity
firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true);
startActivity(firstRunIntent);
} else {
createAccount(false);
startAccountCreation();
}
break;
@ -1359,13 +1358,6 @@ public abstract class DrawerActivity extends ToolbarActivity
*/
protected abstract void restart();
@Override
protected void onAccountCreationSuccessful(AccountManagerFuture<Bundle> 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

View file

@ -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);

View file

@ -266,7 +266,7 @@ public class ManageAccountsActivity extends FileActivity implements AccountListA
}
@Override
public void createAccount() {
public void startAccountCreation() {
AccountManager am = AccountManager.get(getApplicationContext());
am.addAccount(MainApp.getAccountType(this),
null,

View file

@ -158,7 +158,7 @@ public class AccountListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
if (isProviderOrOwnInstallationVisible) {
actionView.setOnClickListener(v -> accountListAdapterListener.showFirstRunActivity());
} else {
actionView.setOnClickListener(v -> accountListAdapterListener.createAccount());
actionView.setOnClickListener(v -> accountListAdapterListener.startAccountCreation());
}
}
@ -284,7 +284,7 @@ public class AccountListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
void showFirstRunActivity();
void createAccount();
void startAccountCreation();
}
/**

View file

@ -0,0 +1,68 @@
/*
* Nextcloud Android client application
*
* @author Chris Narkiewicz
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<ActivityMixin>()
val secondMixin = mock<ActivityMixin>()
registry.add(firstMixin, secondMixin)
// WHEN
// all lifecycle callbacks are invoked
val bundle = mock<Bundle>()
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()
}
}
}

View file

@ -0,0 +1,67 @@
/*
* Nextcloud Android client application
*
* @author Chris Narkiewicz
* Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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))
}
}