Migrate AccountVerificationController from requery to room

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
This commit is contained in:
Andy Scherzinger 2022-07-18 21:45:57 +02:00
parent 8d87d5486b
commit 6e05056b2e
No known key found for this signature in database
GPG Key ID: 6CADC7E3523C308B
5 changed files with 111 additions and 51 deletions

View File

@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.text.TextUtils import android.text.TextUtils
import android.util.Log
import android.view.View import android.view.View
import androidx.work.Data import androidx.work.Data
import androidx.work.OneTimeWorkRequest 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.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.controllers.base.NewBaseController import com.nextcloud.talk.controllers.base.NewBaseController
import com.nextcloud.talk.controllers.util.viewBinding 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.databinding.ControllerAccountVerificationBinding
import com.nextcloud.talk.events.EventStatus import com.nextcloud.talk.events.EventStatus
import com.nextcloud.talk.jobs.CapabilitiesWorker import com.nextcloud.talk.jobs.CapabilitiesWorker
import com.nextcloud.talk.jobs.PushRegistrationWorker import com.nextcloud.talk.jobs.PushRegistrationWorker
import com.nextcloud.talk.jobs.SignalingSettingsWorker 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.capabilities.CapabilitiesOverall
import com.nextcloud.talk.models.json.generic.Status import com.nextcloud.talk.models.json.generic.Status
import com.nextcloud.talk.models.json.userprofile.UserProfileOverall 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.ApiUtils
import com.nextcloud.talk.utils.ClosedInterfaceImpl import com.nextcloud.talk.utils.ClosedInterfaceImpl
import com.nextcloud.talk.utils.UriUtils 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_ORIGINAL_PROTOCOL
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USERNAME 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 com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
import io.reactivex.CompletableObserver import io.reactivex.MaybeObserver
import io.reactivex.Observer import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -68,7 +69,6 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import java.net.CookieManager import java.net.CookieManager
import java.util.ArrayList
import javax.inject.Inject import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class) @AutoInjector(NextcloudTalkApplication::class)
@ -83,7 +83,7 @@ class AccountVerificationController(args: Bundle? = null) :
lateinit var ncApi: NcApi lateinit var ncApi: NcApi
@Inject @Inject
lateinit var userUtils: UserUtils lateinit var userManager: UserManager
@Inject @Inject
lateinit var cookieManager: CookieManager lateinit var cookieManager: CookieManager
@ -248,21 +248,30 @@ class AccountVerificationController(args: Bundle? = null) :
} }
private fun storeProfile(displayName: String?, userId: String) { private fun storeProfile(displayName: String?, userId: String) {
userUtils.createOrUpdateUser( userManager.storeProfile(
username, token, username,
baseUrl, displayName, null, java.lang.Boolean.TRUE, UserManager.UserAttributes(
userId, null, null, id = null,
appPreferences!!.temporaryClientCertAlias, null serverUrl = baseUrl,
currentUser = true,
userId = userId,
token = token,
displayName = displayName,
pushConfigurationState = null,
capabilities = null,
certificateAlias = appPreferences!!.temporaryClientCertAlias,
externalSignalingServer = null
)
) )
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe(object : Observer<UserEntity> { .subscribe(object : MaybeObserver<User> {
override fun onSubscribe(d: Disposable) { override fun onSubscribe(d: Disposable) {
disposables.add(d) disposables.add(d)
} }
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
override fun onNext(userEntity: UserEntity) { override fun onSuccess(user: User) {
internalAccountId = userEntity.id internalAccountId = user.id!!
if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) { if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) {
registerForPush() registerForPush()
} else { } else {
@ -283,7 +292,7 @@ class AccountVerificationController(args: Bundle? = null) :
""" """
${binding.progressText.text} ${binding.progressText.text}
""".trimIndent() + """.trimIndent() +
resources!!.getString(R.string.nc_display_name_not_stored) resources!!.getString(R.string.nc_display_name_not_stored)
abortVerification() abortVerification()
} }
@ -431,10 +440,11 @@ class AccountVerificationController(args: Bundle? = null) :
private fun proceedWithLogin() { private fun proceedWithLogin() {
cookieManager.cookieStore.removeAll() 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) { if (activity != null) {
activity!!.runOnUiThread { activity!!.runOnUiThread {
if (userUtils.users.size == 1) { if (userManager.users.blockingGet().size == 1) {
router.setRoot( router.setRoot(
RouterTransaction.with(ConversationsListController(Bundle())) RouterTransaction.with(ConversationsListController(Bundle()))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
@ -472,18 +482,10 @@ class AccountVerificationController(args: Bundle? = null) :
private fun abortVerification() { private fun abortVerification() {
if (!isAccountImport) { if (!isAccountImport) {
if (internalAccountId != -1L) { if (internalAccountId != -1L) {
userUtils.deleteUserWithId(internalAccountId).subscribe(object : CompletableObserver { val count = userManager.deleteUser(internalAccountId)
override fun onSubscribe(d: Disposable) { if (count > 0) {
// unused atm
}
override fun onComplete() {
activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) } activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) }
} }
override fun onError(e: Throwable) {
// unused atm
}
})
} else { } else {
activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) } activity?.runOnUiThread { Handler().postDelayed({ router.popToRoot() }, DELAY_IN_MILLIS) }
} }
@ -498,7 +500,7 @@ class AccountVerificationController(args: Bundle? = null) :
router.popToRoot() router.popToRoot()
} }
} else { } else {
if (userUtils.anyUserExists()) { if (userManager.users.blockingGet().isNotEmpty()) {
router.setRoot( router.setRoot(
RouterTransaction.with(ConversationsListController(Bundle())) RouterTransaction.with(ConversationsListController(Bundle()))
.pushChangeHandler(HorizontalChangeHandler()) .pushChangeHandler(HorizontalChangeHandler())
@ -518,7 +520,7 @@ class AccountVerificationController(args: Bundle? = null) :
} }
companion object { companion object {
const val TAG = "AccountVerificationController" const val TAG = "AccountVerification"
const val DELAY_IN_MILLIS: Long = 7500 const val DELAY_IN_MILLIS: Long = 7500
} }

View File

@ -24,6 +24,7 @@ package com.nextcloud.talk.data.user
import android.util.Log import android.util.Log
import androidx.room.Dao import androidx.room.Dao
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
@ -45,8 +46,8 @@ abstract class UsersDao {
@Query("SELECT * FROM User where current = 1") @Query("SELECT * FROM User where current = 1")
abstract fun getActiveUserSynchronously(): UserEntity? abstract fun getActiveUserSynchronously(): UserEntity?
@Query("DELETE FROM User WHERE id = :id") @Delete
abstract fun deleteUserWithId(id: Long) abstract fun deleteUser(user: UserEntity): Int
@Update @Update
abstract fun updateUser(user: UserEntity): Int abstract fun updateUser(user: UserEntity): Int

View File

@ -40,7 +40,7 @@ interface UsersRepository {
fun updateUser(user: User): Int fun updateUser(user: User): Int
fun insertUser(user: User): Long fun insertUser(user: User): Long
fun setUserAsActiveWithId(id: Long): Single<Boolean> fun setUserAsActiveWithId(id: Long): Single<Boolean>
fun deleteUserWithId(id: Long) fun deleteUser(user: User): Int
fun setAnyUserAsActive(): Boolean fun setAnyUserAsActive(): Boolean
fun markUserForDeletion(id: Long): Boolean fun markUserForDeletion(id: Long): Boolean
} }

View File

@ -77,8 +77,8 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
return Single.just(usersDao.setUserAsActiveWithId(id)) return Single.just(usersDao.setUserAsActiveWithId(id))
} }
override fun deleteUserWithId(id: Long) { override fun deleteUser(user: User): Int {
usersDao.deleteUserWithId(id) return usersDao.deleteUser(UserMapper.toEntity(user))
} }
override fun setAnyUserAsActive(): Boolean { override fun setAnyUserAsActive(): Boolean {

View File

@ -22,6 +22,7 @@
package com.nextcloud.talk.users package com.nextcloud.talk.users
import android.text.TextUtils import android.text.TextUtils
import android.util.Log
import com.bluelinelabs.logansquare.LoganSquare import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.data.user.UsersRepository import com.nextcloud.talk.data.user.UsersRepository
import com.nextcloud.talk.data.user.model.User 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 com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
import io.reactivex.Maybe import io.reactivex.Maybe
import io.reactivex.Single import io.reactivex.Single
import java.lang.Boolean.FALSE
import java.lang.Boolean.TRUE import java.lang.Boolean.TRUE
@Suppress("TooManyFunctions") @Suppress("TooManyFunctions")
@ -41,9 +43,10 @@ class UserManager internal constructor(private val userRepository: UsersReposito
val usersScheduledForDeletion: Single<List<User>> val usersScheduledForDeletion: Single<List<User>>
get() = userRepository.getUsersScheduledForDeletion() get() = userRepository.getUsersScheduledForDeletion()
private fun setAnyUserAndSetAsActive(): Single<User> { private fun getAnyUserAndSetAsActive(): Single<User> {
val results = userRepository.getUsersNotScheduledForDeletion() val results = userRepository.getUsersNotScheduledForDeletion()
// TODO needs to return Empty in case no user was found (and set active)
return results.map { users -> return results.map { users ->
users users
.firstOrNull() .firstOrNull()
@ -60,12 +63,8 @@ class UserManager internal constructor(private val userRepository: UsersReposito
return userRepository.getActiveUser() return userRepository.getActiveUser()
} }
fun deleteUser(internalId: Long) { fun deleteUser(internalId: Long): Int {
userRepository.deleteUserWithId(internalId) return userRepository.deleteUser(userRepository.getUserWithId(internalId).blockingGet())
}
fun deleteUserWithId(internalId: Long) {
userRepository.deleteUserWithId(internalId)
} }
fun getUserById(userId: String): Maybe<User> { fun getUserById(userId: String): Maybe<User> {
@ -93,7 +92,15 @@ class UserManager internal constructor(private val userRepository: UsersReposito
} }
fun checkIfUserIsScheduledForDeletion(username: String, server: String): Maybe<Boolean> { fun checkIfUserIsScheduledForDeletion(username: String, server: String): Maybe<Boolean> {
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<User> { fun getUserWithInternalId(id: Long): Maybe<User> {
@ -101,10 +108,19 @@ class UserManager internal constructor(private val userRepository: UsersReposito
} }
fun getIfUserWithUsernameAndServer(username: String, server: String): Maybe<Boolean> { fun getIfUserWithUsernameAndServer(username: String, server: String): Maybe<Boolean> {
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<Boolean> { fun scheduleUserForDeletionWithId(id: Long): Single<Boolean> {
// 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 -> return userRepository.getUserWithId(id).map { user ->
user.scheduledForDeletion = true user.scheduledForDeletion = true
user.current = false user.current = false
@ -112,7 +128,7 @@ class UserManager internal constructor(private val userRepository: UsersReposito
} }
.toSingle() .toSingle()
.flatMap { .flatMap {
setAnyUserAndSetAsActive() getAnyUserAndSetAsActive()
}.map { TRUE } }.map { TRUE }
} }
@ -136,19 +152,50 @@ class UserManager internal constructor(private val userRepository: UsersReposito
return userRepository.setUserAsActiveWithId(user.id!!) return userRepository.setUserAsActiveWithId(user.id!!)
} }
fun storeProfile(username: String?, userAttributes: UserAttributes): Maybe<User> {
val userMaybe: Maybe<User> = 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") @Deprecated("Only available for migration, use updateExternalSignalingServer or create new methods")
fun createOrUpdateUser( fun createOrUpdateUser(
username: String?, username: String?,
userAttributes: UserAttributes, userAttributes: UserAttributes,
): Maybe<User> { ): Maybe<User> {
val userMaybe: Maybe<User> = if (userAttributes.id != null) { val userMaybe: Maybe<User> = findUser(username, userAttributes)
userRepository.getUserWithId(userAttributes.id)
} else if (username != null && userAttributes.serverUrl != null) {
userRepository.getUserWithUsernameAndServer(username, userAttributes.serverUrl)
} else {
Maybe.empty()
}
return userMaybe return userMaybe
.map { user: User? -> .map { user: User? ->
@ -175,6 +222,16 @@ class UserManager internal constructor(private val userRepository: UsersReposito
} }
} }
private fun findUser(username: String?, userAttributes: UserAttributes): Maybe<User> {
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<User> { fun getUserWithUsernameAndServer(username: String, server: String): Maybe<User> {
return userRepository.getUserWithUsernameAndServer(username, server) return userRepository.getUserWithUsernameAndServer(username, server)
} }