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 a0cc4cc18..a887748ed 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/AccountVerificationController.kt @@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo import android.os.Bundle import android.os.Handler import android.text.TextUtils +import android.util.Log import android.view.View import androidx.work.Data import androidx.work.OneTimeWorkRequest @@ -39,15 +40,16 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.controllers.base.NewBaseController import com.nextcloud.talk.controllers.util.viewBinding +import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ControllerAccountVerificationBinding import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.jobs.CapabilitiesWorker import com.nextcloud.talk.jobs.PushRegistrationWorker import com.nextcloud.talk.jobs.SignalingSettingsWorker -import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.json.capabilities.CapabilitiesOverall import com.nextcloud.talk.models.json.generic.Status import com.nextcloud.talk.models.json.userprofile.UserProfileOverall +import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.UriUtils @@ -57,9 +59,8 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_IS_ACCOUNT_IMPORT import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ORIGINAL_PROTOCOL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USERNAME -import com.nextcloud.talk.utils.database.user.UserUtils import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder -import io.reactivex.CompletableObserver +import io.reactivex.MaybeObserver import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable @@ -68,7 +69,6 @@ import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import java.net.CookieManager -import java.util.ArrayList import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -83,7 +83,7 @@ class AccountVerificationController(args: Bundle? = null) : lateinit var ncApi: NcApi @Inject - lateinit var userUtils: UserUtils + lateinit var userManager: UserManager @Inject lateinit var cookieManager: CookieManager @@ -248,21 +248,30 @@ class AccountVerificationController(args: Bundle? = null) : } private fun storeProfile(displayName: String?, userId: String) { - userUtils.createOrUpdateUser( - username, token, - baseUrl, displayName, null, java.lang.Boolean.TRUE, - userId, null, null, - appPreferences!!.temporaryClientCertAlias, null + userManager.storeProfile( + username, + UserManager.UserAttributes( + id = null, + serverUrl = baseUrl, + currentUser = true, + userId = userId, + token = token, + displayName = displayName, + pushConfigurationState = null, + capabilities = null, + certificateAlias = appPreferences!!.temporaryClientCertAlias, + externalSignalingServer = null + ) ) .subscribeOn(Schedulers.io()) - .subscribe(object : Observer { + .subscribe(object : MaybeObserver { override fun onSubscribe(d: Disposable) { disposables.add(d) } @SuppressLint("SetTextI18n") - override fun onNext(userEntity: UserEntity) { - internalAccountId = userEntity.id + override fun onSuccess(user: User) { + internalAccountId = user.id!! if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) { registerForPush() } else { @@ -283,7 +292,7 @@ class AccountVerificationController(args: Bundle? = null) : """ ${binding.progressText.text} """.trimIndent() + - resources!!.getString(R.string.nc_display_name_not_stored) + resources!!.getString(R.string.nc_display_name_not_stored) abortVerification() } @@ -431,10 +440,11 @@ class AccountVerificationController(args: Bundle? = null) : private fun proceedWithLogin() { cookieManager.cookieStore.removeAll() - userUtils.disableAllUsersWithoutId(internalAccountId) + val userDisabledCount = userManager.disableAllUsersWithoutId(internalAccountId).blockingGet() + Log.d(TAG, "Disabled $userDisabledCount users that had no id") if (activity != null) { activity!!.runOnUiThread { - if (userUtils.users.size == 1) { + if (userManager.users.blockingGet().size == 1) { router.setRoot( RouterTransaction.with(ConversationsListController(Bundle())) .pushChangeHandler(HorizontalChangeHandler()) @@ -472,18 +482,10 @@ class AccountVerificationController(args: Bundle? = null) : private fun abortVerification() { if (!isAccountImport) { if (internalAccountId != -1L) { - userUtils.deleteUserWithId(internalAccountId).subscribe(object : CompletableObserver { - override fun onSubscribe(d: Disposable) { - // unused atm - } - override fun onComplete() { + val count = userManager.deleteUser(internalAccountId) + if (count > 0) { activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) } - } - - override fun onError(e: Throwable) { - // unused atm - } - }) + } } else { activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) } } @@ -498,7 +500,7 @@ class AccountVerificationController(args: Bundle? = null) : router.popToRoot() } } else { - if (userUtils.anyUserExists()) { + if (userManager.users.blockingGet().isNotEmpty()) { router.setRoot( RouterTransaction.with(ConversationsListController(Bundle())) .pushChangeHandler(HorizontalChangeHandler()) @@ -518,7 +520,7 @@ class AccountVerificationController(args: Bundle? = null) : } companion object { - const val TAG = "AccountVerificationController" + const val TAG = "AccountVerification" const val DELAY_IN_MILLIS: Long = 7500 } 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 e1826f5ce..1bb2e99cb 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 @@ -24,6 +24,7 @@ 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 @@ -45,8 +46,8 @@ abstract class UsersDao { @Query("SELECT * FROM User where current = 1") abstract fun getActiveUserSynchronously(): UserEntity? - @Query("DELETE FROM User WHERE id = :id") - abstract fun deleteUserWithId(id: Long) + @Delete + abstract fun deleteUser(user: UserEntity): Int @Update abstract fun updateUser(user: UserEntity): Int 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 afe903527..c3d4cab2b 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 @@ -40,7 +40,7 @@ interface UsersRepository { fun updateUser(user: User): Int fun insertUser(user: User): Long fun setUserAsActiveWithId(id: Long): Single - fun deleteUserWithId(id: Long) + fun deleteUser(user: User): Int fun setAnyUserAsActive(): Boolean fun markUserForDeletion(id: Long): Boolean } 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 07957cda2..6d564eee7 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 @@ -77,8 +77,8 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository { return Single.just(usersDao.setUserAsActiveWithId(id)) } - override fun deleteUserWithId(id: Long) { - usersDao.deleteUserWithId(id) + override fun deleteUser(user: User): Int { + return usersDao.deleteUser(UserMapper.toEntity(user)) } override fun setAnyUserAsActive(): Boolean { 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 2262da0ac..7d73c8a9c 100644 --- a/app/src/main/java/com/nextcloud/talk/users/UserManager.kt +++ b/app/src/main/java/com/nextcloud/talk/users/UserManager.kt @@ -22,6 +22,7 @@ package com.nextcloud.talk.users import android.text.TextUtils +import android.util.Log import com.bluelinelabs.logansquare.LoganSquare import com.nextcloud.talk.data.user.UsersRepository import com.nextcloud.talk.data.user.model.User @@ -31,6 +32,7 @@ import com.nextcloud.talk.models.json.push.PushConfigurationState import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import io.reactivex.Maybe import io.reactivex.Single +import java.lang.Boolean.FALSE import java.lang.Boolean.TRUE @Suppress("TooManyFunctions") @@ -41,9 +43,10 @@ class UserManager internal constructor(private val userRepository: UsersReposito val usersScheduledForDeletion: Single> get() = userRepository.getUsersScheduledForDeletion() - private fun setAnyUserAndSetAsActive(): Single { + private fun getAnyUserAndSetAsActive(): Single { val results = userRepository.getUsersNotScheduledForDeletion() + // TODO needs to return Empty in case no user was found (and set active) return results.map { users -> users .firstOrNull() @@ -60,12 +63,8 @@ class UserManager internal constructor(private val userRepository: UsersReposito return userRepository.getActiveUser() } - fun deleteUser(internalId: Long) { - userRepository.deleteUserWithId(internalId) - } - - fun deleteUserWithId(internalId: Long) { - userRepository.deleteUserWithId(internalId) + fun deleteUser(internalId: Long): Int { + return userRepository.deleteUser(userRepository.getUserWithId(internalId).blockingGet()) } fun getUserById(userId: String): Maybe { @@ -93,7 +92,15 @@ class UserManager internal constructor(private val userRepository: UsersReposito } fun checkIfUserIsScheduledForDeletion(username: String, server: String): Maybe { - return userRepository.getUserWithUsernameAndServer(username, server).map { it.scheduledForDeletion } + return userRepository + .getUserWithUsernameAndServer(username, server) + .switchIfEmpty(Maybe.empty()) + .map { user: User? -> + when (user) { + null -> FALSE + else -> user.scheduledForDeletion + } + } } fun getUserWithInternalId(id: Long): Maybe { @@ -101,10 +108,19 @@ class UserManager internal constructor(private val userRepository: UsersReposito } fun getIfUserWithUsernameAndServer(username: String, server: String): Maybe { - return userRepository.getUserWithUsernameAndServer(username, server).map { TRUE } + return userRepository + .getUserWithUsernameAndServer(username, server) + .map { user: User? -> + when (user) { + null -> FALSE + else -> TRUE + } + } } fun scheduleUserForDeletionWithId(id: Long): Single { + // TODO needs to return false in case getAnyUserAndSetAsActive doesn't return a user + // or getUserWithId(id) doesn't return a user return userRepository.getUserWithId(id).map { user -> user.scheduledForDeletion = true user.current = false @@ -112,7 +128,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito } .toSingle() .flatMap { - setAnyUserAndSetAsActive() + getAnyUserAndSetAsActive() }.map { TRUE } } @@ -136,19 +152,50 @@ class UserManager internal constructor(private val userRepository: UsersReposito return userRepository.setUserAsActiveWithId(user.id!!) } + fun storeProfile(username: String?, userAttributes: UserAttributes): Maybe { + val userMaybe: Maybe = findUser(null, userAttributes) + + return userMaybe + .map { user: User? -> + when (user) { + null -> createUser( + username, + userAttributes + ) + else -> { + user.token = userAttributes.token + user.baseUrl = userAttributes.serverUrl + user.current = true + user.userId = userAttributes.userId + user.token = userAttributes.token + user.displayName = userAttributes.displayName + user.clientCertificate = userAttributes.certificateAlias + + updateUserData( + user, + userAttributes + ) + + user + } + } + } + .switchIfEmpty(Maybe.just(createUser(username, userAttributes))) + .map { user -> + userRepository.insertUser(user) + } + .flatMap { id -> + userRepository.getUserWithId(id) + } + } + @Deprecated("Only available for migration, use updateExternalSignalingServer or create new methods") fun createOrUpdateUser( username: String?, userAttributes: UserAttributes, ): Maybe { - val userMaybe: Maybe = if (userAttributes.id != null) { - userRepository.getUserWithId(userAttributes.id) - } else if (username != null && userAttributes.serverUrl != null) { - userRepository.getUserWithUsernameAndServer(username, userAttributes.serverUrl) - } else { - Maybe.empty() - } + val userMaybe: Maybe = findUser(username, userAttributes) return userMaybe .map { user: User? -> @@ -175,6 +222,16 @@ class UserManager internal constructor(private val userRepository: UsersReposito } } + private fun findUser(username: String?, userAttributes: UserAttributes): Maybe { + return if (userAttributes.id != null) { + userRepository.getUserWithId(userAttributes.id) + } else if (username != null && userAttributes.serverUrl != null) { + userRepository.getUserWithUsernameAndServer(username, userAttributes.serverUrl) + } else { + Maybe.empty() + } + } + fun getUserWithUsernameAndServer(username: String, server: String): Maybe { return userRepository.getUserWithUsernameAndServer(username, server) }