mirror of
https://github.com/nextcloud/android.git
synced 2024-11-25 06:35:48 +03:00
Merge pull request #13074 from nextcloud/bugfix/fix-npe-FileDataStorageManager
Bugfix Fix NPE FileDataStorageManager
This commit is contained in:
commit
a352271896
8 changed files with 73 additions and 94 deletions
|
@ -24,7 +24,7 @@ public interface CurrentAccountProvider {
|
||||||
* @return Currently selected {@link Account} or first valid {@link Account} registered in OS or null, if not available at all.
|
* @return Currently selected {@link Account} or first valid {@link Account} registered in OS or null, if not available at all.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@Nullable
|
@NonNull
|
||||||
Account getCurrentAccount();
|
Account getCurrentAccount();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,9 +18,9 @@ import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.nextcloud.common.NextcloudClient;
|
import com.nextcloud.common.NextcloudClient;
|
||||||
|
import com.nextcloud.utils.extensions.AccountExtensionsKt;
|
||||||
import com.owncloud.android.MainApp;
|
import com.owncloud.android.MainApp;
|
||||||
import com.owncloud.android.R;
|
import com.owncloud.android.R;
|
||||||
import com.owncloud.android.authentication.AuthenticatorActivity;
|
import com.owncloud.android.authentication.AuthenticatorActivity;
|
||||||
|
@ -39,6 +39,7 @@ import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -132,42 +133,42 @@ public class UserAccountManagerImpl implements UserAccountManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@NonNull
|
||||||
public Account getCurrentAccount() {
|
public Account getCurrentAccount() {
|
||||||
Account[] ocAccounts = getAccounts();
|
Account[] ocAccounts = getAccounts();
|
||||||
Account defaultAccount = null;
|
|
||||||
|
|
||||||
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
|
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context);
|
||||||
|
|
||||||
SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
SharedPreferences appPreferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
String accountName = appPreferences.getString(PREF_SELECT_OC_ACCOUNT, null);
|
String accountName = appPreferences.getString(PREF_SELECT_OC_ACCOUNT, null);
|
||||||
|
|
||||||
// account validation: the saved account MUST be in the list of ownCloud Accounts known by the AccountManager
|
Account defaultAccount = Arrays.stream(ocAccounts)
|
||||||
if (accountName != null) {
|
.filter(account -> account.name.equals(accountName))
|
||||||
for (Account account : ocAccounts) {
|
.findFirst()
|
||||||
if (account.name.equals(accountName)) {
|
.orElse(null);
|
||||||
defaultAccount = account;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultAccount == null && ocAccounts.length > 0) {
|
|
||||||
// take first which is not pending for removal account as fallback
|
// take first which is not pending for removal account as fallback
|
||||||
for (Account account: ocAccounts) {
|
if (defaultAccount == null) {
|
||||||
boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(account.name,
|
defaultAccount = Arrays.stream(ocAccounts)
|
||||||
PENDING_FOR_REMOVAL);
|
.filter(account -> !arbitraryDataProvider.getBooleanValue(account.name, PENDING_FOR_REMOVAL))
|
||||||
|
.findFirst()
|
||||||
if (!pendingForRemoval) {
|
.orElse(null);
|
||||||
defaultAccount = account;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (defaultAccount == null) {
|
||||||
|
if (ocAccounts.length > 0) {
|
||||||
|
defaultAccount = ocAccounts[0];
|
||||||
|
} else {
|
||||||
|
defaultAccount = getAnonymousAccount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultAccount;
|
return defaultAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Account getAnonymousAccount() {
|
||||||
|
return new Account("Anonymous", context.getString(R.string.anonymous_account_type));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Temporary solution to convert platform account to user instance.
|
* Temporary solution to convert platform account to user instance.
|
||||||
* It takes null and returns null on error to ease error handling
|
* It takes null and returns null on error to ease error handling
|
||||||
|
@ -177,8 +178,8 @@ public class UserAccountManagerImpl implements UserAccountManager {
|
||||||
* @return User instance or null, if conversion failed
|
* @return User instance or null, if conversion failed
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private User createUserFromAccount(@Nullable Account account) {
|
private User createUserFromAccount(@NonNull Account account) {
|
||||||
if (account == null) {
|
if (AccountExtensionsKt.isAnonymous(account, context)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,7 +261,7 @@ public class UserAccountManagerImpl implements UserAccountManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@NonNull
|
||||||
public Account getAccountByName(String name) {
|
public Account getAccountByName(String name) {
|
||||||
for (Account account : getAccounts()) {
|
for (Account account : getAccounts()) {
|
||||||
if (account.name.equals(name)) {
|
if (account.name.equals(name)) {
|
||||||
|
@ -268,7 +269,7 @@ public class UserAccountManagerImpl implements UserAccountManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return getAnonymousAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -100,8 +100,7 @@ class AppModule {
|
||||||
@Provides
|
@Provides
|
||||||
UserAccountManager userAccountManager(
|
UserAccountManager userAccountManager(
|
||||||
Context context,
|
Context context,
|
||||||
AccountManager accountManager
|
AccountManager accountManager) {
|
||||||
) {
|
|
||||||
return new UserAccountManagerImpl(context, accountManager);
|
return new UserAccountManagerImpl(context, accountManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,14 +9,12 @@ package com.nextcloud.client.mixins
|
||||||
|
|
||||||
import android.accounts.Account
|
import android.accounts.Account
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ContentResolver
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.nextcloud.client.account.User
|
import com.nextcloud.client.account.User
|
||||||
import com.nextcloud.client.account.UserAccountManager
|
import com.nextcloud.client.account.UserAccountManager
|
||||||
import com.owncloud.android.datamodel.FileDataStorageManager
|
import com.nextcloud.utils.extensions.isAnonymous
|
||||||
import com.owncloud.android.lib.resources.status.OCCapability
|
import com.owncloud.android.lib.resources.status.OCCapability
|
||||||
import com.owncloud.android.ui.activity.BaseActivity
|
|
||||||
import com.owncloud.android.utils.theme.CapabilityUtils
|
import com.owncloud.android.utils.theme.CapabilityUtils
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
|
@ -27,36 +25,25 @@ import java.util.Optional
|
||||||
* It is an intermediary step facilitating comprehensive rework of
|
* It is an intermediary step facilitating comprehensive rework of
|
||||||
* account handling logic.
|
* account handling logic.
|
||||||
*/
|
*/
|
||||||
class SessionMixin constructor(
|
class SessionMixin(
|
||||||
private val activity: Activity,
|
private val activity: Activity,
|
||||||
private val contentResolver: ContentResolver,
|
|
||||||
private val accountManager: UserAccountManager
|
private val accountManager: UserAccountManager
|
||||||
) : ActivityMixin {
|
) : ActivityMixin {
|
||||||
|
lateinit var currentAccount: Account
|
||||||
private companion object {
|
|
||||||
private val TAG = BaseActivity::class.java.simpleName
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentAccount: Account? = null
|
|
||||||
private set
|
|
||||||
var storageManager: FileDataStorageManager? = null
|
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val capabilities: OCCapability?
|
val capabilities: OCCapability?
|
||||||
get() = getUser()
|
get() = getUser()
|
||||||
.map { CapabilityUtils.getCapability(it, activity) }
|
.map { CapabilityUtils.getCapability(it, activity) }
|
||||||
.orElse(null)
|
.orElse(null)
|
||||||
|
|
||||||
fun setAccount(account: Account?) {
|
fun setAccount(account: Account) {
|
||||||
val validAccount = account != null && accountManager.setCurrentOwnCloudAccount(account.name)
|
val validAccount = (accountManager.setCurrentOwnCloudAccount(account.name))
|
||||||
if (validAccount) {
|
|
||||||
currentAccount = account
|
|
||||||
} else {
|
|
||||||
swapToDefaultAccount()
|
|
||||||
}
|
|
||||||
|
|
||||||
currentAccount?.let {
|
currentAccount = if (validAccount) {
|
||||||
val storageManager = FileDataStorageManager(getUser().get(), contentResolver)
|
account
|
||||||
this.storageManager = storageManager
|
} else {
|
||||||
|
getDefaultAccount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,9 +51,12 @@ class SessionMixin constructor(
|
||||||
setAccount(user.toPlatformAccount())
|
setAccount(user.toPlatformAccount())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUser(): Optional<User> = when (val it = this.currentAccount) {
|
fun getUser(): Optional<User> {
|
||||||
null -> Optional.empty()
|
return if (currentAccount.isAnonymous(activity)) {
|
||||||
else -> accountManager.getUser(it.name)
|
Optional.empty()
|
||||||
|
} else {
|
||||||
|
accountManager.getUser(currentAccount.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -75,16 +65,14 @@ class SessionMixin constructor(
|
||||||
* If no valid ownCloud [Account] exists, then the user is requested
|
* If no valid ownCloud [Account] exists, then the user is requested
|
||||||
* to create a new ownCloud [Account].
|
* to create a new ownCloud [Account].
|
||||||
*/
|
*/
|
||||||
private fun swapToDefaultAccount() {
|
private fun getDefaultAccount(): Account {
|
||||||
// default to the most recently used account
|
val defaultAccount = accountManager.currentAccount
|
||||||
val newAccount = accountManager.currentAccount
|
|
||||||
if (newAccount == null) {
|
if (defaultAccount.isAnonymous(activity)) {
|
||||||
// no account available: force account creation
|
|
||||||
currentAccount = null
|
|
||||||
startAccountCreation()
|
startAccountCreation()
|
||||||
} else {
|
|
||||||
currentAccount = newAccount
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return defaultAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +86,7 @@ class SessionMixin constructor(
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
val current = accountManager.currentAccount
|
val current = accountManager.currentAccount
|
||||||
val currentAccount = this.currentAccount
|
val currentAccount = this.currentAccount
|
||||||
if (current != null && currentAccount != null && !currentAccount.name.equals(current.name)) {
|
if (!currentAccount.name.equals(current.name)) {
|
||||||
this.currentAccount = current
|
this.currentAccount = current
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,9 +97,9 @@ class SessionMixin constructor(
|
||||||
*/
|
*/
|
||||||
override fun onRestart() {
|
override fun onRestart() {
|
||||||
super.onRestart()
|
super.onRestart()
|
||||||
val validAccount = currentAccount != null && accountManager.exists(currentAccount)
|
val validAccount = accountManager.exists(currentAccount)
|
||||||
if (!validAccount) {
|
if (!validAccount) {
|
||||||
swapToDefaultAccount()
|
getDefaultAccount()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,11 +108,4 @@ class SessionMixin constructor(
|
||||||
val account = accountManager.currentAccount
|
val account = accountManager.currentAccount
|
||||||
setAccount(account)
|
setAccount(account)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
if (currentAccount == null) {
|
|
||||||
swapToDefaultAccount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Nextcloud - Android Client
|
||||||
|
*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Your Name <your@email.com>
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.nextcloud.utils.extensions
|
||||||
|
|
||||||
|
import android.accounts.Account
|
||||||
|
import android.content.Context
|
||||||
|
import com.owncloud.android.R
|
||||||
|
|
||||||
|
fun Account.isAnonymous(context: Context): Boolean = type.equals(context.getString(R.string.anonymous_account_type))
|
|
@ -48,6 +48,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
|
|
||||||
@Inject UserAccountManager accountManager;
|
@Inject UserAccountManager accountManager;
|
||||||
@Inject AppPreferences preferences;
|
@Inject AppPreferences preferences;
|
||||||
|
@Inject FileDataStorageManager fileDataStorageManager;
|
||||||
|
|
||||||
private AppPreferences.Listener onPreferencesChanged = new AppPreferences.Listener() {
|
private AppPreferences.Listener onPreferencesChanged = new AppPreferences.Listener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -63,9 +64,7 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
sessionMixin = new SessionMixin(this,
|
sessionMixin = new SessionMixin(this, accountManager);
|
||||||
getContentResolver(),
|
|
||||||
accountManager);
|
|
||||||
mixinRegistry.add(sessionMixin);
|
mixinRegistry.add(sessionMixin);
|
||||||
|
|
||||||
if (enableAccountHandling) {
|
if (enableAccountHandling) {
|
||||||
|
@ -174,6 +173,6 @@ public abstract class BaseActivity extends AppCompatActivity implements Injectab
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileDataStorageManager getStorageManager() {
|
public FileDataStorageManager getStorageManager() {
|
||||||
return sessionMixin.getStorageManager();
|
return fileDataStorageManager;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
<string name="app_config_proxy_host_title">Proxy Host Name</string>
|
<string name="app_config_proxy_host_title">Proxy Host Name</string>
|
||||||
<string name="app_config_proxy_port_title">Proxy Port</string>
|
<string name="app_config_proxy_port_title">Proxy Port</string>
|
||||||
|
|
||||||
|
<string name="anonymous_account_type" translatable="false">AnonymousAccountType</string>
|
||||||
|
|
||||||
<string name="assistant_screen_task_types_error_state_message">Unable to fetch task types, please check your internet connection.</string>
|
<string name="assistant_screen_task_types_error_state_message">Unable to fetch task types, please check your internet connection.</string>
|
||||||
<string name="assistant_screen_task_list_error_state_message">Unable to fetch task list, please check your internet connection.</string>
|
<string name="assistant_screen_task_list_error_state_message">Unable to fetch task list, please check your internet connection.</string>
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
package com.nextcloud.client.mixins
|
package com.nextcloud.client.mixins
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ContentResolver
|
|
||||||
import com.nextcloud.client.account.UserAccountManager
|
import com.nextcloud.client.account.UserAccountManager
|
||||||
import junit.framework.Assert.assertNull
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
|
@ -24,9 +22,6 @@ class SessionMixinTest {
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var activity: Activity
|
private lateinit var activity: Activity
|
||||||
|
|
||||||
@Mock
|
|
||||||
private lateinit var contentResolver: ContentResolver
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private lateinit var userAccountManager: UserAccountManager
|
private lateinit var userAccountManager: UserAccountManager
|
||||||
|
|
||||||
|
@ -38,7 +33,6 @@ class SessionMixinTest {
|
||||||
session = spy(
|
session = spy(
|
||||||
SessionMixin(
|
SessionMixin(
|
||||||
activity,
|
activity,
|
||||||
contentResolver,
|
|
||||||
userAccountManager
|
userAccountManager
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -55,15 +49,4 @@ class SessionMixinTest {
|
||||||
// account manager receives parent activity
|
// account manager receives parent activity
|
||||||
verify(userAccountManager).startAccountCreation(same(activity))
|
verify(userAccountManager).startAccountCreation(same(activity))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `trigger accountCreation on resume when currentAccount is null`() {
|
|
||||||
// WHEN
|
|
||||||
// start onResume and currentAccount is null
|
|
||||||
assertNull(session.currentAccount)
|
|
||||||
session.onResume()
|
|
||||||
// THEN
|
|
||||||
// accountCreation flow is started
|
|
||||||
verify(session).startAccountCreation()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue