Browse Source

move everything to flows

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
Andy Scherzinger 2 years ago
parent
commit
8c27b54377

+ 0 - 3
.idea/codeStyles/Project.xml

@@ -66,9 +66,6 @@
     <MarkdownNavigatorCodeStyleSettings>
       <option name="RIGHT_MARGIN" value="120" />
     </MarkdownNavigatorCodeStyleSettings>
-    <XML>
-      <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
-    </XML>
     <codeStyleSettings language="JAVA">
       <option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
       <option name="ALIGN_MULTILINE_METHOD_BRACKETS" value="true" />

+ 1 - 0
app/build.gradle

@@ -209,6 +209,7 @@ dependencies {
     implementation "androidx.exifinterface:exifinterface:1.3.3"
 
     implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
 
     implementation 'androidx.biometric:biometric:1.1.0'
 

+ 88 - 63
app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt

@@ -117,13 +117,15 @@ class MainActivity : BaseActivity(), ActionBarProvider {
         } else if (!router!!.hasRootController()) {
             if (hasDb) {
                 GlobalScope.launch {
-                    if (usersRepository.getUsers().isNotEmpty()) {
-                        runOnUiThread {
-                            setDefaultRootController()
-                        }
-                    } else {
-                        runOnUiThread {
-                            launchLoginScreen()
+                    usersRepository.getUsers().collect {
+                        if (it.isNotEmpty()) {
+                            runOnUiThread {
+                                setDefaultRootController()
+                            }
+                        } else {
+                            runOnUiThread {
+                                launchLoginScreen()
+                            }
                         }
                     }
                 }
@@ -186,8 +188,14 @@ class MainActivity : BaseActivity(), ActionBarProvider {
     }
 
     fun resetConversationsList() {
-        if (usersRepository.getUsers().isNotEmpty()) {
-            setDefaultRootController()
+        GlobalScope.launch {
+            usersRepository.getUsers().collect {
+                if (it.isNotEmpty()) {
+                    runOnUiThread {
+                        setDefaultRootController()
+                    }
+                }
+            }
         }
     }
 
@@ -226,14 +234,22 @@ class MainActivity : BaseActivity(), ActionBarProvider {
                 "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat" -> {
                     val user = userId.substringBeforeLast("@")
                     val baseUrl = userId.substringAfterLast("@")
-                    if (usersRepository.getActiveUser()?.baseUrl?.endsWith(baseUrl) == true) {
-                        startConversation(user)
-                    } else {
-                        Snackbar.make(
-                            binding.controllerContainer,
-                            R.string.nc_phone_book_integration_account_not_found,
-                            Snackbar.LENGTH_LONG
-                        ).show()
+                    GlobalScope.launch {
+                        usersRepository.getActiveUser().collect {
+                            if (it?.baseUrl?.endsWith(baseUrl) == true) {
+                                runOnUiThread {
+                                    startConversation(user)
+                                }
+                            } else {
+                                runOnUiThread {
+                                    Snackbar.make(
+                                        binding.controllerContainer,
+                                        R.string.nc_phone_book_integration_account_not_found,
+                                        Snackbar.LENGTH_LONG
+                                    ).show()
+                                }
+                            }
+                        }
                     }
                 }
             }
@@ -242,38 +258,19 @@ class MainActivity : BaseActivity(), ActionBarProvider {
 
     private fun startConversation(userId: String) {
         val roomType = "1"
-        val currentUser = usersRepository.getActiveUser() ?: return
 
-        val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1))
-        val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)
-        val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
-            apiVersion, currentUser.baseUrl, roomType,
-            null, userId, null
-        )
-        ncApi.createRoom(
-            credentials,
-            retrofitBucket.url, retrofitBucket.queryMap
-        )
-            .subscribeOn(Schedulers.io())
-            .observeOn(AndroidSchedulers.mainThread())
-            .subscribe(object : Observer<RoomOverall> {
-                override fun onSubscribe(d: Disposable) {
-                    // unused atm
-                }
-                override fun onNext(roomOverall: RoomOverall) {
-                    val bundle = Bundle()
-                    bundle.putParcelable(KEY_USER_ENTITY, currentUser)
-                    bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
-                    bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
-
-                    // FIXME once APIv2 or later is used only, the createRoom already returns all the data
-                    ncApi.getRoom(
+        GlobalScope.launch {
+            usersRepository.getActiveUser().collect { currentUser ->
+                if (currentUser != null) {
+                    val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1))
+                    val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token)
+                    val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom(
+                        apiVersion, currentUser.baseUrl, roomType,
+                        null, userId, null
+                    )
+                    ncApi.createRoom(
                         credentials,
-                        ApiUtils.getUrlForRoom(
-                            apiVersion,
-                            currentUser.baseUrl,
-                            roomOverall.ocs!!.data!!.token
-                        )
+                        retrofitBucket.url, retrofitBucket.queryMap
                     )
                         .subscribeOn(Schedulers.io())
                         .observeOn(AndroidSchedulers.mainThread())
@@ -281,33 +278,61 @@ class MainActivity : BaseActivity(), ActionBarProvider {
                             override fun onSubscribe(d: Disposable) {
                                 // unused atm
                             }
+
                             override fun onNext(roomOverall: RoomOverall) {
-                                bundle.putParcelable(
-                                    KEY_ACTIVE_CONVERSATION,
-                                    Parcels.wrap(roomOverall.ocs!!.data)
-                                )
-                                remapChatController(
-                                    router!!, currentUser.id,
-                                    roomOverall.ocs!!.data!!.token!!, bundle, true
+                                val bundle = Bundle()
+                                bundle.putParcelable(KEY_USER_ENTITY, currentUser)
+                                bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token)
+                                bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId)
+
+                                // FIXME once APIv2 or later is used only, the createRoom already returns all the data
+                                ncApi.getRoom(
+                                    credentials,
+                                    ApiUtils.getUrlForRoom(
+                                        apiVersion,
+                                        currentUser.baseUrl,
+                                        roomOverall.ocs!!.data!!.token
+                                    )
                                 )
+                                    .subscribeOn(Schedulers.io())
+                                    .observeOn(AndroidSchedulers.mainThread())
+                                    .subscribe(object : Observer<RoomOverall> {
+                                        override fun onSubscribe(d: Disposable) {
+                                            // unused atm
+                                        }
+
+                                        override fun onNext(roomOverall: RoomOverall) {
+                                            bundle.putParcelable(
+                                                KEY_ACTIVE_CONVERSATION,
+                                                Parcels.wrap(roomOverall.ocs!!.data)
+                                            )
+                                            remapChatController(
+                                                router!!, currentUser.id,
+                                                roomOverall.ocs!!.data!!.token!!, bundle, true
+                                            )
+                                        }
+
+                                        override fun onError(e: Throwable) {
+                                            // unused atm
+                                        }
+
+                                        override fun onComplete() {
+                                            // unused atm
+                                        }
+                                    })
                             }
 
                             override fun onError(e: Throwable) {
                                 // unused atm
                             }
+
                             override fun onComplete() {
                                 // unused atm
                             }
                         })
                 }
-
-                override fun onError(e: Throwable) {
-                    // unused atm
-                }
-                override fun onComplete() {
-                    // unused atm
-                }
-            })
+            }
+        }
     }
 
     @RequiresApi(api = Build.VERSION_CODES.M)
@@ -359,6 +384,6 @@ class MainActivity : BaseActivity(), ActionBarProvider {
     }
 
     companion object {
-        private val TAG = "MainActivity"
+        private const val TAG = "MainActivity"
     }
 }

+ 31 - 6
app/src/main/java/com/nextcloud/talk/controllers/SettingsController.kt

@@ -53,6 +53,7 @@ import android.widget.Toast
 import androidx.appcompat.app.AlertDialog
 import androidx.core.content.ContextCompat
 import androidx.core.view.ViewCompat
+import androidx.lifecycle.lifecycleScope
 import androidx.work.OneTimeWorkRequest
 import androidx.work.WorkManager
 import autodagger.AutoInjector
@@ -63,6 +64,7 @@ import com.bluelinelabs.conductor.changehandler.VerticalChangeHandler
 import com.google.android.material.textfield.TextInputLayout
 import com.nextcloud.talk.BuildConfig
 import com.nextcloud.talk.R
+import com.nextcloud.talk.activities.MainActivity
 import com.nextcloud.talk.api.NcApi
 import com.nextcloud.talk.application.NextcloudTalkApplication
 import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.setAppTheme
@@ -89,6 +91,7 @@ import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri
 import com.nextcloud.talk.utils.SecurityUtils
 import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ARE_CALL_SOUNDS
 import com.nextcloud.talk.utils.database.user.CapabilitiesNgUtil
+import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
 import com.nextcloud.talk.utils.database.user.UserUtils
 import com.nextcloud.talk.utils.preferences.MagicUserInputModule
 import com.nextcloud.talk.utils.singletons.ApplicationWideMessageHolder
@@ -98,6 +101,9 @@ import io.reactivex.Observer
 import io.reactivex.android.schedulers.AndroidSchedulers
 import io.reactivex.disposables.Disposable
 import io.reactivex.schedulers.Schedulers
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
 import net.orange_box.storebox.listeners.OnPreferenceValueChangedListener
 import okhttp3.MediaType.Companion.toMediaTypeOrNull
 import okhttp3.RequestBody
@@ -120,6 +126,9 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
     @Inject
     lateinit var userRepository: UsersRepository
 
+    @Inject
+    lateinit var currentUserProvider: CurrentUserProviderNew
+
     private var saveStateHandler: LovelySaveStateHandler? = null
     private var currentUser: UserNgEntity? = null
     private var credentials: String? = null
@@ -134,13 +143,19 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
     private var profileQueryDisposable: Disposable? = null
     private var dbQueryDisposable: Disposable? = null
 
+    val scope = MainScope()
+
     override val title: String
         get() =
             resources!!.getString(R.string.nc_settings)
 
     private fun getCurrentUser() {
-        currentUser = userRepository.getActiveUser()
-        credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
+        (activity as MainActivity).lifecycleScope.launchWhenCreated {
+            currentUserProvider.currentUser.collect {
+                currentUser = it
+                credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token)
+            }
+        }
     }
 
     override fun onViewBound(view: View) {
@@ -189,10 +204,18 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
     }
 
     private fun setupPhoneBookIntegration() {
-        if (CapabilitiesNgUtil.isPhoneBookIntegrationAvailable(userRepository.getActiveUser())) {
-            binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
-        } else {
-            binding.settingsPhoneBookIntegration.visibility = View.GONE
+        scope.launch {
+            userRepository.getActiveUser().collect {
+                if (CapabilitiesNgUtil.isPhoneBookIntegrationAvailable(it)) {
+                    activity!!.runOnUiThread {
+                        binding.settingsPhoneBookIntegration.visibility = View.VISIBLE
+                    }
+                } else {
+                    activity!!.runOnUiThread {
+                        binding.settingsPhoneBookIntegration.visibility = View.GONE
+                    }
+                }
+            }
         }
     }
 
@@ -730,6 +753,8 @@ class SettingsController : NewBaseController(R.layout.controller_settings) {
         appPreferences?.unregisterReadPrivacyChangeListener(readPrivacyChangeListener)
         appPreferences?.unregisterPhoneBookIntegrationChangeListener(phoneBookIntegrationChangeListener)
 
+        scope.cancel()
+
         super.onDestroy()
     }
 

+ 48 - 39
app/src/main/java/com/nextcloud/talk/data/user/UsersDao.kt

@@ -22,7 +22,6 @@
 
 package com.nextcloud.talk.data.user
 
-import androidx.lifecycle.LiveData
 import androidx.room.Dao
 import androidx.room.Insert
 import androidx.room.OnConflictStrategy
@@ -30,6 +29,8 @@ import androidx.room.Query
 import androidx.room.Transaction
 import androidx.room.Update
 import com.nextcloud.talk.data.user.model.UserNgEntity
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
 import java.lang.Boolean.FALSE
 import java.lang.Boolean.TRUE
 
@@ -38,16 +39,16 @@ import java.lang.Boolean.TRUE
 abstract class UsersDao {
     // get active user
     @Query("SELECT * FROM User where current = 1")
-    abstract fun getActiveUser(): UserNgEntity?
+    abstract fun getActiveUser(): Flow<UserNgEntity?>
 
     @Query("SELECT * FROM User WHERE current = 1")
-    abstract fun getActiveUserLiveData(): LiveData<UserNgEntity?>
+    abstract fun getActiveUserLiveData(): Flow<UserNgEntity?>
 
     @Query("SELECT * FROM User ORDER BY current DESC")
-    abstract fun getUsersLiveData(): LiveData<List<UserNgEntity>>
+    abstract fun getUsersLiveData(): Flow<List<UserNgEntity>>
 
     @Query("SELECT * FROM User WHERE current != 1 ORDER BY current DESC")
-    abstract fun getUsersLiveDataWithoutActive(): LiveData<List<UserNgEntity>>
+    abstract fun getUsersLiveDataWithoutActive(): Flow<List<UserNgEntity>>
 
     @Query("DELETE FROM User WHERE id = :id")
     abstract suspend fun deleteUserWithId(id: Long)
@@ -56,66 +57,70 @@ abstract class UsersDao {
     abstract suspend fun updateUser(user: UserNgEntity): Int
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
-    abstract fun saveUser(user: UserNgEntity): Long
+    abstract suspend fun saveUser(user: UserNgEntity): Long
 
     @Insert(onConflict = OnConflictStrategy.REPLACE)
     abstract suspend fun saveUsers(vararg users: UserNgEntity): List<Long>
 
     // get all users not scheduled for deletion
     @Query("SELECT * FROM User where current != 0")
-    abstract fun getUsers(): List<UserNgEntity>
+    abstract fun getUsers(): Flow<List<UserNgEntity>>
 
     @Query("SELECT * FROM User where id = :id")
-    abstract fun getUserWithId(id: Long): UserNgEntity?
+    abstract fun getUserWithId(id: Long): Flow<UserNgEntity?>
 
     @Query("SELECT * FROM User where id = :id")
-    abstract fun getUserWithIdLiveData(id: Long): LiveData<UserNgEntity?>
+    abstract fun getUserWithIdLiveData(id: Long): Flow<UserNgEntity?>
 
     @Query("SELECT * FROM User where id = :id AND scheduledForDeletion != 1")
-    abstract fun getUserWithIdNotScheduledForDeletion(id: Long): UserNgEntity?
+    abstract fun getUserWithIdNotScheduledForDeletion(id: Long): Flow<UserNgEntity?>
 
     @Query("SELECT * FROM User where userId = :userId")
-    abstract fun getUserWithUserId(userId: String): UserNgEntity?
+    abstract fun getUserWithUserId(userId: String): Flow<UserNgEntity?>
 
     @Query("SELECT * FROM User where userId != :userId")
-    abstract fun getUsersWithoutUserId(userId: Long): List<UserNgEntity>
+    abstract fun getUsersWithoutUserId(userId: Long): Flow<List<UserNgEntity>>
 
     @Query("SELECT * FROM User where current = 0")
-    abstract fun getUsersScheduledForDeletion(): List<UserNgEntity>
+    abstract fun getUsersScheduledForDeletion(): Flow<List<UserNgEntity>>
 
     @Query("SELECT * FROM User where scheduledForDeletion = 0")
-    abstract fun getUsersNotScheduledForDeletion(): List<UserNgEntity>
+    abstract fun getUsersNotScheduledForDeletion(): Flow<List<UserNgEntity>>
 
     @Query("SELECT * FROM User WHERE username = :username AND baseUrl = :server")
-    abstract suspend fun getUserWithUsernameAndServer(username: String, server: String): UserNgEntity?
+    abstract fun getUserWithUsernameAndServer(username: String, server: String): Flow<UserNgEntity?>
 
     @Transaction
-    open suspend fun setUserAsActiveWithId(id: Long): Boolean {
+    open suspend fun setUserAsActiveWithId(id: Long): Flow<Boolean> {
         val users = getUsers()
-        for (user in users) {
-            // removed from clause: && UserStatus.ACTIVE == user.status
-            if (user.id != id) {
-                user.current = TRUE
-                updateUser(user)
-            } // removed from clause: && UserStatus.ACTIVE != user.status
-            else if (user.id == id) {
-                user.current = TRUE
-                updateUser(user)
+        users.collect {
+            for (user in it) {
+                // removed from clause: && UserStatus.ACTIVE == user.status
+                if (user.id != id) {
+                    user.current = TRUE
+                    updateUser(user)
+                } // removed from clause: && UserStatus.ACTIVE != user.status
+                else if (user.id == id) {
+                    user.current = TRUE
+                    updateUser(user)
+                }
             }
         }
 
-        return true
+        return flowOf(TRUE)
     }
 
     @Transaction
-    open suspend fun markUserForDeletion(id: Long): Boolean {
+    open suspend fun markUserForDeletion(id: Long): Flow<Boolean> {
         val users = getUsers()
-        for (user in users) {
-            if (user.id == id) {
-                // TODO currently we only have a boolean, no intermediate states
-                user.current = FALSE
-                updateUser(user)
-                break
+        users.collect {
+            for (user in it) {
+                if (user.id == id) {
+                    // TODO currently we only have a boolean, no intermediate states
+                    user.current = FALSE
+                    updateUser(user)
+                    break
+                }
             }
         }
 
@@ -123,14 +128,18 @@ abstract class UsersDao {
     }
 
     @Transaction
-    open suspend fun setAnyUserAsActive(): Boolean {
+    open suspend fun setAnyUserAsActive(): Flow<Boolean> {
         val users = getUsers()
-        for (user in users) {
-            user.current = TRUE
-            updateUser(user)
-            return true
+        var result = FALSE
+        users.collect {
+            for (user in it) {
+                user.current = TRUE
+                updateUser(user)
+                result = true
+                break
+            }
         }
 
-        return false
+        return flowOf(result)
     }
 }

+ 17 - 18
app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt

@@ -22,29 +22,28 @@
 
 package com.nextcloud.talk.data.user
 
-import androidx.lifecycle.LiveData
 import com.nextcloud.talk.data.user.model.UserNgEntity
-import com.nextcloud.talk.data.user.model.User
+import kotlinx.coroutines.flow.Flow
 
 @Suppress("TooManyFunctions")
 interface UsersRepository {
-    fun getActiveUserLiveData(): LiveData<UserNgEntity?>
-    fun getActiveUser(): UserNgEntity?
-    fun getUsers(): List<UserNgEntity>
-    fun getUserWithId(id: Long): UserNgEntity?
-    fun getUserWithIdLiveData(id: Long): LiveData<UserNgEntity?>
-    fun getUserWithIdNotScheduledForDeletion(id: Long): UserNgEntity?
-    fun getUserWithUserId(userId: String): UserNgEntity?
-    fun getUsersWithoutUserId(userId: Long): List<UserNgEntity>
-    fun getUsersLiveData(): LiveData<List<User>>
-    fun getUsersLiveDataWithoutActive(): LiveData<List<User>>
-    fun getUsersScheduledForDeletion(): List<UserNgEntity>
-    fun getUsersNotScheduledForDeletion(): List<UserNgEntity>
-    suspend fun getUserWithUsernameAndServer(username: String, server: String): UserNgEntity?
+    fun getActiveUserLiveData(): Flow<UserNgEntity?>
+    fun getActiveUser(): Flow<UserNgEntity?>
+    fun getUsers(): Flow<List<UserNgEntity>>
+    fun getUserWithId(id: Long): Flow<UserNgEntity?>
+    fun getUserWithIdLiveData(id: Long): Flow<UserNgEntity?>
+    fun getUserWithIdNotScheduledForDeletion(id: Long): Flow<UserNgEntity?>
+    fun getUserWithUserId(userId: String): Flow<UserNgEntity?>
+    fun getUsersWithoutUserId(userId: Long): Flow<List<UserNgEntity>>
+    fun getUsersLiveData(): Flow<List<UserNgEntity>>
+    fun getUsersLiveDataWithoutActive(): Flow<List<UserNgEntity>>
+    fun getUsersScheduledForDeletion(): Flow<List<UserNgEntity>>
+    fun getUsersNotScheduledForDeletion(): Flow<List<UserNgEntity>>
+    fun getUserWithUsernameAndServer(username: String, server: String): Flow<UserNgEntity?>
     suspend fun updateUser(user: UserNgEntity): Int
     suspend fun insertUser(user: UserNgEntity): Long
-    suspend fun setUserAsActiveWithId(id: Long): Boolean
+    suspend fun setUserAsActiveWithId(id: Long): Flow<Boolean>
     suspend fun deleteUserWithId(id: Long)
-    suspend fun setAnyUserAsActive(): Boolean
-    suspend fun markUserForDeletion(id: Long): Boolean
+    suspend fun setAnyUserAsActive(): Flow<Boolean>
+    suspend fun markUserForDeletion(id: Long): Flow<Boolean>
 }

+ 20 - 34
app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt

@@ -22,75 +22,61 @@
 
 package com.nextcloud.talk.data.user
 
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.distinctUntilChanged
-import androidx.lifecycle.map
 import com.nextcloud.talk.data.user.model.UserNgEntity
-import com.nextcloud.talk.data.user.model.User
-import com.nextcloud.talk.data.user.model.toUser
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
 
 @Suppress("TooManyFunctions")
 class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
-    override fun getActiveUserLiveData(): LiveData<UserNgEntity?> {
+    override fun getActiveUserLiveData(): Flow<UserNgEntity?> {
         return usersDao.getActiveUserLiveData().distinctUntilChanged()
     }
 
-    override fun getActiveUser(): UserNgEntity? {
+    override fun getActiveUser(): Flow<UserNgEntity?> {
         return usersDao.getActiveUser()
     }
 
-    override fun getUsers(): List<UserNgEntity> {
+    override fun getUsers(): Flow<List<UserNgEntity>> {
         return usersDao.getUsers()
     }
 
-    override fun getUserWithId(id: Long): UserNgEntity? {
+    override fun getUserWithId(id: Long): Flow<UserNgEntity?> {
         return usersDao.getUserWithId(id)
     }
 
-    override fun getUserWithIdLiveData(id: Long): LiveData<UserNgEntity?> {
+    override fun getUserWithIdLiveData(id: Long): Flow<UserNgEntity?> {
         return usersDao.getUserWithIdLiveData(id).distinctUntilChanged()
     }
 
-    override fun getUserWithIdNotScheduledForDeletion(id: Long): UserNgEntity? {
+    override fun getUserWithIdNotScheduledForDeletion(id: Long): Flow<UserNgEntity?> {
         return usersDao.getUserWithIdNotScheduledForDeletion(id)
     }
 
-    override fun getUserWithUserId(userId: String): UserNgEntity? {
+    override fun getUserWithUserId(userId: String): Flow<UserNgEntity?> {
         return usersDao.getUserWithUserId(userId)
     }
 
-    override fun getUsersWithoutUserId(userId: Long): List<UserNgEntity> {
+    override fun getUsersWithoutUserId(userId: Long): Flow<List<UserNgEntity>> {
         return usersDao.getUsersWithoutUserId(userId)
     }
 
-    override fun getUsersLiveData(): LiveData<List<User>> {
-        return usersDao.getUsersLiveData().distinctUntilChanged().map { usersList ->
-            usersList.map {
-                it.toUser()
-            }
-        }
+    override fun getUsersLiveData(): Flow<List<UserNgEntity>> {
+        return usersDao.getUsersLiveData().distinctUntilChanged()
     }
 
-    override fun getUsersLiveDataWithoutActive(): LiveData<List<User>> {
-        return usersDao.getUsersLiveDataWithoutActive().distinctUntilChanged().map { usersList ->
-            usersList.map {
-                it.toUser()
-            }
-        }
+    override fun getUsersLiveDataWithoutActive(): Flow<List<UserNgEntity>> {
+        return usersDao.getUsersLiveDataWithoutActive().distinctUntilChanged()
     }
 
-    override fun getUsersScheduledForDeletion(): List<UserNgEntity> {
+    override fun getUsersScheduledForDeletion(): Flow<List<UserNgEntity>> {
         return usersDao.getUsersScheduledForDeletion()
     }
 
-    override fun getUsersNotScheduledForDeletion(): List<UserNgEntity> {
+    override fun getUsersNotScheduledForDeletion(): Flow<List<UserNgEntity>> {
         return usersDao.getUsersNotScheduledForDeletion()
     }
 
-    override suspend fun getUserWithUsernameAndServer(
-        username: String,
-        server: String
-    ): UserNgEntity? {
+    override fun getUserWithUsernameAndServer(username: String, server: String): Flow<UserNgEntity?> {
         return usersDao.getUserWithUsernameAndServer(username, server)
     }
 
@@ -102,7 +88,7 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
         return usersDao.saveUser(user)
     }
 
-    override suspend fun setUserAsActiveWithId(id: Long): Boolean {
+    override suspend fun setUserAsActiveWithId(id: Long): Flow<Boolean> {
         return usersDao.setUserAsActiveWithId(id)
     }
 
@@ -110,11 +96,11 @@ class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository {
         usersDao.deleteUserWithId(id)
     }
 
-    override suspend fun setAnyUserAsActive(): Boolean {
+    override suspend fun setAnyUserAsActive(): Flow<Boolean> {
         return usersDao.setAnyUserAsActive()
     }
 
-    override suspend fun markUserForDeletion(id: Long): Boolean {
+    override suspend fun markUserForDeletion(id: Long): Flow<Boolean> {
         return usersDao.markUserForDeletion(id)
     }
 }

+ 86 - 42
app/src/main/java/com/nextcloud/talk/users/UserManager.kt

@@ -22,7 +22,6 @@
 package com.nextcloud.talk.users
 
 import android.text.TextUtils
-import androidx.lifecycle.LiveData
 import com.bluelinelabs.logansquare.LoganSquare
 import com.nextcloud.talk.data.user.UsersRepository
 import com.nextcloud.talk.data.user.model.UserNgEntity
@@ -30,35 +29,54 @@ import com.nextcloud.talk.models.ExternalSignalingServer
 import com.nextcloud.talk.models.json.capabilities.Capabilities
 import com.nextcloud.talk.models.json.push.PushConfigurationState
 import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+import java.lang.Boolean.FALSE
 
 @Suppress("TooManyFunctions")
 class UserManager internal constructor(private val userRepository: UsersRepository) : CurrentUserProviderNew {
-    fun anyUserExists(): Boolean {
-        return userRepository.getUsers().isNotEmpty()
+    suspend fun anyUserExists(): Boolean {
+        var result = FALSE
+        userRepository.getUsers().collect {
+            result = it.isNotEmpty()
+        }
+
+        return result
     }
 
-    fun hasMultipleUsers(): Boolean {
-        return userRepository.getUsers().size > 1
+    suspend fun hasMultipleUsers(): Flow<Boolean> {
+        var result = FALSE
+        userRepository.getUsers().collect {
+            result = it.size > 1
+        }
+
+        return flowOf(result)
     }
 
-    val users: List<UserNgEntity>
+    val users: Flow<List<UserNgEntity>>
         get() = userRepository.getUsers()
 
-    val usersScheduledForDeletion: List<UserNgEntity>
+    val usersScheduledForDeletion: Flow<List<UserNgEntity>>
         get() = userRepository.getUsersScheduledForDeletion()
 
-    suspend fun setAnyUserAndSetAsActive(): UserNgEntity? {
+    private suspend fun setAnyUserAndSetAsActive(): Flow<UserNgEntity?> {
         val results = userRepository.getUsersNotScheduledForDeletion()
-        if (results.isNotEmpty()) {
-            val user = results[0]
-            user.current = true
-            userRepository.updateUser(user)
-            return user
+
+        var result: UserNgEntity? = null
+
+        results.collect {
+            if (it.isNotEmpty()) {
+                val user = it[0]
+                user.current = true
+                userRepository.updateUser(user)
+                result = user
+            }
         }
-        return null
+
+        return flowOf(result)
     }
 
-    override val currentUser: UserNgEntity?
+    override val currentUser: Flow<UserNgEntity?>
         get() {
             return userRepository.getActiveUser()
         }
@@ -71,59 +89,84 @@ class UserManager internal constructor(private val userRepository: UsersReposito
         userRepository.deleteUserWithId(internalId)
     }
 
-    fun getUserById(userId: String): UserNgEntity? {
+    fun getUserById(userId: String): Flow<UserNgEntity?> {
         return userRepository.getUserWithUserId(userId)
     }
 
-    fun getUserWithId(id: Long): UserNgEntity? {
+    fun getUserWithId(id: Long): Flow<UserNgEntity?> {
         return userRepository.getUserWithId(id)
     }
 
     suspend fun disableAllUsersWithoutId(userId: Long) {
         val results = userRepository.getUsersWithoutUserId(userId)
-        if (results.isNotEmpty()) {
-            for (entity in results) {
-                entity.current = false
-                userRepository.updateUser(entity)
+
+        results.collect {
+            if (it.isNotEmpty()) {
+                for (entity in it) {
+                    entity.current = false
+                    userRepository.updateUser(entity)
+                }
             }
         }
     }
 
-    suspend fun checkIfUserIsScheduledForDeletion(username: String, server: String): Boolean {
+    suspend fun checkIfUserIsScheduledForDeletion(username: String, server: String): Flow<Boolean> {
         val results = userRepository.getUserWithUsernameAndServer(username, server)
-        return results?.scheduledForDeletion ?: false
+        var result = FALSE
+        results.collect {
+            result = it?.scheduledForDeletion ?: FALSE
+        }
+
+        return flowOf(result)
     }
 
-    fun getUserWithInternalId(id: Long): UserNgEntity? {
+    fun getUserWithInternalId(id: Long): Flow<UserNgEntity?> {
         return userRepository.getUserWithIdNotScheduledForDeletion(id)
     }
 
-    suspend fun getIfUserWithUsernameAndServer(username: String, server: String): Boolean {
-        return userRepository.getUserWithUsernameAndServer(username, server) != null
+    suspend fun getIfUserWithUsernameAndServer(username: String, server: String): Flow<Boolean> {
+        val results = userRepository.getUserWithUsernameAndServer(username, server)
+        var result = FALSE
+        results.collect {
+            result = it != null
+        }
+
+        return flowOf(result)
     }
 
-    suspend fun scheduleUserForDeletionWithId(id: Long): Boolean {
-        val result = userRepository.getUserWithId(id)
+    suspend fun scheduleUserForDeletionWithId(id: Long): Flow<Boolean> {
+        val results = userRepository.getUserWithId(id)
+        var result = FALSE
+
+        results.collect {
+            if (it != null) {
+                it.scheduledForDeletion = true
+                it.current = false
+                userRepository.updateUser(it)
+            }
+        }
 
-        if (result != null) {
-            result.scheduledForDeletion = true
-            result.current = false
-            userRepository.updateUser(result)
+        setAnyUserAndSetAsActive().collect {
+            result = it != null
         }
 
-        return setAnyUserAndSetAsActive() != null
+        return flowOf(result)
     }
 
     suspend fun createOrUpdateUser(
         username: String?,
         userAttributes: UserAttributes,
-    ): LiveData<UserNgEntity?> {
-        var user = if (userAttributes.id == null && username != null && userAttributes.serverUrl != null) {
-            userRepository.getUserWithUsernameAndServer(username, userAttributes.serverUrl)
+    ): Flow<UserNgEntity?> {
+        var user: UserNgEntity? = null
+
+        if (userAttributes.id == null && username != null && userAttributes.serverUrl != null) {
+            userRepository.getUserWithUsernameAndServer(username, userAttributes.serverUrl).collect {
+                user = it
+            }
         } else if (userAttributes.id != null) {
-            userRepository.getUserWithId(userAttributes.id)
-        } else {
-            null
+            userRepository.getUserWithId(userAttributes.id).collect {
+                user = it
+            }
         }
 
         if (user == null) {
@@ -133,12 +176,13 @@ class UserManager internal constructor(private val userRepository: UsersReposito
             )
         } else {
             updateUserData(
-                user,
+                user!!,
                 userAttributes
             )
         }
-        userRepository.insertUser(user)
-        return userRepository.getUserWithIdLiveData(user.id)
+
+        userRepository.insertUser(user!!)
+        return userRepository.getUserWithIdLiveData(user!!.id)
     }
 
     private fun updateUserData(user: UserNgEntity, userAttributes: UserAttributes) {

+ 2 - 1
app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderNew.kt

@@ -21,7 +21,8 @@
 package com.nextcloud.talk.utils.database.user
 
 import com.nextcloud.talk.data.user.model.UserNgEntity
+import kotlinx.coroutines.flow.Flow
 
 interface CurrentUserProviderNew {
-    val currentUser: UserNgEntity?
+    val currentUser: Flow<UserNgEntity?>
 }

+ 10 - 0
app/src/main/java/com/nextcloud/talk/utils/database/user/UserModule.kt

@@ -20,6 +20,8 @@
 package com.nextcloud.talk.utils.database.user
 
 import com.nextcloud.talk.dagger.modules.DatabaseModule
+import com.nextcloud.talk.data.user.UsersRepository
+import com.nextcloud.talk.users.UserManager
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -32,10 +34,18 @@ abstract class UserModule {
     @Binds
     abstract fun bindCurrentUserProvider(userUtils: UserUtils): CurrentUserProvider
 
+    @Binds
+    abstract fun bindCurrentUserProviderNew(userManager: UserManager): CurrentUserProviderNew
+
     companion object {
         @Provides
         fun provideUserUtils(dataStore: ReactiveEntityStore<Persistable?>?): UserUtils {
             return UserUtils(dataStore)
         }
+
+        @Provides
+        fun provideUserManager(userRepository: UsersRepository): UserManager {
+            return UserManager(userRepository)
+        }
     }
 }