From cd827fb8279ee60b316b7f340ed00d6885bfc986 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Fri, 23 Dec 2022 14:49:43 +0100 Subject: [PATCH] Added new fragment for spaces list and empty view --- .../android/extensions/FileListOptionExt.kt | 3 + .../files/filelist/MainFileListViewModel.kt | 5 ++ .../presentation/spaces/SpacesListFragment.kt | 63 +++++++++++++++ .../android/ui/activity/DrawerActivity.kt | 30 +++++++ .../android/ui/activity/FileActivity.java | 6 ++ .../ui/activity/FileDisplayActivity.kt | 40 ++++++++-- .../src/main/res/drawable/ic_spaces.xml | 9 +++ .../main/res/layout/spaces_list_fragment.xml | 78 +++++++++++++++++++ .../src/main/res/menu/bottom_navbar_menu.xml | 5 ++ owncloudApp/src/main/res/values/strings.xml | 5 ++ .../data/files/repository/OCFileRepository.kt | 1 + .../domain/capabilities/model/OCCapability.kt | 4 + .../domain/files/model/FileListOption.kt | 3 +- 13 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt create mode 100644 owncloudApp/src/main/res/drawable/ic_spaces.xml create mode 100644 owncloudApp/src/main/res/layout/spaces_list_fragment.xml diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/FileListOptionExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/FileListOptionExt.kt index e0f6e1109da..02fc881793d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/FileListOptionExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/FileListOptionExt.kt @@ -26,6 +26,7 @@ import com.owncloud.android.domain.files.model.FileListOption @StringRes fun FileListOption.toTitleStringRes(): Int = when (this) { FileListOption.ALL_FILES -> R.string.file_list_empty_title_all_files + FileListOption.SPACES_LIST -> R.string.spaces_list_empty_title FileListOption.SHARED_BY_LINK -> R.string.file_list_empty_title_shared_by_links FileListOption.AV_OFFLINE -> R.string.file_list_empty_title_available_offline } @@ -33,6 +34,7 @@ fun FileListOption.toTitleStringRes(): Int = when (this) { @StringRes fun FileListOption.toSubtitleStringRes(): Int = when (this) { FileListOption.ALL_FILES -> R.string.file_list_empty_subtitle_all_files + FileListOption.SPACES_LIST -> R.string.spaces_list_empty_subtitle FileListOption.SHARED_BY_LINK -> R.string.file_list_empty_subtitle_shared_by_links FileListOption.AV_OFFLINE -> R.string.file_list_empty_subtitle_available_offline } @@ -40,6 +42,7 @@ fun FileListOption.toSubtitleStringRes(): Int = when (this) { @DrawableRes fun FileListOption.toDrawableRes(): Int = when (this) { FileListOption.ALL_FILES -> R.drawable.ic_folder + FileListOption.SPACES_LIST -> R.drawable.ic_spaces FileListOption.SHARED_BY_LINK -> R.drawable.ic_shared_by_link FileListOption.AV_OFFLINE -> R.drawable.ic_available_offline } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt index 55e2b678b58..5e4fcb11ee7 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/files/filelist/MainFileListViewModel.kt @@ -182,6 +182,10 @@ class MainFileListViewModel( getFileByRemotePathUseCase.execute(GetFileByRemotePathUseCase.Params(fileById.owner, ROOT_PATH)).getDataOrNull() } else fileById } + FileListOption.SPACES_LIST -> { + // TODO: Spaces is not applicable here at the moment + parentDir = TODO() + } } } else if (parentId == ROOT_PARENT_ID.toLong()) { // Browsing to parent folder. Root @@ -231,6 +235,7 @@ class MainFileListViewModel( FileListOption.ALL_FILES -> retrieveFlowForAllFiles(currentFolderDisplayed, accountName) FileListOption.SHARED_BY_LINK -> retrieveFlowForShareByLink(currentFolderDisplayed, accountName) FileListOption.AV_OFFLINE -> retrieveFlowForAvailableOffline(currentFolderDisplayed, accountName) + FileListOption.SPACES_LIST -> TODO() }.toFileListUiState( currentFolderDisplayed, accountName, diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt new file mode 100644 index 00000000000..8f8f7ed28f4 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt @@ -0,0 +1,63 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 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.spaces + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import com.owncloud.android.databinding.SpacesListFragmentBinding +import com.owncloud.android.domain.files.model.FileListOption +import com.owncloud.android.extensions.toDrawableRes +import com.owncloud.android.extensions.toSubtitleStringRes +import com.owncloud.android.extensions.toTitleStringRes + +class SpacesListFragment : Fragment() { + private var _binding: SpacesListFragmentBinding? = null + private val binding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = SpacesListFragmentBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + showOrHideEmptyView() + } + + // TODO: Use this method only when necessary, for the moment the empty view is shown always + private fun showOrHideEmptyView() { + binding.recyclerSpacesList.isVisible = false + + with(binding.emptyDataParent) { + root.isVisible = true + listEmptyDatasetIcon.setImageResource(FileListOption.SPACES_LIST.toDrawableRes()) + listEmptyDatasetTitle.setText(FileListOption.SPACES_LIST.toTitleStringRes()) + listEmptyDatasetSubTitle.setText(FileListOption.SPACES_LIST.toSubtitleStringRes()) + } + } +} 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 dacc92ed51e..c6eb12501b0 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 @@ -43,6 +43,7 @@ import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.widget.AppCompatImageView import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.GravityCompat +import androidx.core.view.get import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout.DrawerListener @@ -51,7 +52,9 @@ import com.google.android.material.navigation.NavigationView import com.owncloud.android.BuildConfig import com.owncloud.android.MainApp.Companion.initDependencyInjection import com.owncloud.android.R +import com.owncloud.android.domain.capabilities.model.OCCapability import com.owncloud.android.domain.files.model.FileListOption +import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.goToUrl import com.owncloud.android.extensions.openPrivacyPolicy import com.owncloud.android.extensions.sendEmail @@ -63,9 +66,11 @@ import com.owncloud.android.presentation.avatar.AvatarUtils import com.owncloud.android.presentation.accounts.AccountsManagementActivity import com.owncloud.android.presentation.accounts.AccountsManagementActivity.Companion.KEY_ACCOUNT_LIST_CHANGED import com.owncloud.android.presentation.accounts.AccountsManagementActivity.Companion.KEY_CURRENT_ACCOUNT_CHANGED +import com.owncloud.android.presentation.capabilities.CapabilityViewModel import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.PreferenceUtils import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf import timber.log.Timber import kotlin.math.ceil @@ -76,6 +81,11 @@ import kotlin.math.ceil abstract class DrawerActivity : ToolbarActivity() { private val drawerViewModel by viewModel() + private val capabilitiesViewModel by viewModel { + parametersOf( + account?.name + ) + } private var menuAccountAvatarRadiusDimension = 0f private var currentAccountAvatarRadiusDimension = 0f @@ -213,6 +223,19 @@ abstract class DrawerActivity : ToolbarActivity() { open fun setupNavigationBottomBar(menuItemId: Int) { // Allow or disallow touches with other visible windows getBottomNavigationView()?.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(this) + if (account != null) { + capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { uiResult: UIResult -> + if (uiResult is UIResult.Success) { + val capabilities = uiResult.data + if (capabilities?.isSpacesAllowed() == true) { + getBottomNavigationView()?.menu?.get(0)?.title = getString(R.string.bottom_nav_personal) + getBottomNavigationView()?.menu?.get(1)?.isVisible = capabilities.isSpacesProjectsAllowed() + } else { + getBottomNavigationView()?.menu?.get(0)?.title = getString(R.string.bottom_nav_files) + } + } + }) + } setCheckedItemAtBottomBar(menuItemId) getBottomNavigationView()?.setOnNavigationItemSelectedListener { menuItem: MenuItem -> bottomBarNavigationTo(menuItem.itemId, getBottomNavigationView()?.selectedItemId == menuItem.itemId) @@ -223,6 +246,7 @@ abstract class DrawerActivity : ToolbarActivity() { private fun bottomBarNavigationTo(menuItemId: Int, isCurrentOptionActive: Boolean) { when (menuItemId) { R.id.nav_all_files -> navigateToOption(FileListOption.ALL_FILES) + R.id.nav_spaces -> navigateToOption(FileListOption.SPACES_LIST) R.id.nav_uploads -> if (!isCurrentOptionActive) { val uploadListIntent = Intent(applicationContext, UploadListActivity::class.java) uploadListIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) @@ -680,6 +704,12 @@ abstract class DrawerActivity : ToolbarActivity() { */ protected abstract fun restart() + /** + * Checks if the spaces tab is currently selected + */ + protected fun isSpacesTabSelected() = + getBottomNavigationView()?.menu?.findItem(R.id.nav_spaces)?.isChecked == true + companion object { private const val KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE" private const val KEY_CHECKED_MENU_ITEM = "CHECKED_MENU_ITEM" diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 6ca0ec51274..e060ad6a30a 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -415,6 +415,12 @@ public void navigateToOption(FileListOption fileListOption) { case ALL_FILES: restart(); break; + case SPACES_LIST: + intent = new Intent(this, FileDisplayActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.putExtra(EXTRA_FILE_LIST_OPTION, (Parcelable) FileListOption.SPACES_LIST); + startActivity(intent); + break; case SHARED_BY_LINK: intent = new Intent(this, FileDisplayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 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 3067a0fbaee..6bfa73d2bb2 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 @@ -84,6 +84,7 @@ import com.owncloud.android.presentation.files.operations.FileOperation import com.owncloud.android.presentation.files.operations.FileOperationsViewModel import com.owncloud.android.presentation.security.bayPassUnlockOnce import com.owncloud.android.presentation.capabilities.CapabilityViewModel +import com.owncloud.android.presentation.spaces.SpacesListFragment import com.owncloud.android.presentation.transfers.TransfersViewModel import com.owncloud.android.providers.WorkManagerProvider import com.owncloud.android.syncadapter.FileSyncAdapter @@ -142,6 +143,9 @@ class FileDisplayActivity : FileActivity(), private val listMainFileFragment: MainFileListFragment? get() = supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FILES) as MainFileListFragment? + private val spacesListFragment: SpacesListFragment? + get() = supportFragmentManager.findFragmentByTag(TAG_LIST_OF_SPACES) as SpacesListFragment? + private val secondFragment: FileFragment? get() = supportFragmentManager.findFragmentByTag(TAG_SECOND_FRAGMENT) as FileFragment? @@ -253,7 +257,11 @@ class FileDisplayActivity : FileActivity(), capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { onCapabilitiesOperationFinish(it) }) - initAndShowListOfFiles() + if (isSpacesTabSelected()) { + initAndShowListOfSpaces() + } else { + initAndShowListOfFiles() + } } startListeningToOperations() @@ -327,7 +335,14 @@ class FileDisplayActivity : FileActivity(), setSearchListener(findViewById(R.id.root_toolbar_search_view)) } val transaction = supportFragmentManager.beginTransaction() - transaction.add(R.id.left_fragment_container, mainListOfFiles, TAG_LIST_OF_FILES) + transaction.replace(R.id.left_fragment_container, mainListOfFiles, TAG_LIST_OF_FILES) + transaction.commit() + } + + private fun initAndShowListOfSpaces() { + val listOfSpaces = SpacesListFragment() + val transaction = supportFragmentManager.beginTransaction() + transaction.replace(R.id.left_fragment_container, listOfSpaces, TAG_LIST_OF_SPACES) transaction.commit() } @@ -684,7 +699,9 @@ class FileDisplayActivity : FileActivity(), super.onResume() setCheckedItemAtBottomBar(getMenuItemForFileListOption(fileListOption)) - listMainFileFragment?.updateFileListOption(fileListOption, file) + if (!isSpacesTabSelected()) { + listMainFileFragment?.updateFileListOption(fileListOption, file) + } // refresh list of files refreshListOfFilesFragment() @@ -843,6 +860,7 @@ class FileDisplayActivity : FileActivity(), FileListOption.AV_OFFLINE -> getString(R.string.drawer_item_only_available_offline) FileListOption.SHARED_BY_LINK -> getString(R.string.drawer_item_shared_by_link_files) FileListOption.ALL_FILES -> getString(R.string.default_display_name_for_root_folder) + FileListOption.SPACES_LIST -> getString(R.string.drawer_item_spaces) } setupRootToolbar(title, isSearchEnabled = true) listMainFileFragment?.setSearchListener(findViewById(R.id.root_toolbar_search_view)) @@ -1317,15 +1335,25 @@ class FileDisplayActivity : FileActivity(), private fun navigateTo(newFileListOption: FileListOption) { if (fileListOption != newFileListOption) { - if (listMainFileFragment != null) { + if (newFileListOption == FileListOption.SPACES_LIST) { + initAndShowListOfSpaces() + fileListOption = FileListOption.SPACES_LIST + updateToolbar(null) + } + else if (listMainFileFragment != null) { fileListOption = newFileListOption file = storageManager.getFileByPath(OCFile.ROOT_PATH) listMainFileFragment?.updateFileListOption(newFileListOption, file) updateToolbar(null) + } else if (spacesListFragment != null) { + fileListOption = newFileListOption + file = storageManager.getFileByPath(OCFile.ROOT_PATH) + initAndShowListOfFiles() + updateToolbar(null) } else { super.navigateToOption(FileListOption.ALL_FILES) } - } else { + } else if (newFileListOption != FileListOption.SPACES_LIST) { browseToRoot() } } @@ -1335,6 +1363,7 @@ class FileDisplayActivity : FileActivity(), } private fun getMenuItemForFileListOption(fileListOption: FileListOption?): Int = when (fileListOption) { + FileListOption.SPACES_LIST -> R.id.nav_spaces FileListOption.SHARED_BY_LINK -> R.id.nav_shared_by_link_files FileListOption.AV_OFFLINE -> R.id.nav_available_offline_files else -> R.id.nav_all_files @@ -1444,6 +1473,7 @@ class FileDisplayActivity : FileActivity(), companion object { private const val TAG_LIST_OF_FILES = "LIST_OF_FILES" + private const val TAG_LIST_OF_SPACES = "LIST_OF_SPACES" private const val TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT" private const val KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW" diff --git a/owncloudApp/src/main/res/drawable/ic_spaces.xml b/owncloudApp/src/main/res/drawable/ic_spaces.xml new file mode 100644 index 00000000000..f7893d63d89 --- /dev/null +++ b/owncloudApp/src/main/res/drawable/ic_spaces.xml @@ -0,0 +1,9 @@ + + + diff --git a/owncloudApp/src/main/res/layout/spaces_list_fragment.xml b/owncloudApp/src/main/res/layout/spaces_list_fragment.xml new file mode 100644 index 00000000000..1ee3a007d50 --- /dev/null +++ b/owncloudApp/src/main/res/layout/spaces_list_fragment.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/owncloudApp/src/main/res/menu/bottom_navbar_menu.xml b/owncloudApp/src/main/res/menu/bottom_navbar_menu.xml index 4f5bd9a3866..b1dacebc1fa 100644 --- a/owncloudApp/src/main/res/menu/bottom_navbar_menu.xml +++ b/owncloudApp/src/main/res/menu/bottom_navbar_menu.xml @@ -21,6 +21,11 @@ android:id="@+id/nav_all_files" android:icon="@drawable/ic_folder" android:title="@string/bottom_nav_files" /> + Sort by All files Available offline + Spaces Shared by link Settings Uploads @@ -113,6 +114,8 @@ Files + Personal + Spaces Uploads Offline Links @@ -148,9 +151,11 @@ An error occurred while copying the file to a temporary folder. Please try to send again. seconds ago No files in here + No spaces No available offline files No shared links Upload some content or sync with your devices! + You don´t have access to any space! Files and folders you mark as available offline will show up here Files and folders you share by link will show up here Loading… diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index 37226a0c75d..d35738f639b 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -133,6 +133,7 @@ class OCFileRepository( override fun getSearchFolderContent(fileListOption: FileListOption, folderId: Long, search: String): List = when (fileListOption) { FileListOption.ALL_FILES -> localFileDataSource.getSearchFolderContent(folderId, search) + FileListOption.SPACES_LIST -> emptyList() FileListOption.AV_OFFLINE -> localFileDataSource.getSearchAvailableOfflineFolderContent(folderId, search) FileListOption.SHARED_BY_LINK -> localFileDataSource.getSearchSharedByLinkFolderContent(folderId, search) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt index 691034d0b1e..2a255dcd3a0 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/capabilities/model/OCCapability.kt @@ -63,6 +63,10 @@ data class OCCapability( fun isOpenInWebAllowed(): Boolean = filesAppProviders?.openWebUrlAppProviders?.isNotBlank() ?: false + fun isSpacesAllowed(): Boolean = spaces?.enabledSpaces == true + + fun isSpacesProjectsAllowed(): Boolean = spaces?.projectsSpaces == true + data class AppProviders( val enabledAppProviders: Boolean, val versionAppProviders: String, diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/FileListOption.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/FileListOption.kt index e8918efd341..41ea73f0522 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/FileListOption.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/FileListOption.kt @@ -27,9 +27,10 @@ import kotlinx.parcelize.Parcelize @Parcelize enum class FileListOption : Parcelable { - ALL_FILES, SHARED_BY_LINK, AV_OFFLINE; + ALL_FILES, SPACES_LIST, SHARED_BY_LINK, AV_OFFLINE; fun isAllFiles() = this == ALL_FILES + fun isSpacesList() = this == SPACES_LIST fun isSharedByLink() = this == SHARED_BY_LINK fun isAvailableOffline() = this == AV_OFFLINE }