From afdfb477fa47bd24b6eaa90736b6490f06fa6457 Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 25 Nov 2024 12:54:12 +0100 Subject: [PATCH 01/25] feat: added new condition in the login to support light users --- .../android/lib/resources/files/GetBaseUrlRemoteOperation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt index 65a8faf90e3..2fd6ab35cb5 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/GetBaseUrlRemoteOperation.kt @@ -51,7 +51,7 @@ class GetBaseUrlRemoteOperation : RemoteOperation() { val status = client.executeHttpMethod(propFindMethod) - if (isSuccess(status)) { + if (isSuccess(status) || status == HttpConstants.HTTP_NOT_FOUND) { // A light user returns 404 (NOT FOUND) RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { data = propFindMethod.getFinalUrl().toString() } From 64ab6f37c0425ad18524bec33bbed2fc8a2d74f3 Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 25 Nov 2024 14:15:16 +0100 Subject: [PATCH 02/25] feat: added new main fragment (`MainEmptyListFragment`) for light users --- .../files/filelist/MainEmptyListFragment.kt | 57 +++++++++++++++++++ .../ui/activity/FileDisplayActivity.kt | 45 +++++++++++++-- .../res/layout/main_empty_list_fragment.xml | 41 +++++++++++++ owncloudApp/src/main/res/values/strings.xml | 1 + .../spaces/repository/OCSpacesRepository.kt | 9 ++- .../android/domain/user/model/UserQuota.kt | 2 +- 6 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt create mode 100644 owncloudApp/src/main/res/layout/main_empty_list_fragment.xml diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt new file mode 100644 index 00000000000..982e00864a9 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt @@ -0,0 +1,57 @@ +/** + * 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.presentation.files.filelist + +import android.os.Bundle +import androidx.fragment.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.SearchView +import com.owncloud.android.R +import com.owncloud.android.databinding.MainEmptyListFragmentBinding + + +class MainEmptyListFragment : Fragment() { + + private var _binding: MainEmptyListFragmentBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = MainEmptyListFragmentBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + with (binding.emptyDataParent) { + listEmptyDatasetIcon.setImageResource(R.drawable.ic_folder) + listEmptyDatasetTitle.setText(R.string.file_list_empty_title_all_files) + listEmptyDatasetSubTitle.setText(R.string.light_users_subtitle) + } + val searchViewRootToolbar = requireActivity().findViewById(R.id.root_toolbar_search_view) + searchViewRootToolbar.queryHint = getString(R.string.actionbar_search) + } + + +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 85b4397ba97..ff639674b73 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -73,6 +73,7 @@ import com.owncloud.android.domain.files.model.FileListOption import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.model.OCFile.Companion.ROOT_PARENT_ID import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.domain.user.model.UserQuota import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.checkPasscodeEnforced import com.owncloud.android.extensions.collectLatestLifecycleFlow @@ -94,9 +95,11 @@ import com.owncloud.android.lib.resources.status.OwnCloudVersion import com.owncloud.android.operations.SyncProfileOperation import com.owncloud.android.presentation.authentication.AccountUtils.getCurrentOwnCloudAccount import com.owncloud.android.presentation.capabilities.CapabilityViewModel +import com.owncloud.android.presentation.common.DrawerViewModel import com.owncloud.android.presentation.common.UIResult import com.owncloud.android.presentation.conflicts.ConflictsResolveActivity import com.owncloud.android.presentation.files.details.FileDetailsFragment +import com.owncloud.android.presentation.files.filelist.MainEmptyListFragment import com.owncloud.android.presentation.files.filelist.MainFileListFragment import com.owncloud.android.presentation.files.operations.FileOperation import com.owncloud.android.presentation.files.operations.FileOperationsViewModel @@ -185,6 +188,7 @@ class FileDisplayActivity : FileActivity(), private val fileOperationsViewModel: FileOperationsViewModel by viewModel() private val transfersViewModel: TransfersViewModel by viewModel() private lateinit var spacesListViewModel: SpacesListViewModel + private val drawerViewModel: DrawerViewModel by viewModel() private val sharedPreferences: SharedPreferencesProvider by inject() @@ -193,6 +197,8 @@ class FileDisplayActivity : FileActivity(), private lateinit var binding: ActivityMainBinding + private var isLightUser = false + override fun onCreate(savedInstanceState: Bundle?) { Timber.v("onCreate() start") @@ -318,7 +324,10 @@ class FileDisplayActivity : FileActivity(), capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { onCapabilitiesOperationFinish(it) }) - navigateTo(fileListOption, initialState = true) + + drawerViewModel.userQuota.observe(this, Event.EventObserver { + onUserQuotaOperationFinish(it) + }) } startListeningToOperations() @@ -422,6 +431,14 @@ class FileDisplayActivity : FileActivity(), transaction.commit() } + private fun initAndShowEmptyPersonalFolder(){ + val emptyListFragment = MainEmptyListFragment() + this.fileListOption = FileListOption.ALL_FILES + val transaction = supportFragmentManager.beginTransaction() + transaction.replace(R.id.left_fragment_container, emptyListFragment) + transaction.commit() + } + private fun initFragmentsWithFile() { if (account != null && file != null) { /// First fragment @@ -1505,6 +1522,15 @@ class FileDisplayActivity : FileActivity(), } } + private fun onUserQuotaOperationFinish(uiResult: UIResult) { + if (uiResult is UIResult.Success) { + if (uiResult.data?.available == -4L) { + isLightUser = true + } + } + navigateTo(fileListOption, initialState = true) + } + override fun onSavedCertificate() { startSyncFolderOperation(currentDir, false) } @@ -1743,13 +1769,20 @@ class FileDisplayActivity : FileActivity(), val previousFileListOption = fileListOption when (newFileListOption) { FileListOption.ALL_FILES -> { - if (previousFileListOption != newFileListOption || initialState) { - file = storageManager.getRootPersonalFolder() + if (isLightUser) { + file = null fileListOption = newFileListOption - mainFileListFragment?.updateFileListOption(newFileListOption, file) ?: initAndShowListOfFiles(newFileListOption) - updateToolbar(file) + initAndShowEmptyPersonalFolder() + updateToolbar(null) } else { - browseToRoot() + if (previousFileListOption != newFileListOption || initialState) { + file = storageManager.getRootPersonalFolder() + fileListOption = newFileListOption + mainFileListFragment?.updateFileListOption(newFileListOption, file) ?: initAndShowListOfFiles(newFileListOption) + updateToolbar(file) + } else { + browseToRoot() + } } } diff --git a/owncloudApp/src/main/res/layout/main_empty_list_fragment.xml b/owncloudApp/src/main/res/layout/main_empty_list_fragment.xml new file mode 100644 index 00000000000..2b2655226c3 --- /dev/null +++ b/owncloudApp/src/main/res/layout/main_empty_list_fragment.xml @@ -0,0 +1,41 @@ + + + + + + + + diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index c9f71477b41..89390c76cdf 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -165,6 +165,7 @@ No available offline files No shared links Upload some content or sync with your devices! + Personal folder is not available No shares You are not collaborating on other people\'s resources. You don\'t have access to any space! diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt index bcabd393582..3201e32e106 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -39,15 +39,14 @@ class OCSpacesRepository( remoteSpacesDataSource.refreshSpacesForAccount(accountName).also { listOfSpaces -> localSpacesDataSource.saveSpacesForAccount(listOfSpaces) val personalSpace = listOfSpaces.find { it.isPersonal } - personalSpace?.let { - val userQuota = if (it.quota?.total!!.toInt() == 0) { + val userQuota = personalSpace?.let { + if (it.quota?.total!!.toInt() == 0) { UserQuota(accountName, -3, it.quota?.used!!, it.quota?.total!!, UserQuotaState.fromValue(it.quota?.state!!)) } else { UserQuota(accountName, it.quota?.remaining!!, it.quota?.used!!, it.quota?.total!!, UserQuotaState.fromValue(it.quota?.state!!)) } - localUserDataSource.saveQuotaForAccount(accountName, userQuota) - } - + } ?: UserQuota (accountName, -4, 0, 0, UserQuotaState.NORMAL) + localUserDataSource.saveQuotaForAccount(accountName, userQuota) } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/model/UserQuota.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/model/UserQuota.kt index d3ad5c755e1..51bb3c6ccf9 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/user/model/UserQuota.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/user/model/UserQuota.kt @@ -26,7 +26,7 @@ import kotlin.math.roundToLong data class UserQuota( val accountName: String, - val available: Long, + val available: Long, // -4 : Light Users | -3: Unlimited quota | OTHER: Limited quota val used: Long, val total: Long?, val state: UserQuotaState? From 3e02b60a71a5c8fe13ea48f8265e23faca804f6f Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 25 Nov 2024 14:20:02 +0100 Subject: [PATCH 03/25] feat: disabled refresh operation for av.offline view and removed nullable condition for spaces header --- .../files/filelist/MainFileListFragment.kt | 43 ++++++++++--------- .../ui/activity/FileDisplayActivity.kt | 2 +- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt index 46778355b76..b13b068a2e7 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt @@ -258,6 +258,7 @@ class MainFileListFragment : Fragment(), binding.recyclerViewMainFileList.adapter = fileListAdapter // Set Swipe to refresh and its listener + binding.swipeRefreshMainFileList.isEnabled = mainFileListViewModel.fileListOption.value != FileListOption.AV_OFFLINE binding.swipeRefreshMainFileList.setOnRefreshListener { fileOperationsViewModel.performOperation( FileOperation.RefreshFolderOperation( @@ -315,9 +316,9 @@ class MainFileListFragment : Fragment(), // Observe the current folder displayed collectLatestLifecycleFlow(mainFileListViewModel.currentFolderDisplayed) { currentFolderDisplayed: OCFile -> fileActions?.onCurrentFolderUpdated(currentFolderDisplayed, mainFileListViewModel.getSpace()) - val fileListOption = mainFileListViewModel.fileListOption.value + val fileListOption = (requireActivity() as FileDisplayActivity).fileListOption val refreshFolderNeeded = fileListOption.isAllFiles() || - (!fileListOption.isAllFiles() && currentFolderDisplayed.remotePath != ROOT_PATH) + (!fileListOption.isAllFiles() && currentFolderDisplayed.remotePath != ROOT_PATH && !fileListOption.isAvailableOffline()) if (refreshFolderNeeded) { fileOperationsViewModel.performOperation( FileOperation.RefreshFolderOperation( @@ -609,29 +610,28 @@ class MainFileListFragment : Fragment(), ) showOrHideEmptyView(fileListUiState) - fileListUiState.space?.let { - binding.spaceHeader.root.apply { - if (fileListUiState.space.isProject && fileListUiState.folderToDisplay?.remotePath == ROOT_PATH) { - isVisible = true - animate().translationY(0f).duration = 100 - } else { - animate().translationY(-height.toFloat()).withEndAction { isVisible = false } - } + + binding.spaceHeader.root.apply { + if (fileListUiState.space?.isProject == true && fileListUiState.folderToDisplay?.remotePath == ROOT_PATH) { + isVisible = true + animate().translationY(0f).duration = 100 + } else { + animate().translationY(-height.toFloat()).withEndAction { isVisible = false } } + } - val spaceSpecialImage = it.getSpaceSpecialImage() - if (spaceSpecialImage != null) { - binding.spaceHeader.spaceHeaderImage.load( - ThumbnailsRequester.getPreviewUriForSpaceSpecial(spaceSpecialImage), - ThumbnailsRequester.getCoilImageLoader() - ) { - placeholder(R.drawable.ic_spaces) - error(R.drawable.ic_spaces) - } + val spaceSpecialImage = fileListUiState.space?.getSpaceSpecialImage() + if (spaceSpecialImage != null) { + binding.spaceHeader.spaceHeaderImage.load( + ThumbnailsRequester.getPreviewUriForSpaceSpecial(spaceSpecialImage), + ThumbnailsRequester.getCoilImageLoader() + ) { + placeholder(R.drawable.ic_spaces) + error(R.drawable.ic_spaces) } - binding.spaceHeader.spaceHeaderName.text = it.name - binding.spaceHeader.spaceHeaderSubtitle.text = it.description } + binding.spaceHeader.spaceHeaderName.text = fileListUiState.space?.name + binding.spaceHeader.spaceHeaderSubtitle.text = fileListUiState.space?.description actionMode?.invalidate() } @@ -812,6 +812,7 @@ class MainFileListFragment : Fragment(), fun updateFileListOption(newFileListOption: FileListOption, file: OCFile) { mainFileListViewModel.updateFolderToDisplay(file) mainFileListViewModel.updateFileListOption(newFileListOption) + binding.swipeRefreshMainFileList.isEnabled = newFileListOption != FileListOption.AV_OFFLINE showOrHideFab(newFileListOption, file) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index ff639674b73..c7c2acd4798 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -179,7 +179,7 @@ class FileDisplayActivity : FileActivity(), private var syncInProgress = false - private var fileListOption = FileListOption.ALL_FILES + var fileListOption = FileListOption.ALL_FILES private var waitingToSend: OCFile? = null private var waitingToOpen: OCFile? = null From dae2f5a0fc8ed0827e239991dbc4ec95c4262fd3 Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 25 Nov 2024 14:23:16 +0100 Subject: [PATCH 04/25] feat: added new condition to support light users in drawer and manage accounts dialog --- .../accounts/ManageAccountsAdapter.kt | 5 ++++ .../presentation/common/DrawerViewModel.kt | 2 ++ .../android/ui/activity/DrawerActivity.kt | 23 +++++++++++-------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt index 934b0e35152..46222ae4e39 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsAdapter.kt @@ -156,6 +156,11 @@ class ManageAccountsAdapter( private fun updateQuota(quotaText: TextView, quotaBar: ProgressBar, userQuota: UserQuota, context: Context) { when { + userQuota.available == -4L -> { // Light users (oCIS) + quotaBar.visibility = View.GONE + quotaText.text = context.getString(R.string.drawer_unavailable_used_storage) + } + userQuota.available < 0 -> { // Pending, unknown or unlimited free storage. The progress bar is hid quotaBar.visibility = View.GONE quotaText.text = DisplayUtils.bytesToHumanReadable(userQuota.used, context, false) 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 7afe0d49191..f8301362dff 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 @@ -4,6 +4,7 @@ * @author David González Verdugo * @author Abel García de Prada * @author Aitor Ballesteros Pavón + * @author Jorge Aguado Recio * * Copyright (C) 2024 ownCloud GmbH. * @@ -58,6 +59,7 @@ class DrawerViewModel( accountName: String ) = runUseCaseWithResult( coroutineDispatcher = coroutinesDispatcherProvider.io, + requiresConnection = false, showLoading = true, liveData = _userQuota, useCase = getStoredQuotaUseCase, 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 7f68c85f245..47cb2bfc6f2 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 @@ -312,16 +312,21 @@ abstract class DrawerActivity : ToolbarActivity() { 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) - ) + 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 } From 08d277ad4432c32f74f7036b8abd5df6b9ce47a7 Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 26 Nov 2024 08:24:16 +0100 Subject: [PATCH 05/25] feat: disabled light users for automatic uploads --- .../SettingsPictureUploadsFragment.kt | 59 +++++++++++++------ .../SettingsPictureUploadsViewModel.kt | 5 +- .../SettingsVideoUploadsFragment.kt | 59 +++++++++++++------ .../SettingsVideoUploadsViewModel.kt | 5 +- 4 files changed, 88 insertions(+), 40 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index edb74b12ca4..c9e8abbd4f2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -3,8 +3,9 @@ * * @author Juan Carlos Garrote Gascón * @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, @@ -48,7 +49,9 @@ import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_PA import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_SOURCE import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_WIFI_ONLY import com.owncloud.android.domain.automaticuploads.model.UploadBehavior +import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.showAlertDialog +import com.owncloud.android.presentation.accounts.ManageAccountsViewModel import com.owncloud.android.ui.activity.FolderPickerActivity import com.owncloud.android.utils.DisplayUtils import kotlinx.coroutines.launch @@ -59,6 +62,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { // ViewModel private val picturesViewModel by viewModel() + private val manageAccountsViewModel by viewModel() private var prefEnablePictureUploads: SwitchPreferenceCompat? = null private var prefPictureUploadsPath: Preference? = null @@ -103,10 +107,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { ).toTypedArray() entryValues = listOf(UploadBehavior.COPY.name, UploadBehavior.MOVE.name).toTypedArray() } - prefPictureUploadsAccount = findPreference(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME)?.apply { - entries = picturesViewModel.getLoggedAccountNames() - entryValues = picturesViewModel.getLoggedAccountNames() - } + prefPictureUploadsAccount = findPreference(PREF__CAMERA_PICTURE_UPLOADS_ACCOUNT_NAME) val comment = getString(R.string.prefs_camera_upload_source_path_title_required) prefPictureUploadsSourcePath?.title = String.format(prefPictureUploadsSourcePath?.title.toString(), comment) @@ -123,18 +124,30 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { private fun initStateObservers() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - picturesViewModel.pictureUploads.collect { pictureUploadsConfiguration -> - enablePictureUploads(pictureUploadsConfiguration != null) - pictureUploadsConfiguration?.let { - prefPictureUploadsAccount?.value = it.accountName - prefPictureUploadsPath?.summary = picturesViewModel.getUploadPathString() - prefPictureUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) - prefPictureUploadsOnWifi?.isChecked = it.wifiOnly - prefPictureUploadsOnCharging?.isChecked = it.chargingOnly - prefPictureUploadsBehaviour?.value = it.behavior.name - prefPictureUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp) - spaceId = it.spaceId - } ?: resetFields() + collectLatestLifecycleFlow(manageAccountsViewModel.userQuotas) { listUserQuotas -> + val availableAccounts = listUserQuotas.filter { it.available != -4L } + prefPictureUploadsAccount?.apply { + entries = availableAccounts.map { it.accountName }.toTypedArray() + entryValues = availableAccounts.map { it.accountName }.toTypedArray() + } + + if (availableAccounts.isEmpty()) { + disableFields() + } else { + picturesViewModel.pictureUploads.collect { pictureUploadsConfiguration -> + enablePictureUploads(pictureUploadsConfiguration != null) + pictureUploadsConfiguration?.let { + prefPictureUploadsAccount?.value = it.accountName + prefPictureUploadsPath?.summary = picturesViewModel.getUploadPathString() + prefPictureUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) + prefPictureUploadsOnWifi?.isChecked = it.wifiOnly + prefPictureUploadsOnCharging?.isChecked = it.chargingOnly + prefPictureUploadsBehaviour?.value = it.behavior.name + prefPictureUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp) + spaceId = it.spaceId + } ?: resetFields() + } + } } } } @@ -248,4 +261,16 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { prefPictureUploadsBehaviour?.value = UploadBehavior.COPY.name prefPictureUploadsLastSync?.summary = null } + + private fun disableFields() { + prefEnablePictureUploads?.isChecked = false + prefEnablePictureUploads?.isEnabled = false + prefPictureUploadsAccount?.isEnabled = false + prefPictureUploadsPath?.isEnabled = false + prefPictureUploadsSourcePath?.isEnabled = false + prefPictureUploadsOnWifi?.isEnabled = false + prefPictureUploadsOnCharging?.isEnabled = false + prefPictureUploadsBehaviour?.isEnabled = false + prefPictureUploadsLastSync?.isEnabled = false + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt index 256c28e7a90..8b1443d5439 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt @@ -3,8 +3,9 @@ * * @author Juan Carlos Garrote Gascón * @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, @@ -124,8 +125,6 @@ class SettingsPictureUploadsViewModel( fun getPictureUploadsAccount() = _pictureUploads.value?.accountName - fun getLoggedAccountNames(): Array = accountProvider.getLoggedAccounts().map { it.name }.toTypedArray() - fun getPictureUploadsPath() = _pictureUploads.value?.uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH fun getPictureUploadsSourcePath(): String? = _pictureUploads.value?.sourcePath diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index 2fbf963fba1..1d14aee33cc 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -3,8 +3,9 @@ * * @author Juan Carlos Garrote Gascón * @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, @@ -48,7 +49,9 @@ import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_PATH import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_SOURCE import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_WIFI_ONLY import com.owncloud.android.domain.automaticuploads.model.UploadBehavior +import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.showAlertDialog +import com.owncloud.android.presentation.accounts.ManageAccountsViewModel import com.owncloud.android.ui.activity.FolderPickerActivity import com.owncloud.android.utils.DisplayUtils import kotlinx.coroutines.launch @@ -59,6 +62,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { // ViewModel private val videosViewModel by viewModel() + private val manageAccountsViewModel by viewModel() private var prefEnableVideoUploads: SwitchPreferenceCompat? = null private var prefVideoUploadsPath: Preference? = null @@ -100,10 +104,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { entries = listOf(getString(R.string.pref_behaviour_entries_keep_file), getString(R.string.pref_behaviour_entries_remove_original_file)).toTypedArray() entryValues = listOf(UploadBehavior.COPY.name, UploadBehavior.MOVE.name).toTypedArray() } - prefVideoUploadsAccount = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME)?.apply { - entries = videosViewModel.getLoggedAccountNames() - entryValues = videosViewModel.getLoggedAccountNames() - } + prefVideoUploadsAccount = findPreference(PREF__CAMERA_VIDEO_UPLOADS_ACCOUNT_NAME) val comment = getString(R.string.prefs_camera_upload_source_path_title_required) prefVideoUploadsSourcePath?.title = String.format(prefVideoUploadsSourcePath?.title.toString(), comment) @@ -120,18 +121,30 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { private fun initLiveDataObservers() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - videosViewModel.videoUploads.collect { videoUploadsConfiguration -> - enableVideoUploads(videoUploadsConfiguration != null) - videoUploadsConfiguration?.let { - prefVideoUploadsAccount?.value = it.accountName - prefVideoUploadsPath?.summary = videosViewModel.getUploadPathString() - prefVideoUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) - prefVideoUploadsOnWifi?.isChecked = it.wifiOnly - prefVideoUploadsOnCharging?.isChecked = it.chargingOnly - prefVideoUploadsBehaviour?.value = it.behavior.name - prefVideoUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp) - spaceId = it.spaceId - } ?: resetFields() + collectLatestLifecycleFlow(manageAccountsViewModel.userQuotas) { listUserQuotas -> + val availableAccounts = listUserQuotas.filter { it.available != -4L } + prefVideoUploadsAccount?.apply { + entries = availableAccounts.map { it.accountName }.toTypedArray() + entryValues = availableAccounts.map { it.accountName }.toTypedArray() + } + + if (availableAccounts.isEmpty()) { + disableFields() + } else { + videosViewModel.videoUploads.collect { videoUploadsConfiguration -> + enableVideoUploads(videoUploadsConfiguration != null) + videoUploadsConfiguration?.let { + prefVideoUploadsAccount?.value = it.accountName + prefVideoUploadsPath?.summary = videosViewModel.getUploadPathString() + prefVideoUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) + prefVideoUploadsOnWifi?.isChecked = it.wifiOnly + prefVideoUploadsOnCharging?.isChecked = it.chargingOnly + prefVideoUploadsBehaviour?.value = it.behavior.name + prefVideoUploadsLastSync?.summary = DisplayUtils.unixTimeToHumanReadable(it.lastSyncTimestamp) + spaceId = it.spaceId + } ?: resetFields() + } + } } } } @@ -245,4 +258,16 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { prefVideoUploadsBehaviour?.value = UploadBehavior.COPY.name prefVideoUploadsLastSync?.summary = null } + + private fun disableFields() { + prefEnableVideoUploads?.isChecked = false + prefEnableVideoUploads?.isEnabled = false + prefVideoUploadsAccount?.isEnabled = false + prefVideoUploadsPath?.isEnabled = false + prefVideoUploadsSourcePath?.isEnabled = false + prefVideoUploadsOnWifi?.isEnabled = false + prefVideoUploadsOnCharging?.isEnabled = false + prefVideoUploadsBehaviour?.isEnabled = false + prefVideoUploadsLastSync?.isEnabled = false + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt index e205bd0477c..8a76cb1d2d9 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt @@ -3,8 +3,9 @@ * * @author Juan Carlos Garrote Gascón * @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, @@ -124,8 +125,6 @@ class SettingsVideoUploadsViewModel( fun getVideoUploadsAccount() = _videoUploads.value?.accountName - fun getLoggedAccountNames(): Array = accountProvider.getLoggedAccounts().map { it.name }.toTypedArray() - fun getVideoUploadsPath() = _videoUploads.value?.uploadPath ?: PREF__CAMERA_UPLOADS_DEFAULT_PATH fun getVideoUploadsSourcePath(): String? = _videoUploads.value?.sourcePath From 7b35c2ec462db7517f7bfbe9332ba579f42df0dd Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 26 Nov 2024 08:34:26 +0100 Subject: [PATCH 06/25] feat: adapted `ReceiveExternalFiles` (Activity and ViewModel) for light users --- .../dependecyinjection/ViewModelModule.kt | 2 +- .../ui/ReceiveExternalFilesViewModel.kt | 24 +++++++------------ .../ReceiveExternalFilesActivity.java | 18 +++++++------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index 5a5abd60001..0dbc8bd07ff 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -97,7 +97,7 @@ val viewModelModule = module { viewModel { AuthenticationViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { MigrationViewModel(MainApp.dataFolder, get(), get(), get(), get(), get(), get(), get()) } viewModel { TransfersViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } - viewModel { ReceiveExternalFilesViewModel(get(), get(), get()) } + viewModel { ReceiveExternalFilesViewModel(get(), get(), get(), get()) } viewModel { (accountName: String, showPersonalSpace: Boolean) -> SpacesListViewModel(get(), get(), get(), get(), get(), accountName, showPersonalSpace) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt index 469aeda09b5..582c2d5b113 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt @@ -1,7 +1,7 @@ /** * ownCloud Android client application * - * 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, @@ -22,9 +22,8 @@ import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase import com.owncloud.android.domain.files.model.OCFile -import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.usecases.GetPersonalSpaceForAccountUseCase import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult import com.owncloud.android.presentation.common.UIResult @@ -35,15 +34,14 @@ import kotlinx.coroutines.launch class ReceiveExternalFilesViewModel( private val synchronizeFolderUseCase: SynchronizeFolderUseCase, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider, - private val getPersonalSpaceForAccountUseCase: GetPersonalSpaceForAccountUseCase, + private val getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase, ) : ViewModel() { private val _syncFolderLiveData = MediatorLiveData>>() val syncFolderLiveData: LiveData>> = _syncFolderLiveData - private val _personalSpaceLiveData = MutableLiveData() - val personalSpaceLiveData: LiveData = _personalSpaceLiveData - + private val _spacesAreAllowed = MutableLiveData() + val spacesAreAllowed: LiveData = _spacesAreAllowed fun refreshFolderUseCase( folderToSync: OCFile, @@ -60,16 +58,12 @@ class ReceiveExternalFilesViewModel( ) ) - fun getPersonalSpaceForAccount(accountName: String) { + fun areSpacesAllowed(accountName: String) { viewModelScope.launch(coroutinesDispatcherProvider.io) { - val result = getPersonalSpaceForAccountUseCase( - GetPersonalSpaceForAccountUseCase.Params( - accountName = accountName - ) - ) - _personalSpaceLiveData.postValue(result) + val capabilities = getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName)) + val spacesAvailableForAccount = capabilities?.isSpacesAllowed() == true + _spacesAreAllowed.postValue(spacesAvailableForAccount) } } - } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index ea2c2e2947e..a0795fad04c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -10,6 +10,7 @@ * @author Abel García de Prada * @author John Kalimeris * @author Aitor Ballesteros Pavón + * @author Jorge Aguado Recio * * Copyright (C) 2012 Bartek Przybylski * Copyright (C) 2024 ownCloud GmbH. @@ -179,7 +180,7 @@ public class ReceiveExternalFilesActivity extends FileActivity private boolean showHiddenFiles; private OCSharedPreferencesProvider sharedPreferencesProvider; - private OCSpace personalSpace; + private boolean areSpacesAllowed; Pattern pattern = Pattern.compile("[/\\\\]"); @@ -228,10 +229,11 @@ protected void onCreate(Bundle savedInstanceState) { } private void subscribeToViewModels() { - mReceiveExternalFilesViewModel.getPersonalSpaceLiveData().observe(this, ocSpace -> { - personalSpace = ocSpace; + mReceiveExternalFilesViewModel.areSpacesAllowed(getAccount().name); + mReceiveExternalFilesViewModel.getSpacesAreAllowed().observe(this, spaces -> { + areSpacesAllowed = spaces; - if (personalSpace == null) { // OC10 Server + if (!areSpacesAllowed) { // OC10 Server showListOfFiles(); showRetainerFragment(); updateDirectoryList(); @@ -337,7 +339,7 @@ protected void setAccount(Account account, boolean savedAccount) { @Override protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(mAccountWasRestored); - mReceiveExternalFilesViewModel.getPersonalSpaceForAccount(getAccount().name); + mReceiveExternalFilesViewModel.areSpacesAllowed(getAccount().name); initTargetFolder(); mReceiveExternalFilesViewModel.getSyncFolderLiveData().observe(this, eventUiResult -> { @@ -360,7 +362,7 @@ protected void onAccountSet(boolean stateWasRecovered) { } else if (uiResult instanceof UIResult.Success) { mSyncInProgress = false; updateDirectoryList(); - if (mParents.size() == 1 && personalSpace == null) { + if (mParents.size() == 1) { updateToolbar(getString(R.string.uploader_top_message)); } if(fragmentContainer.getVisibility() == View.VISIBLE) { @@ -433,7 +435,7 @@ public void onClick(DialogInterface dialog, int which) { onAccountSet(mAccountWasRestored); dialog.dismiss(); PreferenceManager.setLastUploadPath("/", this); - mReceiveExternalFilesViewModel.getPersonalSpaceForAccount(getAccount().name); + mReceiveExternalFilesViewModel.areSpacesAllowed(getAccount().name); mAccountSelected = true; mAccountSelectionShowing = false; }); @@ -458,7 +460,7 @@ public void onBackPressed() { String full_path = generatePath(mParents); startSyncFolderOperation(getStorageManager().getFileByPath(full_path, currentSpaceId)); updateDirectoryList(); - if (mParents.size() <= 1 && personalSpace == null) { + if (mParents.size() <= 1) { updateToolbar(getString(R.string.uploader_top_message)); } } From 05b9792bb19566437de79b385ef7f99f38374bb9 Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 26 Nov 2024 08:48:57 +0100 Subject: [PATCH 07/25] feat: added release note --- .../presentation/releasenotes/ReleaseNotesViewModel.kt | 5 +++++ owncloudApp/src/main/res/values/strings.xml | 2 ++ 2 files changed, 7 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt index c1991be1e03..d0247f18059 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/releasenotes/ReleaseNotesViewModel.kt @@ -65,6 +65,11 @@ class ReleaseNotesViewModel( subtitle = R.string.release_notes_4_5_0_subtitle_quota_improvements, type = ReleaseNoteType.ENHANCEMENT ), + ReleaseNote( + title = R.string.release_notes_4_5_0_title_light_users, + subtitle = R.string.release_notes_4_5_0_subtitle_light_users, + type = ReleaseNoteType.ENHANCEMENT + ), ) } } diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index 89390c76cdf..c7d983dbd0b 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -739,6 +739,8 @@ Added feedback when (un)setting av. offline in all previews and updated options menu depending on file status Improvements in storage occupation display Storage occupation refreshed frequently in file and refresh operations and available in accounts manager view, besides of drawer menu + oCIS Light users + Light users (users without personal space) are now supported in the app From 128d329b48b064e1cc7fac87732fc9a27fb5d7b4 Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 26 Nov 2024 08:49:15 +0100 Subject: [PATCH 08/25] chore: added calens file --- changelog/unreleased/4518 | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/unreleased/4518 diff --git a/changelog/unreleased/4518 b/changelog/unreleased/4518 new file mode 100644 index 00000000000..9a3dbd5f583 --- /dev/null +++ b/changelog/unreleased/4518 @@ -0,0 +1,6 @@ +Enhancement: oCIS Light Users + +oCIS light users (users without personal space) have been supported in the app + +https://github.com/owncloud/android/issues/4490 +https://github.com/owncloud/android/pull/4518 From d8989c3165ef41ab4375ea417ed9e41a603e0374 Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 26 Nov 2024 10:26:21 +0100 Subject: [PATCH 09/25] refactor: removed unnecessary get in `ViewModelModule` and Ktlint changes --- .../com/owncloud/android/dependecyinjection/ViewModelModule.kt | 2 +- .../presentation/files/filelist/MainEmptyListFragment.kt | 2 +- .../com/owncloud/android/ui/activity/FileDisplayActivity.kt | 2 +- .../android/data/spaces/repository/OCSpacesRepository.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index 0dbc8bd07ff..5a5abd60001 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -97,7 +97,7 @@ val viewModelModule = module { viewModel { AuthenticationViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { MigrationViewModel(MainApp.dataFolder, get(), get(), get(), get(), get(), get(), get()) } viewModel { TransfersViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } - viewModel { ReceiveExternalFilesViewModel(get(), get(), get(), get()) } + viewModel { ReceiveExternalFilesViewModel(get(), get(), get()) } viewModel { (accountName: String, showPersonalSpace: Boolean) -> SpacesListViewModel(get(), get(), get(), get(), get(), accountName, showPersonalSpace) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt index 982e00864a9..5153314f2ff 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt @@ -44,7 +44,7 @@ class MainEmptyListFragment : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - with (binding.emptyDataParent) { + with(binding.emptyDataParent) { listEmptyDatasetIcon.setImageResource(R.drawable.ic_folder) listEmptyDatasetTitle.setText(R.string.file_list_empty_title_all_files) listEmptyDatasetSubTitle.setText(R.string.light_users_subtitle) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index c7c2acd4798..008401b7905 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -431,7 +431,7 @@ class FileDisplayActivity : FileActivity(), transaction.commit() } - private fun initAndShowEmptyPersonalFolder(){ + private fun initAndShowEmptyPersonalFolder() { val emptyListFragment = MainEmptyListFragment() this.fileListOption = FileListOption.ALL_FILES val transaction = supportFragmentManager.beginTransaction() diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt index 3201e32e106..63fdf11d44e 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -45,7 +45,7 @@ class OCSpacesRepository( } else { UserQuota(accountName, it.quota?.remaining!!, it.quota?.used!!, it.quota?.total!!, UserQuotaState.fromValue(it.quota?.state!!)) } - } ?: UserQuota (accountName, -4, 0, 0, UserQuotaState.NORMAL) + } ?: UserQuota(accountName, -4, 0, 0, UserQuotaState.NORMAL) localUserDataSource.saveQuotaForAccount(accountName, userQuota) } } From 404a357b7319fbd28e536c6437b4af8d43a9a206 Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 26 Nov 2024 10:26:32 +0100 Subject: [PATCH 10/25] feat: added a new condition to avoid oCIS accounts warning dialog for light users --- .../src/main/java/com/owncloud/android/MainApp.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt b/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt index f0c30dc2e51..9f520a3e86e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt @@ -50,6 +50,7 @@ import com.owncloud.android.dependecyinjection.viewModelModule import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.spaces.usecases.GetPersonalSpaceForAccountUseCase +import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase import com.owncloud.android.extensions.createNotificationChannel import com.owncloud.android.lib.common.SingleSessionManager import com.owncloud.android.presentation.authentication.AccountUtils @@ -194,7 +195,13 @@ class MainApp : Application() { } } - spacesAllowed && personalSpace == null + val getUserQuotasUseCase: GetUserQuotasUseCase by inject() + val userQuotas = withContext(CoroutineScope(CoroutinesDispatcherProvider().io).coroutineContext) { + getUserQuotasUseCase(Unit) + } + val isLightUser = userQuotas.filter { it.accountName == account.name }.any { it.available == -4L } + + spacesAllowed && personalSpace == null && !isLightUser } override fun onActivityStarted(activity: Activity) { From ef066982c95079ff35b0daa62ff624091dd3179c Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 11:01:33 +0100 Subject: [PATCH 11/25] refactor: changed the content of calens file and release note and renamed one method in `FileDisplayActivity` --- changelog/unreleased/4518 | 2 +- .../android/ui/activity/DrawerActivity.kt | 20 +++++++++---------- .../ui/activity/FileDisplayActivity.kt | 4 ++-- owncloudApp/src/main/res/values/strings.xml | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/changelog/unreleased/4518 b/changelog/unreleased/4518 index 9a3dbd5f583..3348ed917f4 100644 --- a/changelog/unreleased/4518 +++ b/changelog/unreleased/4518 @@ -1,6 +1,6 @@ Enhancement: oCIS Light Users -oCIS light users (users without personal space) have been supported in the app +Support for oCIS light users has been added to the app https://github.com/owncloud/android/issues/4490 https://github.com/owncloud/android/pull/4518 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 47cb2bfc6f2..1df8f805cce 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 @@ -318,16 +318,16 @@ abstract class DrawerActivity : ToolbarActivity() { 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 + 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%. diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 008401b7905..d4c8e5ca8a9 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -431,7 +431,7 @@ class FileDisplayActivity : FileActivity(), transaction.commit() } - private fun initAndShowEmptyPersonalFolder() { + private fun initAndShowEmptyPersonalSpace() { val emptyListFragment = MainEmptyListFragment() this.fileListOption = FileListOption.ALL_FILES val transaction = supportFragmentManager.beginTransaction() @@ -1772,7 +1772,7 @@ class FileDisplayActivity : FileActivity(), if (isLightUser) { file = null fileListOption = newFileListOption - initAndShowEmptyPersonalFolder() + initAndShowEmptyPersonalSpace() updateToolbar(null) } else { if (previousFileListOption != newFileListOption || initialState) { diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index c7d983dbd0b..c8fef27a9dd 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -165,7 +165,7 @@ No available offline files No shared links Upload some content or sync with your devices! - Personal folder is not available + Personal space is not available No shares You are not collaborating on other people\'s resources. You don\'t have access to any space! @@ -739,8 +739,8 @@ Added feedback when (un)setting av. offline in all previews and updated options menu depending on file status Improvements in storage occupation display Storage occupation refreshed frequently in file and refresh operations and available in accounts manager view, besides of drawer menu - oCIS Light users - Light users (users without personal space) are now supported in the app + ownCloud Infinite Scale servers light users + ownCloud Infinite Scale servers light users (users without personal space) are now supported in the app From b4893f784eda54e838ae306cbfd26962b03b8766 Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 11:02:11 +0100 Subject: [PATCH 12/25] refactor: replaced the use case for checking if the account is a light user --- .../src/main/java/com/owncloud/android/MainApp.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt b/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt index 9f520a3e86e..61951578c16 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt @@ -50,6 +50,7 @@ import com.owncloud.android.dependecyinjection.viewModelModule import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.spaces.usecases.GetPersonalSpaceForAccountUseCase +import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase import com.owncloud.android.extensions.createNotificationChannel import com.owncloud.android.lib.common.SingleSessionManager @@ -195,11 +196,15 @@ class MainApp : Application() { } } - val getUserQuotasUseCase: GetUserQuotasUseCase by inject() - val userQuotas = withContext(CoroutineScope(CoroutinesDispatcherProvider().io).coroutineContext) { - getUserQuotasUseCase(Unit) + val getStoredQuotaUseCase: GetStoredQuotaUseCase by inject() + val quota = withContext(CoroutineScope(CoroutinesDispatcherProvider().io).coroutineContext) { + getStoredQuotaUseCase( + GetStoredQuotaUseCase.Params( + accountName = account.name + ) + ) } - val isLightUser = userQuotas.filter { it.accountName == account.name }.any { it.available == -4L } + val isLightUser = quota.getDataOrNull()?.available == -4L spacesAllowed && personalSpace == null && !isLightUser } From 6b06f7a56723db58d2c78e326b80e63bc2f4be25 Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 11:03:02 +0100 Subject: [PATCH 13/25] feat: removed search in toolbar for light users --- .../files/filelist/MainEmptyListFragment.kt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt index 5153314f2ff..da5927ea031 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt @@ -25,7 +25,7 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.appcompat.widget.SearchView +import android.widget.TextView import com.owncloud.android.R import com.owncloud.android.databinding.MainEmptyListFragmentBinding @@ -44,13 +44,16 @@ class MainEmptyListFragment : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - with(binding.emptyDataParent) { + binding.emptyDataParent.apply { listEmptyDatasetIcon.setImageResource(R.drawable.ic_folder) listEmptyDatasetTitle.setText(R.string.file_list_empty_title_all_files) listEmptyDatasetSubTitle.setText(R.string.light_users_subtitle) } - val searchViewRootToolbar = requireActivity().findViewById(R.id.root_toolbar_search_view) - searchViewRootToolbar.queryHint = getString(R.string.actionbar_search) + val titleToolbar = requireActivity().findViewById(R.id.root_toolbar_title) + titleToolbar.apply { + setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0) + titleToolbar.isClickable = false + } } From 35f5fa1c0f8f42b711a007a4a5ca1f201e7b1f8e Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 11:03:52 +0100 Subject: [PATCH 14/25] refactor: removed one unnecessary method from automatic uploads --- .../SettingsPictureUploadsFragment.kt | 20 ++++++------------- .../SettingsVideoUploadsFragment.kt | 20 ++++++------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index c9e8abbd4f2..02a574c3b8b 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -132,10 +132,10 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { } if (availableAccounts.isEmpty()) { - disableFields() + enablePictureUploads(false, true) } else { picturesViewModel.pictureUploads.collect { pictureUploadsConfiguration -> - enablePictureUploads(pictureUploadsConfiguration != null) + enablePictureUploads(pictureUploadsConfiguration != null, false) pictureUploadsConfiguration?.let { prefPictureUploadsAccount?.value = it.accountName prefPictureUploadsPath?.summary = picturesViewModel.getUploadPathString() @@ -241,8 +241,11 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { super.onDestroy() } - private fun enablePictureUploads(value: Boolean) { + private fun enablePictureUploads(value: Boolean, isLightUser: Boolean) { prefEnablePictureUploads?.isChecked = value + if (isLightUser) { + prefEnablePictureUploads?.isEnabled = false + } prefPictureUploadsPath?.isEnabled = value prefPictureUploadsOnWifi?.isEnabled = value prefPictureUploadsOnCharging?.isEnabled = value @@ -262,15 +265,4 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { prefPictureUploadsLastSync?.summary = null } - private fun disableFields() { - prefEnablePictureUploads?.isChecked = false - prefEnablePictureUploads?.isEnabled = false - prefPictureUploadsAccount?.isEnabled = false - prefPictureUploadsPath?.isEnabled = false - prefPictureUploadsSourcePath?.isEnabled = false - prefPictureUploadsOnWifi?.isEnabled = false - prefPictureUploadsOnCharging?.isEnabled = false - prefPictureUploadsBehaviour?.isEnabled = false - prefPictureUploadsLastSync?.isEnabled = false - } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index 1d14aee33cc..6f0b3d9f47e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -129,10 +129,10 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { } if (availableAccounts.isEmpty()) { - disableFields() + enableVideoUploads(false, true) } else { videosViewModel.videoUploads.collect { videoUploadsConfiguration -> - enableVideoUploads(videoUploadsConfiguration != null) + enableVideoUploads(videoUploadsConfiguration != null, false) videoUploadsConfiguration?.let { prefVideoUploadsAccount?.value = it.accountName prefVideoUploadsPath?.summary = videosViewModel.getUploadPathString() @@ -238,8 +238,11 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { super.onDestroy() } - private fun enableVideoUploads(value: Boolean) { + private fun enableVideoUploads(value: Boolean, isLightUser: Boolean) { prefEnableVideoUploads?.isChecked = value + if (isLightUser) { + prefEnableVideoUploads?.isEnabled = false + } prefVideoUploadsPath?.isEnabled = value prefVideoUploadsOnWifi?.isEnabled = value prefVideoUploadsOnCharging?.isEnabled = value @@ -259,15 +262,4 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { prefVideoUploadsLastSync?.summary = null } - private fun disableFields() { - prefEnableVideoUploads?.isChecked = false - prefEnableVideoUploads?.isEnabled = false - prefVideoUploadsAccount?.isEnabled = false - prefVideoUploadsPath?.isEnabled = false - prefVideoUploadsSourcePath?.isEnabled = false - prefVideoUploadsOnWifi?.isEnabled = false - prefVideoUploadsOnCharging?.isEnabled = false - prefVideoUploadsBehaviour?.isEnabled = false - prefVideoUploadsLastSync?.isEnabled = false - } } From 824294e1ccf22084b7011b984899750f2e1b2c38 Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 11:12:29 +0100 Subject: [PATCH 15/25] refactor: moved `ReceiveExternalFilesViewModel` creation (now when the account is set) --- .../main/java/com/owncloud/android/MainApp.kt | 1 - .../dependecyinjection/ViewModelModule.kt | 2 +- .../android/ui/ReceiveExternalFilesViewModel.kt | 17 +++++++++-------- .../activity/ReceiveExternalFilesActivity.java | 13 +++++-------- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt b/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt index 61951578c16..afc5132ab74 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt @@ -51,7 +51,6 @@ import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUs import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.spaces.usecases.GetPersonalSpaceForAccountUseCase import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase -import com.owncloud.android.domain.user.usecases.GetUserQuotasUseCase import com.owncloud.android.extensions.createNotificationChannel import com.owncloud.android.lib.common.SingleSessionManager import com.owncloud.android.presentation.authentication.AccountUtils diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index 5a5abd60001..0dbc8bd07ff 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -97,7 +97,7 @@ val viewModelModule = module { viewModel { AuthenticationViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { MigrationViewModel(MainApp.dataFolder, get(), get(), get(), get(), get(), get(), get()) } viewModel { TransfersViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } - viewModel { ReceiveExternalFilesViewModel(get(), get(), get()) } + viewModel { ReceiveExternalFilesViewModel(get(), get(), get(), get()) } viewModel { (accountName: String, showPersonalSpace: Boolean) -> SpacesListViewModel(get(), get(), get(), get(), get(), accountName, showPersonalSpace) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt index 582c2d5b113..9fccbcf8eeb 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/ReceiveExternalFilesViewModel.kt @@ -35,6 +35,7 @@ class ReceiveExternalFilesViewModel( private val synchronizeFolderUseCase: SynchronizeFolderUseCase, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider, private val getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase, + private val accountName: String, ) : ViewModel() { private val _syncFolderLiveData = MediatorLiveData>>() @@ -43,6 +44,14 @@ class ReceiveExternalFilesViewModel( private val _spacesAreAllowed = MutableLiveData() val spacesAreAllowed: LiveData = _spacesAreAllowed + init { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + val capabilities = getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName)) + val spacesAvailableForAccount = capabilities?.isSpacesAllowed() == true + _spacesAreAllowed.postValue(spacesAvailableForAccount) + } + } + fun refreshFolderUseCase( folderToSync: OCFile, ) = runUseCaseWithResult( @@ -58,12 +67,4 @@ class ReceiveExternalFilesViewModel( ) ) - fun areSpacesAllowed(accountName: String) { - viewModelScope.launch(coroutinesDispatcherProvider.io) { - val capabilities = getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName)) - val spacesAvailableForAccount = capabilities?.isSpacesAllowed() == true - _spacesAreAllowed.postValue(spacesAvailableForAccount) - } - } - } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index a0795fad04c..1e8af8197b5 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -73,7 +73,6 @@ import com.owncloud.android.db.PreferenceManager; import com.owncloud.android.domain.exceptions.UnauthorizedException; import com.owncloud.android.domain.files.model.OCFile; -import com.owncloud.android.domain.spaces.model.OCSpace; import com.owncloud.android.extensions.ActivityExtKt; import com.owncloud.android.extensions.ThrowableExtKt; import com.owncloud.android.lib.common.OwnCloudAccount; @@ -118,6 +117,7 @@ import java.util.regex.Pattern; import static com.owncloud.android.presentation.settings.advanced.SettingsAdvancedFragment.PREF_SHOW_HIDDEN_FILES; +import static org.koin.core.parameter.ParametersHolderKt.parametersOf; import static org.koin.java.KoinJavaComponent.get; import static org.koin.java.KoinJavaComponent.inject; @@ -222,14 +222,11 @@ protected void onCreate(Bundle savedInstanceState) { } mSortOptionsView.setVisibility(View.GONE); - mReceiveExternalFilesViewModel = get(ReceiveExternalFilesViewModel.class); - subscribeToViewModels(); initPickerListener(); } private void subscribeToViewModels() { - mReceiveExternalFilesViewModel.areSpacesAllowed(getAccount().name); mReceiveExternalFilesViewModel.getSpacesAreAllowed().observe(this, spaces -> { areSpacesAllowed = spaces; @@ -339,7 +336,8 @@ protected void setAccount(Account account, boolean savedAccount) { @Override protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(mAccountWasRestored); - mReceiveExternalFilesViewModel.areSpacesAllowed(getAccount().name); + mReceiveExternalFilesViewModel = get(ReceiveExternalFilesViewModel.class, null , () -> parametersOf(getAccount().name)); + subscribeToViewModels(); initTargetFolder(); mReceiveExternalFilesViewModel.getSyncFolderLiveData().observe(this, eventUiResult -> { @@ -362,7 +360,7 @@ protected void onAccountSet(boolean stateWasRecovered) { } else if (uiResult instanceof UIResult.Success) { mSyncInProgress = false; updateDirectoryList(); - if (mParents.size() == 1) { + if (mParents.size() == 1 && !areSpacesAllowed) { updateToolbar(getString(R.string.uploader_top_message)); } if(fragmentContainer.getVisibility() == View.VISIBLE) { @@ -435,7 +433,6 @@ public void onClick(DialogInterface dialog, int which) { onAccountSet(mAccountWasRestored); dialog.dismiss(); PreferenceManager.setLastUploadPath("/", this); - mReceiveExternalFilesViewModel.areSpacesAllowed(getAccount().name); mAccountSelected = true; mAccountSelectionShowing = false; }); @@ -460,7 +457,7 @@ public void onBackPressed() { String full_path = generatePath(mParents); startSyncFolderOperation(getStorageManager().getFileByPath(full_path, currentSpaceId)); updateDirectoryList(); - if (mParents.size() <= 1) { + if (mParents.size() <= 1 && !areSpacesAllowed) { updateToolbar(getString(R.string.uploader_top_message)); } } From 157264e073c9da6f532f70828ecc2b2f7c7243c6 Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 11:25:11 +0100 Subject: [PATCH 16/25] refactor: removed unnecessary code in toolbar for light users --- changelog/unreleased/4518 | 2 +- .../presentation/files/filelist/MainEmptyListFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog/unreleased/4518 b/changelog/unreleased/4518 index 3348ed917f4..5bb03006a0b 100644 --- a/changelog/unreleased/4518 +++ b/changelog/unreleased/4518 @@ -1,6 +1,6 @@ Enhancement: oCIS Light Users -Support for oCIS light users has been added to the app +oCIS light users (users without personal space) are now supported in the app https://github.com/owncloud/android/issues/4490 https://github.com/owncloud/android/pull/4518 diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt index da5927ea031..01a3595105a 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt @@ -52,7 +52,7 @@ class MainEmptyListFragment : Fragment() { val titleToolbar = requireActivity().findViewById(R.id.root_toolbar_title) titleToolbar.apply { setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0) - titleToolbar.isClickable = false + isClickable = false } } From 2b15213e1f0793c51fbab4b3e87872126da7d15f Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 12:01:22 +0100 Subject: [PATCH 17/25] refactor: added identation in `DrawerActivity` --- .../owncloud/android/ui/activity/DrawerActivity.kt | 11 ++++++----- 1 file changed, 6 insertions(+), 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 1df8f805cce..10a17618690 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 @@ -317,17 +317,18 @@ abstract class DrawerActivity : ToolbarActivity() { getAccountQuotaBar()?.isVisible = false getAccountQuotaStatusText()?.isVisible = false } + userQuota.available < 0 -> { // Pending, unknown or unlimited free storage - getAccountQuotaBar()?.apply { + getAccountQuotaBar()?.apply { isVisible = true progress = 0 progressTintList = ColorStateList.valueOf(resources.getColor(R.color.color_accent)) - } - getAccountQuotaText()?.text = String.format( + } + getAccountQuotaText()?.text = String.format( getString(R.string.drawer_unavailable_free_storage), DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) - ) - getAccountQuotaStatusText()?.visibility = View.GONE + ) + getAccountQuotaStatusText()?.visibility = View.GONE } userQuota.available == 0L -> { // Exceeded storage. The value is over 100%. From 1f953dd8e72939d4abb69a13437f1f8bce93f1c8 Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 13:25:15 +0100 Subject: [PATCH 18/25] feat: added a snackbar in automatic uploads for light users and replaced light users by users light in release notes --- .../automaticuploads/SettingsPictureUploadsFragment.kt | 2 ++ .../automaticuploads/SettingsVideoUploadsFragment.kt | 2 ++ owncloudApp/src/main/res/values/strings.xml | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index 02a574c3b8b..3c2d608a0fe 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -51,6 +51,7 @@ import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_PICTURE_UPLOADS_WI import com.owncloud.android.domain.automaticuploads.model.UploadBehavior import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.showAlertDialog +import com.owncloud.android.extensions.showMessageInSnackbar import com.owncloud.android.presentation.accounts.ManageAccountsViewModel import com.owncloud.android.ui.activity.FolderPickerActivity import com.owncloud.android.utils.DisplayUtils @@ -133,6 +134,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { if (availableAccounts.isEmpty()) { enablePictureUploads(false, true) + showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) } else { picturesViewModel.pictureUploads.collect { pictureUploadsConfiguration -> enablePictureUploads(pictureUploadsConfiguration != null, false) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index 6f0b3d9f47e..bc7b5e066b0 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -51,6 +51,7 @@ import com.owncloud.android.db.PreferenceManager.PREF__CAMERA_VIDEO_UPLOADS_WIFI import com.owncloud.android.domain.automaticuploads.model.UploadBehavior import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.showAlertDialog +import com.owncloud.android.extensions.showMessageInSnackbar import com.owncloud.android.presentation.accounts.ManageAccountsViewModel import com.owncloud.android.ui.activity.FolderPickerActivity import com.owncloud.android.utils.DisplayUtils @@ -130,6 +131,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { if (availableAccounts.isEmpty()) { enableVideoUploads(false, true) + showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) } else { videosViewModel.videoUploads.collect { videoUploadsConfiguration -> enableVideoUploads(videoUploadsConfiguration != null, false) diff --git a/owncloudApp/src/main/res/values/strings.xml b/owncloudApp/src/main/res/values/strings.xml index c8fef27a9dd..2bab6878a91 100644 --- a/owncloudApp/src/main/res/values/strings.xml +++ b/owncloudApp/src/main/res/values/strings.xml @@ -78,6 +78,7 @@ Video upload path Upload videos via wifi only Upload videos only when charging + Automatic uploads are not available for users light Advanced Manage notifications Show hidden files @@ -739,8 +740,8 @@ Added feedback when (un)setting av. offline in all previews and updated options menu depending on file status Improvements in storage occupation display Storage occupation refreshed frequently in file and refresh operations and available in accounts manager view, besides of drawer menu - ownCloud Infinite Scale servers light users - ownCloud Infinite Scale servers light users (users without personal space) are now supported in the app + ownCloud Infinite Scale servers users light + ownCloud Infinite Scale servers users light (users without personal space) are now supported in the app From af6ddc67c84c1646d8a6f64f4373bbbc32fcab01 Mon Sep 17 00:00:00 2001 From: joragua Date: Thu, 28 Nov 2024 14:10:21 +0100 Subject: [PATCH 19/25] fix: added a condition to distinguish between `FileDisplayActivity` and `FolderPickerActivity` for refresh operation --- .../presentation/files/filelist/MainFileListFragment.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt index b13b068a2e7..d4ce9415082 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt @@ -316,7 +316,11 @@ class MainFileListFragment : Fragment(), // Observe the current folder displayed collectLatestLifecycleFlow(mainFileListViewModel.currentFolderDisplayed) { currentFolderDisplayed: OCFile -> fileActions?.onCurrentFolderUpdated(currentFolderDisplayed, mainFileListViewModel.getSpace()) - val fileListOption = (requireActivity() as FileDisplayActivity).fileListOption + val fileListOption = if (isPickingAFolder()) { + mainFileListViewModel.fileListOption.value + } else { + (requireActivity() as FileDisplayActivity).fileListOption + } val refreshFolderNeeded = fileListOption.isAllFiles() || (!fileListOption.isAllFiles() && currentFolderDisplayed.remotePath != ROOT_PATH && !fileListOption.isAvailableOffline()) if (refreshFolderNeeded) { From 9832da900b948258380e9add15151da22d4d54f3 Mon Sep 17 00:00:00 2001 From: joragua Date: Fri, 29 Nov 2024 14:46:21 +0100 Subject: [PATCH 20/25] fix: created a new method in 'DrawerViewModel' to avoid navigation when the quota is updated --- .../presentation/common/DrawerViewModel.kt | 10 ++++++++++ .../android/ui/activity/FileDisplayActivity.kt | 15 ++------------- 2 files changed, 12 insertions(+), 13 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 f8301362dff..d942a3add09 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 @@ -41,6 +41,8 @@ import com.owncloud.android.providers.ContextProvider import com.owncloud.android.providers.CoroutinesDispatcherProvider import com.owncloud.android.usecases.accounts.RemoveAccountUseCase import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import timber.log.Timber class DrawerViewModel( @@ -107,4 +109,12 @@ class DrawerViewModel( } } } + + fun checkUserLight(accountName: String): Boolean = runBlocking(CoroutinesDispatcherProvider().io) { + val quota = withContext(CoroutinesDispatcherProvider().io) { + getStoredQuotaUseCase(GetStoredQuotaUseCase.Params(accountName)) + } + quota.getDataOrNull()?.available == -4L + } + } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index d4c8e5ca8a9..797399ff93d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -73,7 +73,6 @@ import com.owncloud.android.domain.files.model.FileListOption import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.model.OCFile.Companion.ROOT_PARENT_ID import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.user.model.UserQuota import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.checkPasscodeEnforced import com.owncloud.android.extensions.collectLatestLifecycleFlow @@ -324,10 +323,9 @@ class FileDisplayActivity : FileActivity(), capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { onCapabilitiesOperationFinish(it) }) + isLightUser = drawerViewModel.checkUserLight(account.name) + navigateTo(fileListOption, initialState = true) - drawerViewModel.userQuota.observe(this, Event.EventObserver { - onUserQuotaOperationFinish(it) - }) } startListeningToOperations() @@ -1522,15 +1520,6 @@ class FileDisplayActivity : FileActivity(), } } - private fun onUserQuotaOperationFinish(uiResult: UIResult) { - if (uiResult is UIResult.Success) { - if (uiResult.data?.available == -4L) { - isLightUser = true - } - } - navigateTo(fileListOption, initialState = true) - } - override fun onSavedCertificate() { startSyncFolderOperation(currentDir, false) } From 98ebf7c9a85258d2b4a2429c422b483f0440be6f Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 2 Dec 2024 11:30:52 +0100 Subject: [PATCH 21/25] fix: set an account by default when automatic uploads are enabled --- .../SettingsPictureUploadsFragment.kt | 6 +++-- .../SettingsPictureUploadsViewModel.kt | 22 +++++++++---------- .../SettingsVideoUploadsFragment.kt | 6 +++-- .../SettingsVideoUploadsViewModel.kt | 22 +++++++++---------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index 3c2d608a0fe..3bc89b18374 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -74,6 +74,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { private var prefPictureUploadsAccount: ListPreference? = null private var prefPictureUploadsLastSync: Preference? = null private var spaceId: String? = null + private lateinit var selectedAccount: String private val selectPictureUploadsPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -136,10 +137,11 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { enablePictureUploads(false, true) showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) } else { + selectedAccount = availableAccounts.first().accountName picturesViewModel.pictureUploads.collect { pictureUploadsConfiguration -> enablePictureUploads(pictureUploadsConfiguration != null, false) pictureUploadsConfiguration?.let { - prefPictureUploadsAccount?.value = it.accountName + prefPictureUploadsAccount?.value = selectedAccount prefPictureUploadsPath?.summary = picturesViewModel.getUploadPathString() prefPictureUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) prefPictureUploadsOnWifi?.isChecked = it.wifiOnly @@ -160,7 +162,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { val value = newValue as Boolean if (value) { - picturesViewModel.enablePictureUploads() + picturesViewModel.enablePictureUploads(selectedAccount) showAlertDialog( title = getString(R.string.common_important), message = getString(R.string.proper_pics_folder_warning_camera_upload) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt index 8b1443d5439..beedf878cc3 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsViewModel.kt @@ -82,20 +82,18 @@ class SettingsPictureUploadsViewModel( } } - fun enablePictureUploads() { - // Use current account as default. It should never be null. If no accounts are attached, picture uploads are hidden - accountProvider.getCurrentOwnCloudAccount()?.name?.let { name -> - viewModelScope.launch(coroutinesDispatcherProvider.io) { - getPersonalSpaceForAccount(name) - savePictureUploadsConfigurationUseCase( - SavePictureUploadsConfigurationUseCase.Params( - composePictureUploadsConfiguration( - accountName = name, - spaceId = pictureUploadsSpace?.id, - ) + fun enablePictureUploads(accountName: String) { + // Use selected account as default. + viewModelScope.launch(coroutinesDispatcherProvider.io) { + getPersonalSpaceForAccount(accountName) + savePictureUploadsConfigurationUseCase( + SavePictureUploadsConfigurationUseCase.Params( + composePictureUploadsConfiguration( + accountName = accountName, + spaceId = pictureUploadsSpace?.id, ) ) - } + ) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index bc7b5e066b0..ba09630b7f6 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -74,6 +74,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { private var prefVideoUploadsAccount: ListPreference? = null private var prefVideoUploadsLastSync: Preference? = null private var spaceId: String? = null + private lateinit var selectedAccount: String private val selectVideoUploadsPathLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -133,10 +134,11 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { enableVideoUploads(false, true) showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) } else { + selectedAccount = availableAccounts.first().accountName videosViewModel.videoUploads.collect { videoUploadsConfiguration -> enableVideoUploads(videoUploadsConfiguration != null, false) videoUploadsConfiguration?.let { - prefVideoUploadsAccount?.value = it.accountName + prefVideoUploadsAccount?.value = selectedAccount prefVideoUploadsPath?.summary = videosViewModel.getUploadPathString() prefVideoUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) prefVideoUploadsOnWifi?.isChecked = it.wifiOnly @@ -157,7 +159,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { val value = newValue as Boolean if (value) { - videosViewModel.enableVideoUploads() + videosViewModel.enableVideoUploads(selectedAccount) showAlertDialog( title = getString(R.string.common_important), message = getString(R.string.proper_videos_folder_warning_camera_upload) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt index 8a76cb1d2d9..89d88041c48 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsViewModel.kt @@ -82,20 +82,18 @@ class SettingsVideoUploadsViewModel( } } - fun enableVideoUploads() { - // Use current account as default. It should never be null. If no accounts are attached, video uploads are hidden - accountProvider.getCurrentOwnCloudAccount()?.name?.let { name -> - viewModelScope.launch(coroutinesDispatcherProvider.io) { - getPersonalSpaceForAccount(name) - saveVideoUploadsConfigurationUseCase( - SaveVideoUploadsConfigurationUseCase.Params( - composeVideoUploadsConfiguration( - accountName = name, - spaceId = videoUploadsSpace?.id, - ) + fun enableVideoUploads(accountName: String) { + // Use selected account as default. + viewModelScope.launch(coroutinesDispatcherProvider.io) { + getPersonalSpaceForAccount(accountName) + saveVideoUploadsConfigurationUseCase( + SaveVideoUploadsConfigurationUseCase.Params( + composeVideoUploadsConfiguration( + accountName = accountName, + spaceId = videoUploadsSpace?.id, ) ) - } + ) } } From 70e6e988ab98dec90ba653db1e7d07f380afddbf Mon Sep 17 00:00:00 2001 From: joragua Date: Mon, 2 Dec 2024 10:31:30 +0000 Subject: [PATCH 22/25] docs: calens changelog updated --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 808857c754b..cd9ba114fc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ ownCloud admins and users. * Enhancement - Quota improvements from GraphAPI: [#4411](https://github.com/owncloud/android/issues/4411) * Enhancement - Upgraded AGP version to 8.7.2: [#4478](https://github.com/owncloud/android/issues/4478) * 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) ## Details @@ -94,6 +95,13 @@ ownCloud admins and users. https://github.com/owncloud/android/issues/4484 https://github.com/owncloud/android/pull/4498 +* Enhancement - OCIS Light Users: [#4490](https://github.com/owncloud/android/issues/4490) + + OCIS light users (users without personal space) are now supported in the app + + https://github.com/owncloud/android/issues/4490 + https://github.com/owncloud/android/pull/4518 + * Enhancement - Enforce OIDC auth flow via branding: [#4500](https://github.com/owncloud/android/issues/4500) A new branded parameter `enforce_oidc` has been added to enforce the app to From 1c1329916f1ef821d70e04c49db5e25dc7cdfa7a Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 3 Dec 2024 10:22:24 +0100 Subject: [PATCH 23/25] refactor: moved `checkUserLight` method to `ManageAccountsViewModel` and removed unnecessary check for refresh operation --- .../presentation/accounts/ManageAccountsViewModel.kt | 11 +++++++++++ .../android/presentation/common/DrawerViewModel.kt | 7 ------- .../files/filelist/MainFileListFragment.kt | 8 ++------ .../android/ui/activity/FileDisplayActivity.kt | 5 +++-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsViewModel.kt index 88fd5a64d8f..283918cc137 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/accounts/ManageAccountsViewModel.kt @@ -29,6 +29,7 @@ import androidx.lifecycle.viewModelScope import com.owncloud.android.domain.user.model.UserQuota import com.owncloud.android.domain.automaticuploads.model.AutomaticUploadsConfiguration import com.owncloud.android.domain.automaticuploads.usecases.GetAutomaticUploadsConfigurationUseCase +import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase import com.owncloud.android.domain.user.usecases.GetUserQuotasAsStreamUseCase import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.ViewModelExt.runUseCaseWithResult @@ -40,11 +41,14 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext class ManageAccountsViewModel( private val accountProvider: AccountProvider, private val removeLocalFilesForAccountUseCase: RemoveLocalFilesForAccountUseCase, private val getAutomaticUploadsConfigurationUseCase: GetAutomaticUploadsConfigurationUseCase, + private val getStoredQuotaUseCase: GetStoredQuotaUseCase, getUserQuotasAsStreamUseCase: GetUserQuotasAsStreamUseCase, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider, ) : ViewModel() { @@ -84,4 +88,11 @@ class ManageAccountsViewModel( return accountName == automaticUploadsConfiguration?.pictureUploadsConfiguration?.accountName || accountName == automaticUploadsConfiguration?.videoUploadsConfiguration?.accountName } + + fun checkUserLight(accountName: String): Boolean = runBlocking(CoroutinesDispatcherProvider().io) { + val quota = withContext(CoroutinesDispatcherProvider().io) { + getStoredQuotaUseCase(GetStoredQuotaUseCase.Params(accountName)) + } + quota.getDataOrNull()?.available == -4L + } } 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 d942a3add09..87650242790 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 @@ -110,11 +110,4 @@ class DrawerViewModel( } } - fun checkUserLight(accountName: String): Boolean = runBlocking(CoroutinesDispatcherProvider().io) { - val quota = withContext(CoroutinesDispatcherProvider().io) { - getStoredQuotaUseCase(GetStoredQuotaUseCase.Params(accountName)) - } - quota.getDataOrNull()?.available == -4L - } - } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt index d4ce9415082..ee9abe69faf 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListFragment.kt @@ -316,11 +316,7 @@ class MainFileListFragment : Fragment(), // Observe the current folder displayed collectLatestLifecycleFlow(mainFileListViewModel.currentFolderDisplayed) { currentFolderDisplayed: OCFile -> fileActions?.onCurrentFolderUpdated(currentFolderDisplayed, mainFileListViewModel.getSpace()) - val fileListOption = if (isPickingAFolder()) { - mainFileListViewModel.fileListOption.value - } else { - (requireActivity() as FileDisplayActivity).fileListOption - } + val fileListOption = mainFileListViewModel.fileListOption.value val refreshFolderNeeded = fileListOption.isAllFiles() || (!fileListOption.isAllFiles() && currentFolderDisplayed.remotePath != ROOT_PATH && !fileListOption.isAvailableOffline()) if (refreshFolderNeeded) { @@ -814,9 +810,9 @@ class MainFileListFragment : Fragment(), } fun updateFileListOption(newFileListOption: FileListOption, file: OCFile) { - mainFileListViewModel.updateFolderToDisplay(file) mainFileListViewModel.updateFileListOption(newFileListOption) binding.swipeRefreshMainFileList.isEnabled = newFileListOption != FileListOption.AV_OFFLINE + mainFileListViewModel.updateFolderToDisplay(file) showOrHideFab(newFileListOption, file) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 797399ff93d..ef9ad591db4 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -92,6 +92,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode import com.owncloud.android.lib.resources.status.OwnCloudVersion import com.owncloud.android.operations.SyncProfileOperation +import com.owncloud.android.presentation.accounts.ManageAccountsViewModel import com.owncloud.android.presentation.authentication.AccountUtils.getCurrentOwnCloudAccount import com.owncloud.android.presentation.capabilities.CapabilityViewModel import com.owncloud.android.presentation.common.DrawerViewModel @@ -187,7 +188,7 @@ class FileDisplayActivity : FileActivity(), private val fileOperationsViewModel: FileOperationsViewModel by viewModel() private val transfersViewModel: TransfersViewModel by viewModel() private lateinit var spacesListViewModel: SpacesListViewModel - private val drawerViewModel: DrawerViewModel by viewModel() + private val manageAccountsViewModel: ManageAccountsViewModel by viewModel() private val sharedPreferences: SharedPreferencesProvider by inject() @@ -323,7 +324,7 @@ class FileDisplayActivity : FileActivity(), capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { onCapabilitiesOperationFinish(it) }) - isLightUser = drawerViewModel.checkUserLight(account.name) + isLightUser = manageAccountsViewModel.checkUserLight(account.name) navigateTo(fileListOption, initialState = true) } From 4f3409b0ba57d4b20485da47a278e751d67b5833 Mon Sep 17 00:00:00 2001 From: joragua Date: Tue, 3 Dec 2024 10:39:11 +0100 Subject: [PATCH 24/25] fix: set the correct account for automatic uploads when there are users light --- .../android/presentation/common/DrawerViewModel.kt | 2 -- .../automaticuploads/SettingsPictureUploadsFragment.kt | 10 +++++++++- .../automaticuploads/SettingsVideoUploadsFragment.kt | 10 +++++++++- .../android/ui/activity/FileDisplayActivity.kt | 1 - 4 files changed, 18 insertions(+), 5 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 87650242790..01ba4de8ce8 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 @@ -41,8 +41,6 @@ import com.owncloud.android.providers.ContextProvider import com.owncloud.android.providers.CoroutinesDispatcherProvider import com.owncloud.android.usecases.accounts.RemoveAccountUseCase import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import timber.log.Timber class DrawerViewModel( diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index 3bc89b18374..cb7a19de19b 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -137,7 +137,15 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { enablePictureUploads(false, true) showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) } else { - selectedAccount = availableAccounts.first().accountName + val currentAccount = manageAccountsViewModel.getCurrentAccount()?.name + currentAccount?.let { + selectedAccount = if (manageAccountsViewModel.checkUserLight(currentAccount)) { + availableAccounts.first().accountName + } else { + currentAccount + } + } + picturesViewModel.pictureUploads.collect { pictureUploadsConfiguration -> enablePictureUploads(pictureUploadsConfiguration != null, false) pictureUploadsConfiguration?.let { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index ba09630b7f6..5adb6ff0dc2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -134,7 +134,15 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { enableVideoUploads(false, true) showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) } else { - selectedAccount = availableAccounts.first().accountName + val currentAccount = manageAccountsViewModel.getCurrentAccount()?.name + currentAccount?.let { + selectedAccount = if (manageAccountsViewModel.checkUserLight(currentAccount)) { + availableAccounts.first().accountName + } else { + currentAccount + } + } + videosViewModel.videoUploads.collect { videoUploadsConfiguration -> enableVideoUploads(videoUploadsConfiguration != null, false) videoUploadsConfiguration?.let { diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index ef9ad591db4..74319110fa8 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -95,7 +95,6 @@ import com.owncloud.android.operations.SyncProfileOperation import com.owncloud.android.presentation.accounts.ManageAccountsViewModel import com.owncloud.android.presentation.authentication.AccountUtils.getCurrentOwnCloudAccount import com.owncloud.android.presentation.capabilities.CapabilityViewModel -import com.owncloud.android.presentation.common.DrawerViewModel import com.owncloud.android.presentation.common.UIResult import com.owncloud.android.presentation.conflicts.ConflictsResolveActivity import com.owncloud.android.presentation.files.details.FileDetailsFragment From 7908a14bfe138d5121b046733b90376416fb1f61 Mon Sep 17 00:00:00 2001 From: joragua Date: Wed, 4 Dec 2024 13:15:34 +0100 Subject: [PATCH 25/25] fix: set the correct backup account in UI and not the current one --- .../settings/automaticuploads/SettingsPictureUploadsFragment.kt | 2 +- .../settings/automaticuploads/SettingsVideoUploadsFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index cb7a19de19b..730108e8132 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt @@ -149,7 +149,7 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { picturesViewModel.pictureUploads.collect { pictureUploadsConfiguration -> enablePictureUploads(pictureUploadsConfiguration != null, false) pictureUploadsConfiguration?.let { - prefPictureUploadsAccount?.value = selectedAccount + prefPictureUploadsAccount?.value = it.accountName prefPictureUploadsPath?.summary = picturesViewModel.getUploadPathString() prefPictureUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) prefPictureUploadsOnWifi?.isChecked = it.wifiOnly diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt index 5adb6ff0dc2..9d1f39b2b45 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsVideoUploadsFragment.kt @@ -146,7 +146,7 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { videosViewModel.videoUploads.collect { videoUploadsConfiguration -> enableVideoUploads(videoUploadsConfiguration != null, false) videoUploadsConfiguration?.let { - prefVideoUploadsAccount?.value = selectedAccount + prefVideoUploadsAccount?.value = it.accountName prefVideoUploadsPath?.summary = videosViewModel.getUploadPathString() prefVideoUploadsSourcePath?.summary = DisplayUtils.getPathWithoutLastSlash(it.sourcePath.toUri().path) prefVideoUploadsOnWifi?.isChecked = it.wifiOnly