Merge pull request #2581 from nextcloud/bugfix/2446/fixDisappearingAccounts

Bugfix/2446/fix disappearing accounts
This commit is contained in:
Andy Scherzinger 2022-12-15 13:58:07 +01:00 committed by GitHub
commit 77c888fd91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 141 additions and 118 deletions

View File

@ -1661,7 +1661,7 @@ public class CallActivity extends CallBaseActivity {
} }
private void processMessage(NCSignalingMessage ncSignalingMessage) { private void processMessage(NCSignalingMessage ncSignalingMessage) {
if (ncSignalingMessage.getRoomType().equals("video") || ncSignalingMessage.getRoomType().equals("screen")) { if ("video".equals(ncSignalingMessage.getRoomType()) || "screen".equals(ncSignalingMessage.getRoomType())) {
String type = null; String type = null;
if (ncSignalingMessage.getPayload() != null && ncSignalingMessage.getPayload().getType() != null) { if (ncSignalingMessage.getPayload() != null && ncSignalingMessage.getPayload().getType() != null) {
type = ncSignalingMessage.getPayload().getType(); type = ncSignalingMessage.getPayload().getType();

View File

@ -28,6 +28,7 @@ import android.os.Handler
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.widget.Toast
import androidx.work.Data import androidx.work.Data
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
@ -323,9 +324,9 @@ class AccountVerificationController(args: Bundle? = null) :
} }
if (!TextUtils.isEmpty(displayName)) { if (!TextUtils.isEmpty(displayName)) {
storeProfile( storeProfile(
displayName, userProfileOverall.ocs!!.data!!.userId!!, displayName,
capabilities.ocs!!.data!! userProfileOverall.ocs!!.data!!.userId!!,
.capabilities!! capabilities.ocs!!.data!!.capabilities!!
) )
} else { } else {
if (activity != null) { if (activity != null) {
@ -443,25 +444,33 @@ class AccountVerificationController(args: Bundle? = null) :
} }
private fun proceedWithLogin() { private fun proceedWithLogin() {
Log.d(TAG, "proceedWithLogin...")
cookieManager.cookieStore.removeAll() cookieManager.cookieStore.removeAll()
val userDisabledCount = userManager.disableAllUsersWithoutId(internalAccountId).blockingGet()
Log.d(TAG, "Disabled $userDisabledCount users that had no id") val userToSetAsActive = userManager.getUserWithId(internalAccountId).blockingGet()
if (activity != null) { Log.d(TAG, "userToSetAsActive: " + userToSetAsActive.username)
activity!!.runOnUiThread {
if (userManager.users.blockingGet().size == 1) { if (userManager.setUserAsActive(userToSetAsActive).blockingGet()) {
router.setRoot( if (activity != null) {
RouterTransaction.with(ConversationsListController(Bundle())) activity!!.runOnUiThread {
.pushChangeHandler(HorizontalChangeHandler()) if (userManager.users.blockingGet().size == 1) {
.popChangeHandler(HorizontalChangeHandler()) router.setRoot(
) RouterTransaction.with(ConversationsListController(Bundle()))
} else { .pushChangeHandler(HorizontalChangeHandler())
if (isAccountImport) { .popChangeHandler(HorizontalChangeHandler())
ApplicationWideMessageHolder.getInstance().messageType = )
ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED } else {
if (isAccountImport) {
ApplicationWideMessageHolder.getInstance().messageType =
ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED
}
router.popToRoot()
} }
router.popToRoot()
} }
} }
} else {
Log.e(TAG, "failed to set active user")
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
} }
} }

View File

@ -209,26 +209,30 @@ class ConversationsListController(bundle: Bundle) :
private fun loadUserAvatar( private fun loadUserAvatar(
target: Target target: Target
) { ) {
if (activity != null) { if (activity != null) {
val url = ApiUtils.getUrlForAvatar( if (currentUser != null) {
currentUser!!.baseUrl, val url = ApiUtils.getUrlForAvatar(
currentUser!!.userId, currentUser!!.baseUrl,
true currentUser!!.userId,
) true
)
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
context.imageLoader.enqueue( context.imageLoader.enqueue(
ImageRequest.Builder(context) ImageRequest.Builder(context)
.data(url) .data(url)
.addHeader("Authorization", credentials) .addHeader("Authorization", credentials)
.placeholder(R.drawable.ic_user) .placeholder(R.drawable.ic_user)
.transformations(CircleCropTransformation()) .transformations(CircleCropTransformation())
.crossfade(true) .crossfade(true)
.target(target) .target(target)
.build() .build()
) )
} else {
Log.e(TAG, "currentUser was null in loadUserAvatar")
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
}
} }
} }
@ -238,6 +242,7 @@ class ConversationsListController(bundle: Bundle) :
override fun onStart(placeholder: Drawable?) { override fun onStart(placeholder: Drawable?) {
button.icon = placeholder button.icon = placeholder
} }
override fun onSuccess(result: Drawable) { override fun onSuccess(result: Drawable) {
button.icon = result button.icon = result
} }
@ -251,6 +256,7 @@ class ConversationsListController(bundle: Bundle) :
override fun onStart(placeholder: Drawable?) { override fun onStart(placeholder: Drawable?) {
menuItem.icon = placeholder menuItem.icon = placeholder
} }
override fun onSuccess(result: Drawable) { override fun onSuccess(result: Drawable) {
menuItem.icon = result menuItem.icon = result
} }
@ -288,6 +294,9 @@ class ConversationsListController(bundle: Bundle) :
.colorMaterialTextButton((activity as MainActivity?)!!.binding.switchAccountButton) .colorMaterialTextButton((activity as MainActivity?)!!.binding.switchAccountButton)
} }
fetchRooms() fetchRooms()
} else {
Log.e(TAG, "userManager.currentUser.blockingGet() returned null")
Toast.makeText(context, R.string.nc_common_error_sorry, Toast.LENGTH_LONG).show()
} }
} }

View File

@ -22,15 +22,14 @@
package com.nextcloud.talk.data.user package com.nextcloud.talk.data.user
import android.util.Log
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Delete import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import com.nextcloud.talk.data.user.model.UserEntity import com.nextcloud.talk.data.user.model.UserEntity
import com.nextcloud.talk.models.json.push.PushConfigurationState
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@ -74,9 +73,6 @@ abstract class UsersDao {
@Query("SELECT * FROM User where userId = :userId") @Query("SELECT * FROM User where userId = :userId")
abstract fun getUserWithUserId(userId: String): Maybe<UserEntity> abstract fun getUserWithUserId(userId: String): Maybe<UserEntity>
@Query("SELECT * FROM User where id != :id")
abstract fun getUsersWithoutId(id: Long): Single<List<UserEntity>>
@Query("SELECT * FROM User where scheduledForDeletion = 1") @Query("SELECT * FROM User where scheduledForDeletion = 1")
abstract fun getUsersScheduledForDeletion(): Single<List<UserEntity>> abstract fun getUsersScheduledForDeletion(): Single<List<UserEntity>>
@ -86,20 +82,16 @@ abstract class UsersDao {
@Query("SELECT * FROM User WHERE username = :username AND baseUrl = :server") @Query("SELECT * FROM User WHERE username = :username AND baseUrl = :server")
abstract fun getUserWithUsernameAndServer(username: String, server: String): Maybe<UserEntity> abstract fun getUserWithUsernameAndServer(username: String, server: String): Maybe<UserEntity>
@Transaction @Query(
@Suppress("Detekt.TooGenericExceptionCaught") // blockingGet() only throws RuntimeExceptions per rx docs "UPDATE User SET current = CASE " +
open fun setUserAsActiveWithId(id: Long): Boolean { "WHEN id == :id THEN 1 " +
return try { "WHEN id != :id THEN 0 " +
getUsers().blockingGet().forEach { user -> "END"
user.current = user.id == id )
updateUser(user) abstract fun setUserAsActiveWithId(id: Long): Int
}
true @Query("Update User SET pushConfigurationState = :state WHERE id == :id")
} catch (e: RuntimeException) { abstract fun updatePushState(id: Long, state: PushConfigurationState): Single<Int>
Log.e(TAG, "Error setting user active", e)
false
}
}
companion object { companion object {
const val TAG = "UsersDao" const val TAG = "UsersDao"

View File

@ -23,6 +23,7 @@
package com.nextcloud.talk.data.user package com.nextcloud.talk.data.user
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.push.PushConfigurationState
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@ -35,7 +36,6 @@ interface UsersRepository {
fun getUserWithId(id: Long): Maybe<User> fun getUserWithId(id: Long): Maybe<User>
fun getUserWithIdNotScheduledForDeletion(id: Long): Maybe<User> fun getUserWithIdNotScheduledForDeletion(id: Long): Maybe<User>
fun getUserWithUserId(userId: String): Maybe<User> fun getUserWithUserId(userId: String): Maybe<User>
fun getUsersWithoutUserId(id: Long): Single<List<User>>
fun getUsersScheduledForDeletion(): Single<List<User>> fun getUsersScheduledForDeletion(): Single<List<User>>
fun getUsersNotScheduledForDeletion(): Single<List<User>> fun getUsersNotScheduledForDeletion(): Single<List<User>>
fun getUserWithUsernameAndServer(username: String, server: String): Maybe<User> fun getUserWithUsernameAndServer(username: String, server: String): Maybe<User>
@ -43,4 +43,5 @@ interface UsersRepository {
fun insertUser(user: User): Long fun insertUser(user: User): Long
fun setUserAsActiveWithId(id: Long): Single<Boolean> fun setUserAsActiveWithId(id: Long): Single<Boolean>
fun deleteUser(user: User): Int fun deleteUser(user: User): Int
fun updatePushState(id: Long, state: PushConfigurationState): Single<Int>
} }

View File

@ -23,6 +23,7 @@
package com.nextcloud.talk.data.user package com.nextcloud.talk.data.user
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.push.PushConfigurationState
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@ -54,10 +55,6 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
return usersDao.getUserWithUserId(userId).map { UserMapper.toModel(it) } return usersDao.getUserWithUserId(userId).map { UserMapper.toModel(it) }
} }
override fun getUsersWithoutUserId(id: Long): Single<List<User>> {
return usersDao.getUsersWithoutId(id).map { UserMapper.toModel(it) }
}
override fun getUsersScheduledForDeletion(): Single<List<User>> { override fun getUsersScheduledForDeletion(): Single<List<User>> {
return usersDao.getUsersScheduledForDeletion().map { UserMapper.toModel(it) } return usersDao.getUsersScheduledForDeletion().map { UserMapper.toModel(it) }
} }
@ -79,10 +76,19 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
} }
override fun setUserAsActiveWithId(id: Long): Single<Boolean> { override fun setUserAsActiveWithId(id: Long): Single<Boolean> {
return Single.just(usersDao.setUserAsActiveWithId(id)) val amountUpdated = usersDao.setUserAsActiveWithId(id)
return if (amountUpdated > 0) {
Single.just(true)
} else {
Single.just(false)
}
} }
override fun deleteUser(user: User): Int { override fun deleteUser(user: User): Int {
return usersDao.deleteUser(UserMapper.toEntity(user)) return usersDao.deleteUser(UserMapper.toEntity(user))
} }
override fun updatePushState(id: Long, state: PushConfigurationState): Single<Int> {
return usersDao.updatePushState(id, state)
}
} }

View File

@ -146,8 +146,13 @@ public class ChooseAccountDialogFragment extends DialogFragment {
User userEntity; User userEntity;
Participant participant; Participant participant;
for (Object userItem : userManager.getUsers().blockingGet()) { for (User userItem : userManager.getUsers().blockingGet()) {
userEntity = (User) userItem; userEntity = userItem;
Log.d(TAG, "---------------------");
Log.d(TAG, "userEntity.getUserId() " + userEntity.getUserId());
Log.d(TAG, "userEntity.getCurrent() " + userEntity.getCurrent());
Log.d(TAG, "---------------------");
if (!userEntity.getCurrent()) { if (!userEntity.getCurrent()) {
String userId; String userId;
if (userEntity.getUserId() != null) { if (userEntity.getUserId() != null) {

View File

@ -43,6 +43,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito
val currentUser: Maybe<User> val currentUser: Maybe<User>
get() { get() {
return userRepository.getActiveUser() return userRepository.getActiveUser()
.switchIfEmpty(getAnyUserAndSetAsActive())
} }
val currentUserObservable: Observable<User> val currentUserObservable: Observable<User>
@ -58,22 +59,6 @@ class UserManager internal constructor(private val userRepository: UsersReposito
return userRepository.getUserWithId(id) return userRepository.getUserWithId(id)
} }
fun disableAllUsersWithoutId(id: Long): Single<Int> {
val results = userRepository.getUsersWithoutUserId(id)
return results.map { users ->
var count = 0
if (users.isNotEmpty()) {
for (entity in users) {
entity.current = false
userRepository.updateUser(entity)
count++
}
}
count
}
}
fun checkIfUserIsScheduledForDeletion(username: String, server: String): Single<Boolean> { fun checkIfUserIsScheduledForDeletion(username: String, server: String): Single<Boolean> {
return userRepository return userRepository
.getUserWithUsernameAndServer(username, server) .getUserWithUsernameAndServer(username, server)
@ -165,7 +150,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito
else -> { else -> {
user.token = userAttributes.token user.token = userAttributes.token
user.baseUrl = userAttributes.serverUrl user.baseUrl = userAttributes.serverUrl
user.current = true user.current = userAttributes.currentUser
user.userId = userAttributes.userId user.userId = userAttributes.userId
user.token = userAttributes.token user.token = userAttributes.token
user.displayName = userAttributes.displayName user.displayName = userAttributes.displayName
@ -246,6 +231,10 @@ class UserManager internal constructor(private val userRepository: UsersReposito
return user return user
} }
fun updatePushState(id: Long, state: PushConfigurationState): Single<Int> {
return userRepository.updatePushState(id, state)
}
companion object { companion object {
const val TAG = "UserManager" const val TAG = "UserManager"
} }
@ -253,7 +242,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito
data class UserAttributes( data class UserAttributes(
val id: Long?, val id: Long?,
val serverUrl: String?, val serverUrl: String?,
val currentUser: Boolean?, val currentUser: Boolean,
val userId: String?, val userId: String?,
val token: String?, val token: String?,
val displayName: String?, val displayName: String?,

View File

@ -235,17 +235,17 @@ public class PushUtils {
List<User> users = userManager.getUsers().blockingGet(); List<User> users = userManager.getUsers().blockingGet();
for (User user : users) { for (User user : users) {
if (!user.getScheduledForDeletion()) { if (!user.getScheduledForDeletion()) {
Map<String, String> nextcloudRegisterPushMap = new HashMap<>(); Map<String, String> nextcloudRegisterPushMap = new HashMap<>();
nextcloudRegisterPushMap.put("format", "json"); nextcloudRegisterPushMap.put("format", "json");
nextcloudRegisterPushMap.put("pushTokenHash", pushTokenHash); nextcloudRegisterPushMap.put("pushTokenHash", pushTokenHash);
nextcloudRegisterPushMap.put("devicePublicKey", devicePublicKeyBase64); nextcloudRegisterPushMap.put("devicePublicKey", devicePublicKeyBase64);
nextcloudRegisterPushMap.put("proxyServer", proxyServer); nextcloudRegisterPushMap.put("proxyServer", proxyServer);
registerDeviceWithNextcloud(ncApi, nextcloudRegisterPushMap, token, user); registerDeviceWithNextcloud(ncApi, nextcloudRegisterPushMap, token, user);
}
} }
}
} }
} else { } else {
@ -260,9 +260,9 @@ public class PushUtils {
String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken()); String credentials = ApiUtils.getCredentials(user.getUsername(), user.getToken());
ncApi.registerDeviceForNotificationsWithNextcloud( ncApi.registerDeviceForNotificationsWithNextcloud(
credentials, credentials,
ApiUtils.getUrlNextcloudPush(user.getBaseUrl()), ApiUtils.getUrlNextcloudPush(user.getBaseUrl()),
nextcloudRegisterPushMap) nextcloudRegisterPushMap)
.subscribe(new Observer<PushRegistrationOverall>() { .subscribe(new Observer<PushRegistrationOverall>() {
@Override @Override
public void onSubscribe(@NonNull Disposable d) { public void onSubscribe(@NonNull Disposable d) {
@ -311,7 +311,7 @@ public class PushUtils {
public void onNext(@NonNull Void aVoid) { public void onNext(@NonNull Void aVoid) {
try { try {
Log.d(TAG, "pushToken successfully registered at pushproxy."); Log.d(TAG, "pushToken successfully registered at pushproxy.");
createOrUpdateUser(proxyMap, user); updatePushStateForUser(proxyMap, user);
} catch (IOException e) { } catch (IOException e) {
Log.e(TAG, "IOException while updating user", e); Log.e(TAG, "IOException while updating user", e);
} }
@ -330,7 +330,7 @@ public class PushUtils {
}); });
} }
private void createOrUpdateUser(Map<String, String> proxyMap, User user) throws IOException { private void updatePushStateForUser(Map<String, String> proxyMap, User user) throws IOException {
PushConfigurationState pushConfigurationState = new PushConfigurationState(); PushConfigurationState pushConfigurationState = new PushConfigurationState();
pushConfigurationState.setPushToken(proxyMap.get("pushToken")); pushConfigurationState.setPushToken(proxyMap.get("pushToken"));
pushConfigurationState.setDeviceIdentifier(proxyMap.get("deviceIdentifier")); pushConfigurationState.setDeviceIdentifier(proxyMap.get("deviceIdentifier"));
@ -338,27 +338,31 @@ public class PushUtils {
pushConfigurationState.setUserPublicKey(proxyMap.get("userPublicKey")); pushConfigurationState.setUserPublicKey(proxyMap.get("userPublicKey"));
pushConfigurationState.setUsesRegularPass(Boolean.FALSE); pushConfigurationState.setUsesRegularPass(Boolean.FALSE);
user.setPushConfigurationState(pushConfigurationState); if (user.getId() != null) {
userManager.saveUser(user).subscribe(new SingleObserver<Integer>() { userManager.updatePushState(user.getId(), pushConfigurationState).subscribe(new SingleObserver<Integer>() {
@Override @Override
public void onSubscribe(Disposable d) { public void onSubscribe(Disposable d) {
// unused atm // unused atm
} }
@Override @Override
public void onSuccess(Integer integer) { public void onSuccess(Integer integer) {
eventBus.post(new EventStatus(UserIdUtils.INSTANCE.getIdForUser(user), eventBus.post(new EventStatus(UserIdUtils.INSTANCE.getIdForUser(user),
EventStatus.EventType.PUSH_REGISTRATION, EventStatus.EventType.PUSH_REGISTRATION,
true)); true));
} }
@Override
public void onError(Throwable e) {
eventBus.post(new EventStatus(UserIdUtils.INSTANCE.getIdForUser(user),
EventStatus.EventType.PUSH_REGISTRATION,
false));
}
});
} else {
Log.e(TAG, "failed to update updatePushStateForUser. user.getId() was null");
}
@Override
public void onError(Throwable e) {
eventBus.post(new EventStatus(UserIdUtils.INSTANCE.getIdForUser(user),
EventStatus.EventType.PUSH_REGISTRATION,
false));
}
});
} }
private Key readKeyFromString(boolean readPublicKey, String keyString) { private Key readKeyFromString(boolean readPublicKey, String keyString) {

View File

@ -31,7 +31,12 @@ import javax.inject.Inject
* Listens to changes in the database and provides the current user without needing to query the database everytime. * Listens to changes in the database and provides the current user without needing to query the database everytime.
*/ */
class CurrentUserProviderImpl @Inject constructor(private val userManager: UserManager) : CurrentUserProviderNew { class CurrentUserProviderImpl @Inject constructor(private val userManager: UserManager) : CurrentUserProviderNew {
private var _currentUser: User? = null private var _currentUser: User? = null
// synchronized to avoid multiple observers initialized from different threads
@get:Synchronized
@set:Synchronized
private var currentUserObserver: Disposable? = null private var currentUserObserver: Disposable? = null
override val currentUser: Maybe<User> override val currentUser: Maybe<User>
@ -40,8 +45,10 @@ class CurrentUserProviderImpl @Inject constructor(private val userManager: UserM
// immediately get a result synchronously // immediately get a result synchronously
_currentUser = userManager.currentUser.blockingGet() _currentUser = userManager.currentUser.blockingGet()
if (currentUserObserver == null) { if (currentUserObserver == null) {
// start observable for auto-updates currentUserObserver = userManager.currentUserObservable
currentUserObserver = userManager.currentUserObservable.subscribe { _currentUser = it } .subscribe {
_currentUser = it
}
} }
} }
return _currentUser?.let { Maybe.just(it) } ?: Maybe.empty() return _currentUser?.let { Maybe.just(it) } ?: Maybe.empty()

View File

@ -64,8 +64,9 @@ public class MagicKeyManager implements X509KeyManager {
@Override @Override
public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
String alias; String alias;
if ((userManager.getCurrentUser().blockingGet() != null && User currentUser = userManager.getCurrentUser().blockingGet();
!TextUtils.isEmpty(alias = userManager.getCurrentUser().blockingGet().getClientCertificate())) || if ((currentUser != null &&
!TextUtils.isEmpty(alias = currentUser.getClientCertificate())) ||
!TextUtils.isEmpty(alias = appPreferences.getTemporaryClientCertAlias()) !TextUtils.isEmpty(alias = appPreferences.getTemporaryClientCertAlias())
&& new ArrayList<>(Arrays.asList(getClientAliases())).contains(alias)) { && new ArrayList<>(Arrays.asList(getClientAliases())).contains(alias)) {
return alias; return alias;