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) {
if (ncSignalingMessage.getRoomType().equals("video") || ncSignalingMessage.getRoomType().equals("screen")) {
if ("video".equals(ncSignalingMessage.getRoomType()) || "screen".equals(ncSignalingMessage.getRoomType())) {
String type = null;
if (ncSignalingMessage.getPayload() != null && ncSignalingMessage.getPayload().getType() != null) {
type = ncSignalingMessage.getPayload().getType();

View File

@ -28,6 +28,7 @@ import android.os.Handler
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.work.Data
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
@ -323,9 +324,9 @@ class AccountVerificationController(args: Bundle? = null) :
}
if (!TextUtils.isEmpty(displayName)) {
storeProfile(
displayName, userProfileOverall.ocs!!.data!!.userId!!,
capabilities.ocs!!.data!!
.capabilities!!
displayName,
userProfileOverall.ocs!!.data!!.userId!!,
capabilities.ocs!!.data!!.capabilities!!
)
} else {
if (activity != null) {
@ -443,25 +444,33 @@ class AccountVerificationController(args: Bundle? = null) :
}
private fun proceedWithLogin() {
Log.d(TAG, "proceedWithLogin...")
cookieManager.cookieStore.removeAll()
val userDisabledCount = userManager.disableAllUsersWithoutId(internalAccountId).blockingGet()
Log.d(TAG, "Disabled $userDisabledCount users that had no id")
if (activity != null) {
activity!!.runOnUiThread {
if (userManager.users.blockingGet().size == 1) {
router.setRoot(
RouterTransaction.with(ConversationsListController(Bundle()))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
} else {
if (isAccountImport) {
ApplicationWideMessageHolder.getInstance().messageType =
ApplicationWideMessageHolder.MessageType.ACCOUNT_WAS_IMPORTED
val userToSetAsActive = userManager.getUserWithId(internalAccountId).blockingGet()
Log.d(TAG, "userToSetAsActive: " + userToSetAsActive.username)
if (userManager.setUserAsActive(userToSetAsActive).blockingGet()) {
if (activity != null) {
activity!!.runOnUiThread {
if (userManager.users.blockingGet().size == 1) {
router.setRoot(
RouterTransaction.with(ConversationsListController(Bundle()))
.pushChangeHandler(HorizontalChangeHandler())
.popChangeHandler(HorizontalChangeHandler())
)
} 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(
target: Target
) {
if (activity != null) {
val url = ApiUtils.getUrlForAvatar(
currentUser!!.baseUrl,
currentUser!!.userId,
true
)
if (currentUser != null) {
val url = ApiUtils.getUrlForAvatar(
currentUser!!.baseUrl,
currentUser!!.userId,
true
)
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
context.imageLoader.enqueue(
ImageRequest.Builder(context)
.data(url)
.addHeader("Authorization", credentials)
.placeholder(R.drawable.ic_user)
.transformations(CircleCropTransformation())
.crossfade(true)
.target(target)
.build()
)
context.imageLoader.enqueue(
ImageRequest.Builder(context)
.data(url)
.addHeader("Authorization", credentials)
.placeholder(R.drawable.ic_user)
.transformations(CircleCropTransformation())
.crossfade(true)
.target(target)
.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?) {
button.icon = placeholder
}
override fun onSuccess(result: Drawable) {
button.icon = result
}
@ -251,6 +256,7 @@ class ConversationsListController(bundle: Bundle) :
override fun onStart(placeholder: Drawable?) {
menuItem.icon = placeholder
}
override fun onSuccess(result: Drawable) {
menuItem.icon = result
}
@ -288,6 +294,9 @@ class ConversationsListController(bundle: Bundle) :
.colorMaterialTextButton((activity as MainActivity?)!!.binding.switchAccountButton)
}
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
import android.util.Log
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import com.nextcloud.talk.data.user.model.UserEntity
import com.nextcloud.talk.models.json.push.PushConfigurationState
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@ -74,9 +73,6 @@ abstract class UsersDao {
@Query("SELECT * FROM User where userId = :userId")
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")
abstract fun getUsersScheduledForDeletion(): Single<List<UserEntity>>
@ -86,20 +82,16 @@ abstract class UsersDao {
@Query("SELECT * FROM User WHERE username = :username AND baseUrl = :server")
abstract fun getUserWithUsernameAndServer(username: String, server: String): Maybe<UserEntity>
@Transaction
@Suppress("Detekt.TooGenericExceptionCaught") // blockingGet() only throws RuntimeExceptions per rx docs
open fun setUserAsActiveWithId(id: Long): Boolean {
return try {
getUsers().blockingGet().forEach { user ->
user.current = user.id == id
updateUser(user)
}
true
} catch (e: RuntimeException) {
Log.e(TAG, "Error setting user active", e)
false
}
}
@Query(
"UPDATE User SET current = CASE " +
"WHEN id == :id THEN 1 " +
"WHEN id != :id THEN 0 " +
"END"
)
abstract fun setUserAsActiveWithId(id: Long): Int
@Query("Update User SET pushConfigurationState = :state WHERE id == :id")
abstract fun updatePushState(id: Long, state: PushConfigurationState): Single<Int>
companion object {
const val TAG = "UsersDao"

View File

@ -23,6 +23,7 @@
package com.nextcloud.talk.data.user
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.push.PushConfigurationState
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@ -35,7 +36,6 @@ interface UsersRepository {
fun getUserWithId(id: Long): Maybe<User>
fun getUserWithIdNotScheduledForDeletion(id: Long): Maybe<User>
fun getUserWithUserId(userId: String): Maybe<User>
fun getUsersWithoutUserId(id: Long): Single<List<User>>
fun getUsersScheduledForDeletion(): Single<List<User>>
fun getUsersNotScheduledForDeletion(): Single<List<User>>
fun getUserWithUsernameAndServer(username: String, server: String): Maybe<User>
@ -43,4 +43,5 @@ interface UsersRepository {
fun insertUser(user: User): Long
fun setUserAsActiveWithId(id: Long): Single<Boolean>
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
import com.nextcloud.talk.data.user.model.User
import com.nextcloud.talk.models.json.push.PushConfigurationState
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
@ -54,10 +55,6 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
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>> {
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> {
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 {
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;
Participant participant;
for (Object userItem : userManager.getUsers().blockingGet()) {
userEntity = (User) userItem;
for (User userItem : userManager.getUsers().blockingGet()) {
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()) {
String userId;
if (userEntity.getUserId() != null) {

View File

@ -43,6 +43,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito
val currentUser: Maybe<User>
get() {
return userRepository.getActiveUser()
.switchIfEmpty(getAnyUserAndSetAsActive())
}
val currentUserObservable: Observable<User>
@ -58,22 +59,6 @@ class UserManager internal constructor(private val userRepository: UsersReposito
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> {
return userRepository
.getUserWithUsernameAndServer(username, server)
@ -165,7 +150,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito
else -> {
user.token = userAttributes.token
user.baseUrl = userAttributes.serverUrl
user.current = true
user.current = userAttributes.currentUser
user.userId = userAttributes.userId
user.token = userAttributes.token
user.displayName = userAttributes.displayName
@ -246,6 +231,10 @@ class UserManager internal constructor(private val userRepository: UsersReposito
return user
}
fun updatePushState(id: Long, state: PushConfigurationState): Single<Int> {
return userRepository.updatePushState(id, state)
}
companion object {
const val TAG = "UserManager"
}
@ -253,7 +242,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito
data class UserAttributes(
val id: Long?,
val serverUrl: String?,
val currentUser: Boolean?,
val currentUser: Boolean,
val userId: String?,
val token: String?,
val displayName: String?,

View File

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

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.
*/
class CurrentUserProviderImpl @Inject constructor(private val userManager: UserManager) : CurrentUserProviderNew {
private var _currentUser: User? = null
// synchronized to avoid multiple observers initialized from different threads
@get:Synchronized
@set:Synchronized
private var currentUserObserver: Disposable? = null
override val currentUser: Maybe<User>
@ -40,8 +45,10 @@ class CurrentUserProviderImpl @Inject constructor(private val userManager: UserM
// immediately get a result synchronously
_currentUser = userManager.currentUser.blockingGet()
if (currentUserObserver == null) {
// start observable for auto-updates
currentUserObserver = userManager.currentUserObservable.subscribe { _currentUser = it }
currentUserObserver = userManager.currentUserObservable
.subscribe {
_currentUser = it
}
}
}
return _currentUser?.let { Maybe.just(it) } ?: Maybe.empty()

View File

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