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 9d3afc7d760..21a89956871 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -75,5 +75,5 @@ val viewModelModule = module { viewModel { PreviewImageViewModel(get(), get(), get()) } viewModel { FileDetailsViewModel(get(), get(), get(), get(), get()) } viewModel { FileOperationViewModel(get(), get(), get(), get(), get(), get()) } - viewModel { MainFileListViewModel(get(), get(), get(), get(), get()) } + viewModel { MainFileListViewModel(get(), get(), get(), get(), get(), get()) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt index 4c9b9e5e723..33e10d79812 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt @@ -32,11 +32,13 @@ import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.R import com.owncloud.android.databinding.ItemFileListBinding import com.owncloud.android.databinding.ListFooterBinding +import com.owncloud.android.db.PreferenceManager import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.model.OCFooterFile import com.owncloud.android.extensions.setPicture import com.owncloud.android.presentation.diffutils.FileListDiffCallback import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.FileStorageUtils import com.owncloud.android.utils.MimetypeIconUtil class FileListAdapter( @@ -45,7 +47,8 @@ class FileListAdapter( private val listener: FileListAdapterListener, ) : RecyclerView.Adapter() { - private val files = mutableListOf() + + private var files = mutableListOf() private lateinit var viewHolder: RecyclerView.ViewHolder private val TYPE_ITEMS = 0 @@ -56,9 +59,11 @@ class FileListAdapter( val diffResult = DiffUtil.calculateDiff(diffUtilCallback) files.clear() files.addAll(filesToAdd) + if (filesToAdd.isNotEmpty()) { files.add(OCFooterFile(manageListOfFilesAndGenerateText(filesToAdd))) } + diffResult.dispatchUpdatesTo(this) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt index 45a947db9ee..6182a6a87ed 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt @@ -27,16 +27,25 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import com.owncloud.android.databinding.MainFileListFragmentBinding +import com.owncloud.android.db.PreferenceManager import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.utils.Event import com.owncloud.android.extensions.cancel import com.owncloud.android.presentation.adapters.filelist.FileListAdapter import com.owncloud.android.presentation.observers.EmptyDataObserver import com.owncloud.android.presentation.onSuccess +import com.owncloud.android.presentation.ui.files.SortBottomSheetFragment +import com.owncloud.android.presentation.ui.files.SortBottomSheetFragment.Companion.newInstance +import com.owncloud.android.presentation.ui.files.SortBottomSheetFragment.SortDialogListener +import com.owncloud.android.presentation.ui.files.SortOptionsView +import com.owncloud.android.presentation.ui.files.SortOrder +import com.owncloud.android.presentation.ui.files.SortType +import com.owncloud.android.presentation.ui.files.ViewType import com.owncloud.android.ui.activity.FileListOption +import com.owncloud.android.utils.FileStorageUtils import org.koin.androidx.viewmodel.ext.android.viewModel -class MainFileListFragment : Fragment() { +class MainFileListFragment : Fragment(), SortDialogListener, SortOptionsView.SortOptionsListener, SortOptionsView.CreateFolderListener { private val mainFileListViewModel by viewModel() @@ -44,6 +53,7 @@ class MainFileListFragment : Fragment() { private val binding get() = _binding!! private lateinit var fileListAdapter: FileListAdapter + private lateinit var files: List override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -80,6 +90,15 @@ class MainFileListFragment : Fragment() { // Set Swipe to refresh and its listener binding.swipeRefreshMainFileList.setOnRefreshListener { mainFileListViewModel.refreshDirectory() } + + //Set SortOptions and its listeners + binding.optionsLayout.let { + it.onSortOptionsListener = this + if (isPickingAFolder()) { + it.onCreateFolderListener = this + it.selectAdditionalView(SortOptionsView.AdditionalView.CREATE_FOLDER) + } + } } private fun subscribeToViewModels() { @@ -87,6 +106,11 @@ class MainFileListFragment : Fragment() { mainFileListViewModel.getFilesListStatusLiveData.observe(viewLifecycleOwner, Event.EventObserver { it.onSuccess { data -> updateFileListData(files = data ?: emptyList()) + files = data ?: emptyList() + val sortedFiles = mainFileListViewModel.sortList(files) + fileListAdapter.updateFileList(filesToAdd = sortedFiles) + registerListAdapterDataObserver() + binding.swipeRefreshMainFileList.cancel() } }) @@ -119,6 +143,41 @@ class MainFileListFragment : Fragment() { fileListAdapter.registerAdapterDataObserver(emptyDataObserver) } + override fun onSortTypeListener(sortType: SortType, sortOrder: SortOrder) { + val sortBottomSheetFragment = newInstance(sortType, sortOrder) + sortBottomSheetFragment.sortDialogListener = this + sortBottomSheetFragment.show(childFragmentManager, SortBottomSheetFragment.TAG) + } + + override fun onViewTypeListener(viewType: ViewType) { + //TODO("Not yet implemented") + } + + override fun onSortSelected(sortType: SortType) { + binding.optionsLayout.sortTypeSelected = sortType + + val isAscending = binding.optionsLayout.sortOrderSelected == SortOrder.SORT_ORDER_ASCENDING + + when (sortType) { + SortType.SORT_TYPE_BY_NAME -> sortAdapterBy(FileStorageUtils.SORT_NAME, isAscending) + SortType.SORT_TYPE_BY_DATE -> sortAdapterBy(FileStorageUtils.SORT_DATE, isAscending) + SortType.SORT_TYPE_BY_SIZE -> sortAdapterBy(FileStorageUtils.SORT_SIZE, isAscending) + } + } + + private fun sortAdapterBy(sortType: Int, isDescending: Boolean) { + PreferenceManager.setSortOrder(sortType, requireContext(), FileStorageUtils.FILE_DISPLAY_SORT) + PreferenceManager.setSortAscending(isDescending, requireContext(), FileStorageUtils.FILE_DISPLAY_SORT) + + val sortedFiles = mainFileListViewModel.sortList(files) + fileListAdapter.updateFileList(filesToAdd = sortedFiles) + } + + private fun isPickingAFolder(): Boolean { + val args = arguments + return args != null && args.getBoolean(ARG_PICKING_A_FOLDER, false) + } + override fun onDestroy() { super.onDestroy() _binding = null @@ -131,25 +190,32 @@ class MainFileListFragment : Fragment() { } fun updateFileListOption(newFileListOption: FileListOption) { - when(newFileListOption) { - FileListOption.ALL_FILES -> mainFileListViewModel.listCurrentDirectory() - FileListOption.AV_OFFLINE -> mainFileListViewModel.getAvailableOfflineFilesList() - FileListOption.SHARED_BY_LINK -> mainFileListViewModel.getSharedByLinkFilesList() + when (newFileListOption) { + FileListOption.ALL_FILES -> mainFileListViewModel.listCurrentDirectory() + FileListOption.AV_OFFLINE -> mainFileListViewModel.getAvailableOfflineFilesList() + FileListOption.SHARED_BY_LINK -> mainFileListViewModel.getSharedByLinkFilesList() } // TODO Manage FAB button } companion object { - val ARG_JUST_FOLDERS = MainFileListFragment::class.java.canonicalName + ".JUST_FOLDERS" + val ARG_JUST_FOLDERS = "${MainFileListFragment::class.java.canonicalName}.JUST_FOLDERS" + val ARG_PICKING_A_FOLDER = "${MainFileListFragment::class.java.canonicalName}.ARG_PICKING_A_FOLDER}" fun newInstance( - justFolders: Boolean + justFolders: Boolean, + pickingAFolder: Boolean = false ): MainFileListFragment { val args = Bundle() args.putBoolean(ARG_JUST_FOLDERS, justFolders) + args.putBoolean(ARG_PICKING_A_FOLDER, pickingAFolder) return MainFileListFragment().apply { arguments = args } } } + + override fun onCreateFolderListener() { + //TODO("Not yet implemented") + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListViewModel.kt index ccafa8a27c1..db5036f3dc2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListViewModel.kt @@ -24,8 +24,9 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.owncloud.android.domain.UseCaseResult import androidx.lifecycle.viewModelScope +import com.owncloud.android.db.PreferenceManager +import com.owncloud.android.domain.UseCaseResult import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.usecases.GetFilesAvailableOfflineUseCase import com.owncloud.android.domain.files.usecases.GetFilesSharedByLinkUseCase @@ -33,7 +34,9 @@ import com.owncloud.android.domain.files.usecases.GetFolderContentAsLiveDataUseC import com.owncloud.android.domain.files.usecases.RefreshFolderFromServerAsyncUseCase import com.owncloud.android.domain.utils.Event import com.owncloud.android.presentation.UIResult +import com.owncloud.android.providers.ContextProvider import com.owncloud.android.providers.CoroutinesDispatcherProvider +import com.owncloud.android.utils.FileStorageUtils import kotlinx.coroutines.launch class MainFileListViewModel( @@ -41,7 +44,8 @@ class MainFileListViewModel( private val getFilesSharedByLinkUseCase: GetFilesSharedByLinkUseCase, private val getFilesAvailableOfflineUseCase: GetFilesAvailableOfflineUseCase, private val refreshFolderFromServerAsyncUseCase: RefreshFolderFromServerAsyncUseCase, - private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider + private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider, + private val contextProvider: ContextProvider, ) : ViewModel() { private lateinit var file: OCFile @@ -58,7 +62,6 @@ class MainFileListViewModel( val getFilesAvailableOfflineData: LiveData>>> get() = _getFilesAvailableOfflineData - private fun getFilesList(folderId: Long) { val filesListLiveData: LiveData> = getFolderContentAsLiveDataUseCase.execute(GetFolderContentAsLiveDataUseCase.Params(folderId = folderId)) @@ -109,5 +112,15 @@ class MainFileListViewModel( fun refreshDirectory() { refreshFilesList(file.remotePath) } + + fun sortList(files: List): List { + val sortOrderSaved = PreferenceManager.getSortOrder(contextProvider.getContext(), FileStorageUtils.FILE_DISPLAY_SORT) + val ascendingModeSaved = PreferenceManager.getSortAscending(contextProvider.getContext(), FileStorageUtils.FILE_DISPLAY_SORT) + + return FileStorageUtils.sortFolder( + files, sortOrderSaved, + ascendingModeSaved + ) + } } 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 3329c5cdeb7..c0c49549c6f 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 @@ -56,7 +56,6 @@ import com.owncloud.android.BuildConfig import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.authentication.BiometricManager -import com.owncloud.android.presentation.ui.security.PassCodeManager import com.owncloud.android.authentication.PatternManager import com.owncloud.android.databinding.ActivityMainBinding import com.owncloud.android.datamodel.FileDataStorageManager @@ -80,9 +79,9 @@ import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.presentation.UIResult import com.owncloud.android.presentation.manager.DOWNLOAD_ADDED_MESSAGE import com.owncloud.android.presentation.manager.DOWNLOAD_FINISH_MESSAGE -import com.owncloud.android.presentation.ui.files.filelist.MainFileListFragment import com.owncloud.android.presentation.ui.files.operations.FileOperation import com.owncloud.android.presentation.ui.files.operations.FileOperationViewModel +import com.owncloud.android.presentation.ui.security.PassCodeManager import com.owncloud.android.syncadapter.FileSyncAdapter import com.owncloud.android.ui.errorhandling.ErrorMessageAdapter import com.owncloud.android.ui.fragment.FileDetailFragment @@ -149,9 +148,6 @@ class FileDisplayActivity : FileActivity(), FileFragment.ContainerActivity, OnEn private val listOfFilesFragment: OCFileListFragment? get() = supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FILES) as OCFileListFragment? - private val listMainFileFragment: MainFileListFragment? - get() = supportFragmentManager.findFragmentByTag(TAG_LIST_OF_FILES_BIS) as MainFileListFragment? - private val secondFragment: FileFragment? get() = supportFragmentManager.findFragmentByTag(TAG_SECOND_FRAGMENT) as FileFragment? @@ -343,21 +339,17 @@ class FileDisplayActivity : FileActivity(), FileFragment.ContainerActivity, OnEn // TODO Change to start using new MainListFragment private fun createMinFragments() { - /* val listOfFiles = OCFileListFragment.newInstance(false, fileListOption, false, false, true) - listOfFiles.setSearchListener(findViewById(R.id.root_toolbar_search_view))*/ - - val list = MainFileListFragment.newInstance(justFolders = false) - - + val listOfFiles = OCFileListFragment.newInstance(false, fileListOption, false, false, true) + listOfFiles.setSearchListener(findViewById(R.id.root_toolbar_search_view)) val transaction = supportFragmentManager.beginTransaction() - transaction.add(R.id.left_fragment_container, list, TAG_LIST_OF_FILES_BIS) + transaction.add(R.id.left_fragment_container, listOfFiles, TAG_LIST_OF_FILES) transaction.commit() } private fun initFragmentsWithFile() { if (account != null && file != null) { /// First fragment - listMainFileFragment?.listDirectory(currentDir) + listOfFilesFragment?.listDirectory(currentDir) ?: Timber.e("Still have a chance to lose the initialization of list fragment >(") /// Second fragment @@ -477,10 +469,8 @@ class FileDisplayActivity : FileActivity(), FileFragment.ContainerActivity, OnEn } fun refreshListOfFilesFragment(reloadData: Boolean) { - /*val fileListFragment = listOfFilesFragment - fileListFragment?.listDirectory(reloadData)*/ - val fileListFragment = listMainFileFragment - fileListFragment?.listDirectory(file) + val fileListFragment = listOfFilesFragment + fileListFragment?.listDirectory(reloadData) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -1617,10 +1607,10 @@ class FileDisplayActivity : FileActivity(), FileFragment.ContainerActivity, OnEn private fun navigateTo(newFileListOption: FileListOption) { if (fileListOption != newFileListOption) { - if (listMainFileFragment != null) { + if (listOfFilesFragment != null) { fileListOption = newFileListOption file = storageManager.getFileByPath(OCFile.ROOT_PATH) - listMainFileFragment?.updateFileListOption(newFileListOption) + listOfFilesFragment?.updateFileListOption(newFileListOption) updateToolbar(null) } else { super.navigateToOption(FileListOption.ALL_FILES) @@ -1656,7 +1646,6 @@ class FileDisplayActivity : FileActivity(), FileFragment.ContainerActivity, OnEn } companion object { - private const val TAG_LIST_OF_FILES_BIS = "TAG_LIST_OF_FILES_BIS" private const val TAG_LIST_OF_FILES = "LIST_OF_FILES" private const val TAG_SECOND_FRAGMENT = "SECOND_FRAGMENT" diff --git a/owncloudApp/src/main/res/layout/main_file_list_fragment.xml b/owncloudApp/src/main/res/layout/main_file_list_fragment.xml index 588c94abbf9..947570d58d2 100644 --- a/owncloudApp/src/main/res/layout/main_file_list_fragment.xml +++ b/owncloudApp/src/main/res/layout/main_file_list_fragment.xml @@ -18,39 +18,75 @@ ~ --> - + android:filterTouchesWhenObscured="true"> - - - + + + + + + + + + android:layout_marginTop="@dimen/file_list_progress_bar_height" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/options_layout"> - + - + + + - + +