diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java index 1093934dc..497899e31 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.java @@ -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(); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt index 36b50f983..984d6131f 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt @@ -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() } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt index 12e3e492d..3674200d9 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ConversationsListController.kt @@ -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() } } diff --git a/app/src/main/java/com/nextcloud/talk/data/user/UsersDao.kt b/app/src/main/java/com/nextcloud/talk/data/user/UsersDao.kt index dc0cd86cc..565d980f1 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/UsersDao.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/UsersDao.kt @@ -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 - @Query("SELECT * FROM User where id != :id") - abstract fun getUsersWithoutId(id: Long): Single> - @Query("SELECT * FROM User where scheduledForDeletion = 1") abstract fun getUsersScheduledForDeletion(): Single> @@ -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 - @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 companion object { const val TAG = "UsersDao" diff --git a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt index 4d15a97b8..e8470b59f 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt @@ -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 fun getUserWithIdNotScheduledForDeletion(id: Long): Maybe fun getUserWithUserId(userId: String): Maybe - fun getUsersWithoutUserId(id: Long): Single> fun getUsersScheduledForDeletion(): Single> fun getUsersNotScheduledForDeletion(): Single> fun getUserWithUsernameAndServer(username: String, server: String): Maybe @@ -43,4 +43,5 @@ interface UsersRepository { fun insertUser(user: User): Long fun setUserAsActiveWithId(id: Long): Single fun deleteUser(user: User): Int + fun updatePushState(id: Long, state: PushConfigurationState): Single } diff --git a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt index d88d2147a..d6723d7ee 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt @@ -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> { - return usersDao.getUsersWithoutId(id).map { UserMapper.toModel(it) } - } - override fun getUsersScheduledForDeletion(): Single> { return usersDao.getUsersScheduledForDeletion().map { UserMapper.toModel(it) } } @@ -79,10 +76,19 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository { } override fun setUserAsActiveWithId(id: Long): Single { - 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 { + return usersDao.updatePushState(id, state) + } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java index d75c99403..6007db129 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountDialogFragment.java @@ -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) { diff --git a/app/src/main/java/com/nextcloud/talk/users/UserManager.kt b/app/src/main/java/com/nextcloud/talk/users/UserManager.kt index a61894bfa..378cb8112 100644 --- a/app/src/main/java/com/nextcloud/talk/users/UserManager.kt +++ b/app/src/main/java/com/nextcloud/talk/users/UserManager.kt @@ -43,6 +43,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito val currentUser: Maybe get() { return userRepository.getActiveUser() + .switchIfEmpty(getAnyUserAndSetAsActive()) } val currentUserObservable: Observable @@ -58,22 +59,6 @@ class UserManager internal constructor(private val userRepository: UsersReposito return userRepository.getUserWithId(id) } - fun disableAllUsersWithoutId(id: Long): Single { - 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 { 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 { + 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?, diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java index 70b2179a1..2890acfea 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.java @@ -235,17 +235,17 @@ public class PushUtils { List users = userManager.getUsers().blockingGet(); - for (User user : users) { - if (!user.getScheduledForDeletion()) { - Map 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 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() { @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 proxyMap, User user) throws IOException { + private void updatePushStateForUser(Map 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() { - @Override - public void onSubscribe(Disposable d) { - // unused atm - } + if (user.getId() != null) { + userManager.updatePushState(user.getId(), pushConfigurationState).subscribe(new SingleObserver() { + @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) { diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt b/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt index 57848ff47..21aaa8875 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt @@ -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 @@ -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() diff --git a/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java b/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java index 14a13903d..6e1748c0b 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ssl/MagicKeyManager.java @@ -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;