From 7430accdcb96c39bd38c1d0dc7d8847e2ba8555e Mon Sep 17 00:00:00 2001 From: joragua Date: Wed, 11 Dec 2024 10:32:24 +0100 Subject: [PATCH 1/9] feat: added a new use case to fetch the user quota as Flow (`GetStoredQuotaAsStreamUseCase`) --- .../dependecyinjection/UseCaseModule.kt | 3 + .../presentation/common/DrawerViewModel.kt | 32 +-- .../android/ui/activity/DrawerActivity.kt | 210 +++++++++--------- .../user/datasources/LocalUserDataSource.kt | 8 +- .../implementation/OCLocalUserDataSource.kt | 3 + .../owncloud/android/data/user/db/UserDao.kt | 9 +- .../data/user/repository/OCUserRepository.kt | 6 +- .../android/domain/user/UserRepository.kt | 4 +- .../usecases/GetStoredQuotaAsStreamUseCase.kt | 36 +++ 9 files changed, 172 insertions(+), 139 deletions(-) create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 1ff523b3797..6ba70e60e17 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -5,6 +5,7 @@ * @author Abel García de Prada * @author Juan Carlos Garrote Gascón * @author Aitor Ballesteros Pavón + * @author Jorge Aguado Recio * * Copyright (C) 2024 ownCloud GmbH. * @@ -101,6 +102,7 @@ import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsStreamUse import com.owncloud.android.domain.transfers.usecases.GetAllTransfersUseCase import com.owncloud.android.domain.transfers.usecases.UpdatePendingUploadsPathUseCase import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase +import com.owncloud.android.domain.user.usecases.GetStoredQuotaAsStreamUseCase import com.owncloud.android.domain.user.usecases.GetUserAvatarAsyncUseCase import com.owncloud.android.domain.user.usecases.GetUserInfoAsyncUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase @@ -252,6 +254,7 @@ val useCaseModule = module { factoryOf(::UploadFilesFromSystemUseCase) // User + factoryOf(::GetStoredQuotaAsStreamUseCase) factoryOf(::GetStoredQuotaUseCase) factoryOf(::GetUserAvatarAsyncUseCase) factoryOf(::GetUserInfoAsyncUseCase) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt index 01ba4de8ce8..ba4b70fbe53 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt @@ -25,65 +25,43 @@ package com.owncloud.android.presentation.common import android.accounts.Account import android.content.Context -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.owncloud.android.R import com.owncloud.android.data.providers.LocalStorageProvider import com.owncloud.android.domain.user.model.UserQuota -import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase +import com.owncloud.android.domain.user.usecases.GetStoredQuotaAsStreamUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase -import com.owncloud.android.domain.utils.Event -import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult import com.owncloud.android.presentation.authentication.AccountUtils import com.owncloud.android.providers.ContextProvider import com.owncloud.android.providers.CoroutinesDispatcherProvider import com.owncloud.android.usecases.accounts.RemoveAccountUseCase +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch import timber.log.Timber class DrawerViewModel( - private val getStoredQuotaUseCase: GetStoredQuotaUseCase, + getStoredQuotaAsStreamUseCase: GetStoredQuotaAsStreamUseCase, private val removeAccountUseCase: RemoveAccountUseCase, private val getUserQuotasUseCase: GetUserQuotasUseCase, private val localStorageProvider: LocalStorageProvider, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider, private val contextProvider: ContextProvider, + accountName: String, ) : ViewModel() { - private val _userQuota = MediatorLiveData>>() - val userQuota: LiveData>> = _userQuota - - fun getStoredQuota( - accountName: String - ) = runUseCaseWithResult( - coroutineDispatcher = coroutinesDispatcherProvider.io, - requiresConnection = false, - showLoading = true, - liveData = _userQuota, - useCase = getStoredQuotaUseCase, - useCaseParams = GetStoredQuotaUseCase.Params(accountName = accountName) - ) + val userQuota: Flow = getStoredQuotaAsStreamUseCase(GetStoredQuotaAsStreamUseCase.Params(accountName)) fun getAccounts(context: Context): List { return AccountUtils.getAccounts(context).asList() } - fun getCurrentAccount(context: Context): Account? { - return AccountUtils.getCurrentOwnCloudAccount(context) - } - fun getUsernameOfAccount(accountName: String): String { return AccountUtils.getUsernameOfAccount(accountName) } fun getFeedbackMail() = contextProvider.getString(R.string.mail_feedback) - fun setCurrentAccount(context: Context, accountName: String): Boolean { - return AccountUtils.setCurrentOwnCloudAccount(context, accountName) - } - fun removeAccount(context: Context) { viewModelScope.launch(coroutinesDispatcherProvider.io) { val loggedAccounts = AccountUtils.getAccounts(context) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt index 10a17618690..801d7be0660 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt @@ -59,6 +59,7 @@ import com.owncloud.android.domain.capabilities.model.OCCapability import com.owncloud.android.domain.files.model.FileListOption import com.owncloud.android.domain.user.model.UserQuotaState import com.owncloud.android.domain.utils.Event +import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.goToUrl import com.owncloud.android.extensions.openPrivacyPolicy import com.owncloud.android.extensions.sendEmailOrOpenFeedbackDialogAction @@ -82,7 +83,11 @@ import timber.log.Timber */ abstract class DrawerActivity : ToolbarActivity() { - private val drawerViewModel by viewModel() + private val drawerViewModel by viewModel { + parametersOf( + account?.name + ) + } private val capabilitiesViewModel by viewModel { parametersOf( account?.name @@ -287,7 +292,6 @@ abstract class DrawerActivity : ToolbarActivity() { open fun openDrawer() { getDrawerLayout()?.openDrawer(GravityCompat.START) findViewById(R.id.nav_view).requestFocus() - drawerViewModel.getStoredQuota(account.name) } /** @@ -305,115 +309,105 @@ abstract class DrawerActivity : ToolbarActivity() { */ private fun updateQuota() { Timber.d("Update Quota") - val account = drawerViewModel.getCurrentAccount(this) ?: return - drawerViewModel.getStoredQuota(account.name) - drawerViewModel.userQuota.observe(this) { event -> - when (val uiResult = event.peekContent()) { - is UIResult.Success -> { - uiResult.data?.let { userQuota -> - when { - userQuota.available == -4L -> { // Light users (oCIS) - getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) - getAccountQuotaBar()?.isVisible = false - getAccountQuotaStatusText()?.isVisible = false - } - - userQuota.available < 0 -> { // Pending, unknown or unlimited free storage - getAccountQuotaBar()?.apply { - isVisible = true - progress = 0 - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.text = String.format( - getString(R.string.drawer_unavailable_free_storage), - DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) - ) - getAccountQuotaStatusText()?.visibility = View.GONE - } - - userQuota.available == 0L -> { // Exceeded storage. The value is over 100%. - getAccountQuotaBar()?.apply { - isVisible = true - progress = 100 - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) - } - - if (userQuota.state == UserQuotaState.EXCEEDED) { // oCIS - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_exceeded_quota) - } - } else { // oC10 - getAccountQuotaText()?.text = getString(R.string.drawer_exceeded_quota) - getAccountQuotaStatusText()?.visibility = View.GONE - } - } - - else -> { // Limited storage. Value under 100% - if (userQuota.state == UserQuotaState.NEARING) { // Nearing storage. Value between 75% and 90% - getAccountQuotaBar()?.apply { - isVisible = true - progress = userQuota.getRelative().toInt() - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_nearing_quota) - } - } else if (userQuota.state == UserQuotaState.CRITICAL || userQuota.state == UserQuotaState.EXCEEDED) { // Critical storage. Value over 90% - getAccountQuotaBar()?.apply { - isVisible = true - progress = userQuota.getRelative().toInt() - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) - } - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_critical_quota) - } - } else { // Normal storage. Value under 75% - getAccountQuotaBar()?.apply { - progress = userQuota.getRelative().toInt() - isVisible = true - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, this, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), this, true), - userQuota.getRelative() - ) - getAccountQuotaStatusText()?.visibility = View.GONE - } - } + collectLatestLifecycleFlow(drawerViewModel.userQuota) { userQuota -> + when { + userQuota.available == -4L -> { // Light users (oCIS) + getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) + getAccountQuotaBar()?.isVisible = false + getAccountQuotaStatusText()?.isVisible = false + } + + userQuota.available < 0 -> { // Pending, unknown or unlimited free storage + getAccountQuotaBar()?.apply { + isVisible = true + progress = 0 + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + } + getAccountQuotaText()?.text = String.format( + getString(R.string.drawer_unavailable_free_storage), + DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) + ) + getAccountQuotaStatusText()?.visibility = View.GONE + } + + userQuota.available == 0L -> { // Exceeded storage. The value is over 100%. + getAccountQuotaBar()?.apply { + isVisible = true + progress = 100 + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) + } + + if (userQuota.state == UserQuotaState.EXCEEDED) { // oCIS + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) + } + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_exceeded_quota) + } + } else { // oC10 + getAccountQuotaText()?.text = getString(R.string.drawer_exceeded_quota) + getAccountQuotaStatusText()?.visibility = View.GONE + } + } + + else -> { // Limited storage. Value under 100% + if (userQuota.state == UserQuotaState.NEARING) { // Nearing storage. Value between 75% and 90% + getAccountQuotaBar()?.apply { + isVisible = true + progress = userQuota.getRelative().toInt() + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + } + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) + } + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_nearing_quota) + } + } else if (userQuota.state == UserQuotaState.CRITICAL || userQuota.state == UserQuotaState.EXCEEDED) { // Critical storage. Value over 90% + getAccountQuotaBar()?.apply { + isVisible = true + progress = userQuota.getRelative().toInt() + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) + } + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) + } + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_critical_quota) + } + } else { // Normal storage. Value under 75% + getAccountQuotaBar()?.apply { + progress = userQuota.getRelative().toInt() + isVisible = true + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) } + getAccountQuotaText()?.text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, this, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), this, true), + userQuota.getRelative() + ) + getAccountQuotaStatusText()?.visibility = View.GONE } } - is UIResult.Loading -> getAccountQuotaText()?.text = getString(R.string.drawer_loading_quota) - is UIResult.Error -> getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) } } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt index 9c6dca9187f..d90aec4eccf 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. + * @author Jorge Aguado Recio + * + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -32,6 +34,10 @@ interface LocalUserDataSource { accountName: String ): UserQuota? + fun getQuotaForAccountAsFlow( + accountName: String + ): Flow + fun getAllUserQuotas(): List fun getAllUserQuotasAsFlow(): Flow> diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt index 0cca77b3f87..97dd5bb0b67 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt @@ -41,6 +41,9 @@ class OCLocalUserDataSource( override fun getQuotaForAccount(accountName: String): UserQuota? = userDao.getQuotaForAccount(accountName = accountName)?.toModel() + override fun getQuotaForAccountAsFlow(accountName: String): Flow = + userDao.getQuotaForAccountAsFlow(accountName = accountName).map { it.toModel() } + override fun getAllUserQuotas(): List { return userDao.getAllUserQuotas().map { userQuotaEntity -> userQuotaEntity.toModel() diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt index 5e78bca38e7..55527a944d2 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. + * @author Jorge Aguado Recio + * + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -33,6 +35,11 @@ interface UserDao { accountName: String ): UserQuotaEntity? + @Query(SELECT_QUOTA) + fun getQuotaForAccountAsFlow( + accountName: String + ): Flow + @Query(SELECT_ALL_QUOTAS) fun getAllUserQuotas(): List diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt index 0cb97b6cc06..2d095b8d27b 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt @@ -3,8 +3,9 @@ * * @author Abel García de Prada * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2022 ownCloud GmbH. + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -42,6 +43,9 @@ class OCUserRepository( override fun getStoredUserQuota(accountName: String): UserQuota? = localUserDataSource.getQuotaForAccount(accountName) + override fun getStoredUserQuotaAsFlow(accountName: String): Flow = + localUserDataSource.getQuotaForAccountAsFlow(accountName) + override fun getAllUserQuotas(): List = localUserDataSource.getAllUserQuotas() diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt index 3c7ddf48278..5e96a829e0d 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt @@ -3,8 +3,9 @@ * * @author Abel García de Prada * @author Juan Carlos Garrote Gascón + * @author Jorge Aguado Recio * - * Copyright (C) 2022 ownCloud GmbH. + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -30,6 +31,7 @@ interface UserRepository { fun getUserInfo(accountName: String): UserInfo fun getUserQuota(accountName: String): UserQuota fun getStoredUserQuota(accountName: String): UserQuota? + fun getStoredUserQuotaAsFlow(accountName: String): Flow fun getAllUserQuotas(): List fun getAllUserQuotasAsFlow(): Flow> fun getUserAvatar(accountName: String): UserAvatar diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt new file mode 100644 index 00000000000..01f766923d6 --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt @@ -0,0 +1,36 @@ +/** + * ownCloud Android client application + * + * @author Jorge Aguado Recio + * + * Copyright (C) 2024 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.owncloud.android.domain.user.usecases + +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.user.UserRepository +import com.owncloud.android.domain.user.model.UserQuota +import kotlinx.coroutines.flow.Flow + +class GetStoredQuotaAsStreamUseCase( + private val userRepository: UserRepository +) : BaseUseCase, GetStoredQuotaAsStreamUseCase.Params>() { + + override fun run(params: Params): Flow = + userRepository.getStoredUserQuotaAsFlow(params.accountName) + + data class Params(val accountName: String) +} From 7bc8754e3b0582568a8d85b5c751b1ac91b0cc2e Mon Sep 17 00:00:00 2001 From: joragua Date: Wed, 11 Dec 2024 10:38:14 +0100 Subject: [PATCH 2/9] test: added some necessary tests from `OCLocalUserDataSourceTest` and remove unnecessary ones from `DrawerViewModelTest` --- .../viewmodels/DrawerViewModelTest.kt | 101 +++++++----------- .../OCLocalUserDataSourceTest.kt | 41 ++++++- 2 files changed, 73 insertions(+), 69 deletions(-) diff --git a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt b/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt index f199ef6fab7..353cad8e0de 100644 --- a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt +++ b/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt @@ -21,16 +21,11 @@ package com.owncloud.android.presentation.viewmodels import com.owncloud.android.data.providers.LocalStorageProvider -import com.owncloud.android.domain.UseCaseResult -import com.owncloud.android.domain.user.model.UserQuota -import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase +import com.owncloud.android.domain.user.usecases.GetStoredQuotaAsStreamUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase -import com.owncloud.android.domain.utils.Event import com.owncloud.android.presentation.common.DrawerViewModel -import com.owncloud.android.presentation.common.UIResult import com.owncloud.android.providers.ContextProvider import com.owncloud.android.testutil.OC_ACCOUNT_NAME -import com.owncloud.android.testutil.OC_USER_QUOTA import com.owncloud.android.usecases.accounts.RemoveAccountUseCase import io.mockk.every import io.mockk.mockk @@ -39,7 +34,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.setMain import org.junit.After import org.junit.Before -import org.junit.Test import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.dsl.module @@ -47,7 +41,7 @@ import org.koin.dsl.module @ExperimentalCoroutinesApi class DrawerViewModelTest : ViewModelTest() { private lateinit var drawerViewModel: DrawerViewModel - private lateinit var getStoredQuotaUseCase: GetStoredQuotaUseCase + private lateinit var getStoredQuotaAsStreamUseCase: GetStoredQuotaAsStreamUseCase private lateinit var removeAccountUseCase: RemoveAccountUseCase private lateinit var getUserQuotasUseCase: GetUserQuotasUseCase private lateinit var localStorageProvider: LocalStorageProvider @@ -56,68 +50,45 @@ class DrawerViewModelTest : ViewModelTest() { private val commonException = Exception() - @Before - fun setUp() { - contextProvider = mockk() + @Before + fun setUp() { + contextProvider = mockk() - every { contextProvider.isConnected() } returns true + every { contextProvider.isConnected() } returns true - Dispatchers.setMain(testCoroutineDispatcher) - startKoin { - allowOverride(override = true) - modules( - module { - factory { - contextProvider - } - }) - } + Dispatchers.setMain(testCoroutineDispatcher) + startKoin { + allowOverride(override = true) + modules( + module { + factory { + contextProvider + } + }) + } - getStoredQuotaUseCase = mockk() - removeAccountUseCase = mockk() - getUserQuotasUseCase = mockk() - localStorageProvider = mockk() + getStoredQuotaAsStreamUseCase = mockk() + removeAccountUseCase = mockk() + getUserQuotasUseCase = mockk() + localStorageProvider = mockk() - testCoroutineDispatcher.pauseDispatcher() + testCoroutineDispatcher.pauseDispatcher() - drawerViewModel = DrawerViewModel( - getStoredQuotaUseCase = getStoredQuotaUseCase, - removeAccountUseCase = removeAccountUseCase, - getUserQuotasUseCase = getUserQuotasUseCase, - localStorageProvider = localStorageProvider, - coroutinesDispatcherProvider = coroutineDispatcherProvider, - contextProvider = contextProvider, - ) - } + drawerViewModel = DrawerViewModel( + getStoredQuotaAsStreamUseCase = getStoredQuotaAsStreamUseCase, + removeAccountUseCase = removeAccountUseCase, + getUserQuotasUseCase = getUserQuotasUseCase, + localStorageProvider = localStorageProvider, + coroutinesDispatcherProvider = coroutineDispatcherProvider, + contextProvider = contextProvider, + accountName = OC_ACCOUNT_NAME + ) + } - @After - override fun tearDown() { - super.tearDown() - stopKoin() - } + @After + override fun tearDown() { + super.tearDown() + stopKoin() + } - @Test - fun getStoredQuotaOk() { - every { getStoredQuotaUseCase(any()) } returns UseCaseResult.Success(OC_USER_QUOTA) - drawerViewModel.getStoredQuota(OC_ACCOUNT_NAME) - - assertEmittedValues( - expectedValues = listOf>>( - Event(UIResult.Loading()), Event(UIResult.Success(OC_USER_QUOTA)) - ), - liveData = drawerViewModel.userQuota - ) - } - - @Test - fun getStoredQuotaException() { - every { getStoredQuotaUseCase(any()) } returns UseCaseResult.Error(commonException) - drawerViewModel.getStoredQuota(OC_ACCOUNT_NAME) - - assertEmittedValues( - expectedValues = listOf>> - (Event(UIResult.Loading()), Event(UIResult.Error(commonException))), - liveData = drawerViewModel.userQuota - ) - } } diff --git a/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt index b5e62e38412..5ed8d66bc49 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt @@ -3,8 +3,9 @@ * * @author Abel García de Prada * @author Aitor Ballesteros Pavón + * @author Jorge Aguado Recio * - * Copyright (C) 2023 ownCloud GmbH. + * Copyright (C) 2024 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -29,6 +30,9 @@ import com.owncloud.android.testutil.OC_USER_QUOTA import io.mockk.every import io.mockk.mockk import io.mockk.verify +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Before @@ -82,12 +86,16 @@ class OCLocalUserDataSourceTest { } @Test - fun `deleteQuotaForAccount removes user quota correctly`() { + fun `getQuotaForAccountAsFlow returns a Flow with an UserQuota`() = runTest { + every { + ocUserQuotaDao.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME) + } returns flowOf(userQuotaEntity) - ocLocalUserDataSource.deleteQuotaForAccount(OC_ACCOUNT_NAME) + val userQuota = ocLocalUserDataSource.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME).first() + assertEquals(userQuotaEntity.toModel(), userQuota) verify(exactly = 1) { - ocUserQuotaDao.deleteQuotaForAccount(OC_ACCOUNT_NAME) + ocUserQuotaDao.getQuotaForAccountAsFlow(OC_ACCOUNT_NAME) } } @@ -104,4 +112,29 @@ class OCLocalUserDataSourceTest { ocUserQuotaDao.getAllUserQuotas() } } + + @Test + fun `getAllUserQuotasAsFlow returns a Flow with a list of UserQuota`() = runTest { + every { + ocUserQuotaDao.getAllUserQuotasAsFlow() + } returns flowOf(listOf(userQuotaEntity)) + + val listOfUserQuotas = ocLocalUserDataSource.getAllUserQuotasAsFlow().first() + assertEquals(listOf(userQuotaEntity.toModel()), listOfUserQuotas) + + verify(exactly = 1) { + ocUserQuotaDao.getAllUserQuotasAsFlow() + } + } + + @Test + fun `deleteQuotaForAccount removes user quota correctly`() { + + ocLocalUserDataSource.deleteQuotaForAccount(OC_ACCOUNT_NAME) + + verify(exactly = 1) { + ocUserQuotaDao.deleteQuotaForAccount(OC_ACCOUNT_NAME) + } + } + } From fb4247ec0a4242ef0bb510a0430210f72ba3bb4e Mon Sep 17 00:00:00 2001 From: joragua Date: Wed, 11 Dec 2024 11:30:19 +0100 Subject: [PATCH 3/9] chore: added calens file --- changelog/unreleased/4525 | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 changelog/unreleased/4525 diff --git a/changelog/unreleased/4525 b/changelog/unreleased/4525 new file mode 100644 index 00000000000..20f5b8e346b --- /dev/null +++ b/changelog/unreleased/4525 @@ -0,0 +1,7 @@ +Enhancement: Technical improvements for user quota + +A new use case has been added to fetch the user quota as a flow. +Also, all unnecessary calls from DrawerActivity have been removed. + +https://github.com/owncloud/android/issues/4521 +https://github.com/owncloud/android/pull/4525 From a7a0195446376ae2da6b83587236f09966c56e8a Mon Sep 17 00:00:00 2001 From: joragua Date: Wed, 11 Dec 2024 10:46:05 +0000 Subject: [PATCH 4/9] docs: calens changelog updated --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 734b62088c5..64038868f7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ ownCloud admins and users. * Enhancement - Added text labels for BottomNavigationView: [#4484](https://github.com/owncloud/android/issues/4484) * Enhancement - OCIS Light Users: [#4490](https://github.com/owncloud/android/issues/4490) * Enhancement - Enforce OIDC auth flow via branding: [#4500](https://github.com/owncloud/android/issues/4500) +* Enhancement - Technical improvements for user quota: [#4521](https://github.com/owncloud/android/issues/4521) ## Details @@ -124,6 +125,14 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4500 https://github.com/owncloud/android/pull/4516 +* Enhancement - Technical improvements for user quota: [#4521](https://github.com/owncloud/android/issues/4521) + + A new use case has been added to fetch the user quota as a flow. Also, all + unnecessary calls from DrawerActivity have been removed. + + https://github.com/owncloud/android/issues/4521 + https://github.com/owncloud/android/pull/4525 + # Changelog for ownCloud Android Client [4.4.1] (2024-10-30) The following sections list the changes in ownCloud Android Client 4.4.1 relevant to From 7362d1ce24ca15c5f3f4c8df252b54bbee5ff557 Mon Sep 17 00:00:00 2001 From: joragua Date: Wed, 11 Dec 2024 12:23:59 +0100 Subject: [PATCH 5/9] refactor: added identation in `DrawerViewModelTest` --- .../viewmodels/DrawerViewModelTest.kt | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt b/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt index 353cad8e0de..877df446be5 100644 --- a/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt +++ b/owncloudApp/src/test/java/com/owncloud/android/presentation/viewmodels/DrawerViewModelTest.kt @@ -50,45 +50,45 @@ class DrawerViewModelTest : ViewModelTest() { private val commonException = Exception() - @Before - fun setUp() { - contextProvider = mockk() + @Before + fun setUp() { + contextProvider = mockk() - every { contextProvider.isConnected() } returns true + every { contextProvider.isConnected() } returns true - Dispatchers.setMain(testCoroutineDispatcher) - startKoin { - allowOverride(override = true) - modules( - module { - factory { - contextProvider - } - }) - } + Dispatchers.setMain(testCoroutineDispatcher) + startKoin { + allowOverride(override = true) + modules( + module { + factory { + contextProvider + } + }) + } - getStoredQuotaAsStreamUseCase = mockk() - removeAccountUseCase = mockk() - getUserQuotasUseCase = mockk() - localStorageProvider = mockk() + getStoredQuotaAsStreamUseCase = mockk() + removeAccountUseCase = mockk() + getUserQuotasUseCase = mockk() + localStorageProvider = mockk() - testCoroutineDispatcher.pauseDispatcher() + testCoroutineDispatcher.pauseDispatcher() - drawerViewModel = DrawerViewModel( - getStoredQuotaAsStreamUseCase = getStoredQuotaAsStreamUseCase, - removeAccountUseCase = removeAccountUseCase, - getUserQuotasUseCase = getUserQuotasUseCase, - localStorageProvider = localStorageProvider, - coroutinesDispatcherProvider = coroutineDispatcherProvider, - contextProvider = contextProvider, - accountName = OC_ACCOUNT_NAME - ) - } + drawerViewModel = DrawerViewModel( + getStoredQuotaAsStreamUseCase = getStoredQuotaAsStreamUseCase, + removeAccountUseCase = removeAccountUseCase, + getUserQuotasUseCase = getUserQuotasUseCase, + localStorageProvider = localStorageProvider, + coroutinesDispatcherProvider = coroutineDispatcherProvider, + contextProvider = contextProvider, + accountName = OC_ACCOUNT_NAME + ) + } - @After - override fun tearDown() { - super.tearDown() - stopKoin() - } + @After + override fun tearDown() { + super.tearDown() + stopKoin() + } } From e6d9a437a9fa8d02c3317e7692ff46c831fb237a Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 17 Dec 2024 08:43:18 +0100 Subject: [PATCH 6/9] refactor: modified some asserts from `OCLocalUserDataSourceTest` --- .../datasources/implementation/OCLocalUserDataSourceTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt b/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt index 5ed8d66bc49..0488f8f1aee 100644 --- a/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt +++ b/owncloudData/src/test/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSourceTest.kt @@ -106,7 +106,7 @@ class OCLocalUserDataSourceTest { val resultActual = ocLocalUserDataSource.getAllUserQuotas() - assertEquals(listOf(userQuotaEntity.toModel()), resultActual) + assertEquals(listOf(OC_USER_QUOTA), resultActual) verify(exactly = 1) { ocUserQuotaDao.getAllUserQuotas() @@ -120,7 +120,7 @@ class OCLocalUserDataSourceTest { } returns flowOf(listOf(userQuotaEntity)) val listOfUserQuotas = ocLocalUserDataSource.getAllUserQuotasAsFlow().first() - assertEquals(listOf(userQuotaEntity.toModel()), listOfUserQuotas) + assertEquals(listOf(OC_USER_QUOTA), listOfUserQuotas) verify(exactly = 1) { ocUserQuotaDao.getAllUserQuotasAsFlow() From f9c7f993824b0296f537dbdcfc41c5262b31a705 Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 17 Dec 2024 08:44:39 +0100 Subject: [PATCH 7/9] feat: added nullability to all classes that are related with `GetStoredQuotaAsStreamUseCase` --- .../android/data/user/datasources/LocalUserDataSource.kt | 2 +- .../user/datasources/implementation/OCLocalUserDataSource.kt | 4 ++-- .../main/java/com/owncloud/android/data/user/db/UserDao.kt | 2 +- .../owncloud/android/data/user/repository/OCUserRepository.kt | 2 +- .../java/com/owncloud/android/domain/user/UserRepository.kt | 2 +- .../domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt index d90aec4eccf..06167dcb833 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/LocalUserDataSource.kt @@ -36,7 +36,7 @@ interface LocalUserDataSource { fun getQuotaForAccountAsFlow( accountName: String - ): Flow + ): Flow fun getAllUserQuotas(): List diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt index 97dd5bb0b67..098e0e9883b 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/datasources/implementation/OCLocalUserDataSource.kt @@ -41,8 +41,8 @@ class OCLocalUserDataSource( override fun getQuotaForAccount(accountName: String): UserQuota? = userDao.getQuotaForAccount(accountName = accountName)?.toModel() - override fun getQuotaForAccountAsFlow(accountName: String): Flow = - userDao.getQuotaForAccountAsFlow(accountName = accountName).map { it.toModel() } + override fun getQuotaForAccountAsFlow(accountName: String): Flow = + userDao.getQuotaForAccountAsFlow(accountName = accountName).map { it?.toModel() } override fun getAllUserQuotas(): List { return userDao.getAllUserQuotas().map { userQuotaEntity -> diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt index 55527a944d2..a8d2649ebb0 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/db/UserDao.kt @@ -38,7 +38,7 @@ interface UserDao { @Query(SELECT_QUOTA) fun getQuotaForAccountAsFlow( accountName: String - ): Flow + ): Flow @Query(SELECT_ALL_QUOTAS) fun getAllUserQuotas(): List diff --git a/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt index 2d095b8d27b..bc65fa73131 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/user/repository/OCUserRepository.kt @@ -43,7 +43,7 @@ class OCUserRepository( override fun getStoredUserQuota(accountName: String): UserQuota? = localUserDataSource.getQuotaForAccount(accountName) - override fun getStoredUserQuotaAsFlow(accountName: String): Flow = + override fun getStoredUserQuotaAsFlow(accountName: String): Flow = localUserDataSource.getQuotaForAccountAsFlow(accountName) override fun getAllUserQuotas(): List = diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt index 5e96a829e0d..38dd48bb86f 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/UserRepository.kt @@ -31,7 +31,7 @@ interface UserRepository { fun getUserInfo(accountName: String): UserInfo fun getUserQuota(accountName: String): UserQuota fun getStoredUserQuota(accountName: String): UserQuota? - fun getStoredUserQuotaAsFlow(accountName: String): Flow + fun getStoredUserQuotaAsFlow(accountName: String): Flow fun getAllUserQuotas(): List fun getAllUserQuotasAsFlow(): Flow> fun getUserAvatar(accountName: String): UserAvatar diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt index 01f766923d6..3307ff00c01 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt @@ -27,9 +27,9 @@ import kotlinx.coroutines.flow.Flow class GetStoredQuotaAsStreamUseCase( private val userRepository: UserRepository -) : BaseUseCase, GetStoredQuotaAsStreamUseCase.Params>() { +) : BaseUseCase, GetStoredQuotaAsStreamUseCase.Params>() { - override fun run(params: Params): Flow = + override fun run(params: Params): Flow = userRepository.getStoredUserQuotaAsFlow(params.accountName) data class Params(val accountName: String) From d7f96a287b0ca0c666b05e13b389478de6591d34 Mon Sep 17 00:00:00 2001 From: joragua Date: Wed, 18 Dec 2024 12:39:52 +0100 Subject: [PATCH 8/9] refactor: replaced `BaseUseCase` by `BaseUseCaseWithResult` to have all possible quota cases --- .../presentation/common/DrawerViewModel.kt | 18 +- .../android/ui/activity/DrawerActivity.kt | 187 ++++++++++-------- .../usecases/GetStoredQuotaAsStreamUseCase.kt | 4 +- 3 files changed, 122 insertions(+), 87 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt index ba4b70fbe53..8d8bea931a4 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/common/DrawerViewModel.kt @@ -32,11 +32,15 @@ import com.owncloud.android.data.providers.LocalStorageProvider import com.owncloud.android.domain.user.model.UserQuota import com.owncloud.android.domain.user.usecases.GetStoredQuotaAsStreamUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase +import com.owncloud.android.domain.utils.Event +import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult import com.owncloud.android.presentation.authentication.AccountUtils import com.owncloud.android.providers.ContextProvider import com.owncloud.android.providers.CoroutinesDispatcherProvider import com.owncloud.android.usecases.accounts.RemoveAccountUseCase import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import timber.log.Timber @@ -50,7 +54,19 @@ class DrawerViewModel( accountName: String, ) : ViewModel() { - val userQuota: Flow = getStoredQuotaAsStreamUseCase(GetStoredQuotaAsStreamUseCase.Params(accountName)) + private val _userQuota = MutableStateFlow>>?>(null) + val userQuota: StateFlow>>?> = _userQuota + + init { + runUseCaseWithResult( + coroutineDispatcher = coroutinesDispatcherProvider.io, + requiresConnection = false, + showLoading = true, + flow = _userQuota, + useCase = getStoredQuotaAsStreamUseCase, + useCaseParams = GetStoredQuotaAsStreamUseCase.Params(accountName = accountName), + ) + } fun getAccounts(context: Context): List { return AccountUtils.getAccounts(context).asList() diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt index 801d7be0660..726c40f54c0 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt @@ -57,6 +57,7 @@ import com.google.android.material.navigation.NavigationView import com.owncloud.android.R import com.owncloud.android.domain.capabilities.model.OCCapability import com.owncloud.android.domain.files.model.FileListOption +import com.owncloud.android.domain.user.model.UserQuota import com.owncloud.android.domain.user.model.UserQuotaState import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.collectLatestLifecycleFlow @@ -309,104 +310,121 @@ abstract class DrawerActivity : ToolbarActivity() { */ private fun updateQuota() { Timber.d("Update Quota") - collectLatestLifecycleFlow(drawerViewModel.userQuota) { userQuota -> - when { - userQuota.available == -4L -> { // Light users (oCIS) - getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) - getAccountQuotaBar()?.isVisible = false - getAccountQuotaStatusText()?.isVisible = false + collectLatestLifecycleFlow(drawerViewModel.userQuota) { event -> + event?.let { + when (val uiResult = event.peekContent()) { + is UIResult.Success -> { + uiResult.data?.let { userQuotaData -> + collectLatestLifecycleFlow(userQuotaData) { quota -> + quota?.let { onUpdateQuotaIsSuccessful(it) } + } + } + } + is UIResult.Loading -> getAccountQuotaText()?.text = getString(R.string.drawer_loading_quota) + is UIResult.Error -> getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) } + } + } + } - userQuota.available < 0 -> { // Pending, unknown or unlimited free storage - getAccountQuotaBar()?.apply { - isVisible = true - progress = 0 - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + private fun onUpdateQuotaIsSuccessful(userQuota: UserQuota) { + when { + userQuota.available == -4L -> { // Light users (oCIS) + getAccountQuotaText()?.text = getString(R.string.drawer_unavailable_used_storage) + getAccountQuotaBar()?.isVisible = false + getAccountQuotaStatusText()?.isVisible = false + } + + userQuota.available < 0 -> { // Pending, unknown or unlimited free storage + getAccountQuotaBar()?.apply { + isVisible = true + progress = 0 + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + } + getAccountQuotaText()?.text = String.format( + getString(R.string.drawer_unavailable_free_storage), + DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) + ) + getAccountQuotaStatusText()?.visibility = View.GONE + } + + userQuota.available == 0L -> { // Exceeded storage. The value is over 100%. + getAccountQuotaBar()?.apply { + isVisible = true + progress = 100 + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) + } + + if (userQuota.state == UserQuotaState.EXCEEDED) { // oCIS + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) } - getAccountQuotaText()?.text = String.format( - getString(R.string.drawer_unavailable_free_storage), - DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) - ) + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_exceeded_quota) + } + } else { // oC10 + getAccountQuotaText()?.text = getString(R.string.drawer_exceeded_quota) getAccountQuotaStatusText()?.visibility = View.GONE } + } - userQuota.available == 0L -> { // Exceeded storage. The value is over 100%. + else -> { // Limited storage. Value under 100% + if (userQuota.state == UserQuotaState.NEARING) { // Nearing storage. Value between 75% and 90% getAccountQuotaBar()?.apply { isVisible = true - progress = 100 + progress = userQuota.getRelative().toInt() progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) } - - if (userQuota.state == UserQuotaState.EXCEEDED) { // oCIS - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_exceeded_quota) - } - } else { // oC10 - getAccountQuotaText()?.text = getString(R.string.drawer_exceeded_quota) - getAccountQuotaStatusText()?.visibility = View.GONE + getAccountQuotaText()?.apply { + text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), + userQuota.getRelative() + ) } - } - - else -> { // Limited storage. Value under 100% - if (userQuota.state == UserQuotaState.NEARING) { // Nearing storage. Value between 75% and 90% - getAccountQuotaBar()?.apply { - isVisible = true - progress = userQuota.getRelative().toInt() - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_nearing_quota) - } - } else if (userQuota.state == UserQuotaState.CRITICAL || userQuota.state == UserQuotaState.EXCEEDED) { // Critical storage. Value over 90% - getAccountQuotaBar()?.apply { - isVisible = true - progress = userQuota.getRelative().toInt() - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) - } - getAccountQuotaText()?.apply { - text = String.format( - getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), - userQuota.getRelative() - ) - } - getAccountQuotaStatusText()?.apply { - visibility = View.VISIBLE - text = getString(R.string.drawer_critical_quota) - } - } else { // Normal storage. Value under 75% - getAccountQuotaBar()?.apply { - progress = userQuota.getRelative().toInt() - isVisible = true - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.text = String.format( + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_nearing_quota) + } + } else if (userQuota.state == UserQuotaState.CRITICAL || + userQuota.state == UserQuotaState.EXCEEDED) { // Critical storage. Value over 90% + getAccountQuotaBar()?.apply { + isVisible = true + progress = userQuota.getRelative().toInt() + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.quota_exceeded)) + } + getAccountQuotaText()?.apply { + text = String.format( getString(R.string.drawer_quota), - DisplayUtils.bytesToHumanReadable(userQuota.used, this, true), - DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), this, true), + DisplayUtils.bytesToHumanReadable(userQuota.used, context, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), context, true), userQuota.getRelative() ) - getAccountQuotaStatusText()?.visibility = View.GONE } + getAccountQuotaStatusText()?.apply { + visibility = View.VISIBLE + text = getString(R.string.drawer_critical_quota) + } + } else { // Normal storage. Value under 75% + getAccountQuotaBar()?.apply { + progress = userQuota.getRelative().toInt() + isVisible = true + progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) + } + getAccountQuotaText()?.text = String.format( + getString(R.string.drawer_quota), + DisplayUtils.bytesToHumanReadable(userQuota.used, this, true), + DisplayUtils.bytesToHumanReadable(userQuota.getTotal(), this, true), + userQuota.getRelative() + ) + getAccountQuotaStatusText()?.visibility = View.GONE } } } @@ -490,7 +508,8 @@ abstract class DrawerActivity : ToolbarActivity() { findItem(R.id.nav_settings)?.contentDescription = "${getString(R.string.actionbar_settings)} $roleAccessibilityDescription" findItem(R.id.drawer_menu_feedback)?.contentDescription = "${getString(R.string.drawer_feedback)} $roleAccessibilityDescription" findItem(R.id.drawer_menu_help)?.contentDescription = "${getString(R.string.prefs_help)} $roleAccessibilityDescription" - findItem(R.id.drawer_menu_privacy_policy)?.contentDescription = "${getString(R.string.prefs_privacy_policy)} $roleAccessibilityDescription" + findItem(R.id.drawer_menu_privacy_policy)?.contentDescription = + "${getString(R.string.prefs_privacy_policy)} $roleAccessibilityDescription" } } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt index 3307ff00c01..b514833ce6b 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/usecases/GetStoredQuotaAsStreamUseCase.kt @@ -20,14 +20,14 @@ package com.owncloud.android.domain.user.usecases -import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.user.UserRepository import com.owncloud.android.domain.user.model.UserQuota import kotlinx.coroutines.flow.Flow class GetStoredQuotaAsStreamUseCase( private val userRepository: UserRepository -) : BaseUseCase, GetStoredQuotaAsStreamUseCase.Params>() { +) : BaseUseCaseWithResult, GetStoredQuotaAsStreamUseCase.Params>() { override fun run(params: Params): Flow = userRepository.getStoredUserQuotaAsFlow(params.accountName) From 34533314fca7c7d2271f2dead65254d460807764 Mon Sep 17 00:00:00 2001 From: joragua Date: Fri, 20 Dec 2024 09:42:18 +0100 Subject: [PATCH 9/9] feat: removed progress bar from drawer when the quota is unlimited --- .../java/com/owncloud/android/ui/activity/DrawerActivity.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt index 726c40f54c0..6467e8bfb61 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt @@ -336,11 +336,7 @@ abstract class DrawerActivity : ToolbarActivity() { } userQuota.available < 0 -> { // Pending, unknown or unlimited free storage - getAccountQuotaBar()?.apply { - isVisible = true - progress = 0 - progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } + getAccountQuotaBar()?.visibility = View.GONE getAccountQuotaText()?.text = String.format( getString(R.string.drawer_unavailable_free_storage), DisplayUtils.bytesToHumanReadable(userQuota.used, this, true)