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 diff --git a/changelog/unreleased/4518 b/changelog/unreleased/4518 new file mode 100644 index 00000000000..5bb03006a0b --- /dev/null +++ b/changelog/unreleased/4518 @@ -0,0 +1,6 @@ +Enhancement: oCIS Light Users + +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/MainApp.kt b/owncloudApp/src/main/java/com/owncloud/android/MainApp.kt index f0c30dc2e51..afc5132ab74 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.extensions.createNotificationChannel import com.owncloud.android.lib.common.SingleSessionManager import com.owncloud.android.presentation.authentication.AccountUtils @@ -194,7 +195,17 @@ class MainApp : Application() { } } - spacesAllowed && personalSpace == null + val getStoredQuotaUseCase: GetStoredQuotaUseCase by inject() + val quota = withContext(CoroutineScope(CoroutinesDispatcherProvider().io).coroutineContext) { + getStoredQuotaUseCase( + GetStoredQuotaUseCase.Params( + accountName = account.name + ) + ) + } + val isLightUser = quota.getDataOrNull()?.available == -4L + + spacesAllowed && personalSpace == null && !isLightUser } override fun onActivityStarted(activity: Activity) { 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/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/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 7afe0d49191..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 @@ -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, @@ -105,4 +107,5 @@ class DrawerViewModel( } } } + } 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..01a3595105a --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainEmptyListFragment.kt @@ -0,0 +1,60 @@ +/** + * 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 android.widget.TextView +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?) { + 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 titleToolbar = requireActivity().findViewById(R.id.root_toolbar_title) + titleToolbar.apply { + setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0) + isClickable = false + } + } + + +} 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..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 @@ -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( @@ -317,7 +318,7 @@ class MainFileListFragment : Fragment(), fileActions?.onCurrentFolderUpdated(currentFolderDisplayed, mainFileListViewModel.getSpace()) val fileListOption = mainFileListViewModel.fileListOption.value 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() } @@ -810,8 +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/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/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/settings/automaticuploads/SettingsPictureUploadsFragment.kt index edb74b12ca4..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 @@ -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,10 @@ 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.extensions.showMessageInSnackbar +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 +63,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 @@ -69,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 -> @@ -103,10 +109,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 +126,40 @@ 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()) { + enablePictureUploads(false, true) + showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) + } else { + 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 { + 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() + } + } } } } @@ -145,7 +170,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) @@ -228,8 +253,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 @@ -248,4 +276,5 @@ class SettingsPictureUploadsFragment : PreferenceFragmentCompat() { prefPictureUploadsBehaviour?.value = UploadBehavior.COPY.name prefPictureUploadsLastSync?.summary = null } + } 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..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 @@ -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, @@ -81,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, ) ) - } + ) } } @@ -124,8 +123,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..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 @@ -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,10 @@ 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.extensions.showMessageInSnackbar +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 +63,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 @@ -69,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 -> @@ -100,10 +106,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 +123,40 @@ 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()) { + enableVideoUploads(false, true) + showMessageInSnackbar(getString(R.string.prefs_automatic_uploads_not_available_users_light)) + } else { + 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 { + 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() + } + } } } } @@ -142,7 +167,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) @@ -225,8 +250,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 @@ -245,4 +273,5 @@ class SettingsVideoUploadsFragment : PreferenceFragmentCompat() { prefVideoUploadsBehaviour?.value = UploadBehavior.COPY.name prefVideoUploadsLastSync?.summary = null } + } 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..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 @@ -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, @@ -81,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, ) ) - } + ) } } @@ -124,8 +123,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 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..9fccbcf8eeb 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,23 @@ import kotlinx.coroutines.launch class ReceiveExternalFilesViewModel( private val synchronizeFolderUseCase: SynchronizeFolderUseCase, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider, - private val getPersonalSpaceForAccountUseCase: GetPersonalSpaceForAccountUseCase, + private val getStoredCapabilitiesUseCase: GetStoredCapabilitiesUseCase, + private val accountName: String, ) : 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 + init { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + val capabilities = getStoredCapabilitiesUseCase(GetStoredCapabilitiesUseCase.Params(accountName)) + val spacesAvailableForAccount = capabilities?.isSpacesAllowed() == true + _spacesAreAllowed.postValue(spacesAvailableForAccount) + } + } fun refreshFolderUseCase( folderToSync: OCFile, @@ -60,16 +67,4 @@ class ReceiveExternalFilesViewModel( ) ) - fun getPersonalSpaceForAccount(accountName: String) { - viewModelScope.launch(coroutinesDispatcherProvider.io) { - val result = getPersonalSpaceForAccountUseCase( - GetPersonalSpaceForAccountUseCase.Params( - accountName = accountName - ) - ) - _personalSpaceLiveData.postValue(result) - } - } - - } 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..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 @@ -312,15 +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)) + 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) + getString(R.string.drawer_unavailable_free_storage), + DisplayUtils.bytesToHumanReadable(userQuota.used, this, true) ) getAccountQuotaStatusText()?.visibility = View.GONE } 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..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 @@ -92,11 +92,13 @@ 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.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 @@ -176,7 +178,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 @@ -185,6 +187,7 @@ class FileDisplayActivity : FileActivity(), private val fileOperationsViewModel: FileOperationsViewModel by viewModel() private val transfersViewModel: TransfersViewModel by viewModel() private lateinit var spacesListViewModel: SpacesListViewModel + private val manageAccountsViewModel: ManageAccountsViewModel by viewModel() private val sharedPreferences: SharedPreferencesProvider by inject() @@ -193,6 +196,8 @@ class FileDisplayActivity : FileActivity(), private lateinit var binding: ActivityMainBinding + private var isLightUser = false + override fun onCreate(savedInstanceState: Bundle?) { Timber.v("onCreate() start") @@ -318,7 +323,9 @@ class FileDisplayActivity : FileActivity(), capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { onCapabilitiesOperationFinish(it) }) + isLightUser = manageAccountsViewModel.checkUserLight(account.name) navigateTo(fileListOption, initialState = true) + } startListeningToOperations() @@ -422,6 +429,14 @@ class FileDisplayActivity : FileActivity(), transaction.commit() } + private fun initAndShowEmptyPersonalSpace() { + 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 @@ -1743,13 +1758,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) + initAndShowEmptyPersonalSpace() + 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/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index ea2c2e2947e..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 @@ -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. @@ -72,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; @@ -117,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; @@ -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("[/\\\\]"); @@ -221,17 +222,15 @@ protected void onCreate(Bundle savedInstanceState) { } mSortOptionsView.setVisibility(View.GONE); - mReceiveExternalFilesViewModel = get(ReceiveExternalFilesViewModel.class); - subscribeToViewModels(); initPickerListener(); } private void subscribeToViewModels() { - mReceiveExternalFilesViewModel.getPersonalSpaceLiveData().observe(this, ocSpace -> { - personalSpace = ocSpace; + mReceiveExternalFilesViewModel.getSpacesAreAllowed().observe(this, spaces -> { + areSpacesAllowed = spaces; - if (personalSpace == null) { // OC10 Server + if (!areSpacesAllowed) { // OC10 Server showListOfFiles(); showRetainerFragment(); updateDirectoryList(); @@ -337,7 +336,8 @@ protected void setAccount(Account account, boolean savedAccount) { @Override protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(mAccountWasRestored); - mReceiveExternalFilesViewModel.getPersonalSpaceForAccount(getAccount().name); + mReceiveExternalFilesViewModel = get(ReceiveExternalFilesViewModel.class, null , () -> parametersOf(getAccount().name)); + subscribeToViewModels(); initTargetFolder(); mReceiveExternalFilesViewModel.getSyncFolderLiveData().observe(this, eventUiResult -> { @@ -360,7 +360,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 && !areSpacesAllowed) { updateToolbar(getString(R.string.uploader_top_message)); } if(fragmentContainer.getVisibility() == View.VISIBLE) { @@ -433,7 +433,6 @@ public void onClick(DialogInterface dialog, int which) { onAccountSet(mAccountWasRestored); dialog.dismiss(); PreferenceManager.setLastUploadPath("/", this); - mReceiveExternalFilesViewModel.getPersonalSpaceForAccount(getAccount().name); mAccountSelected = true; mAccountSelectionShowing = false; }); @@ -458,7 +457,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 && !areSpacesAllowed) { updateToolbar(getString(R.string.uploader_top_message)); } } 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..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 @@ -165,6 +166,7 @@ No available offline files No shared links Upload some content or sync with your devices! + Personal space is not available No shares You are not collaborating on other people\'s resources. You don\'t have access to any space! @@ -738,6 +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 users light + ownCloud Infinite Scale servers users light (users without personal space) are now supported in the app 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() } 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..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 @@ -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?