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 0291aff625e..12529861277 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -23,6 +23,7 @@ package com.owncloud.android.dependecyinjection import com.owncloud.android.MainApp +import com.owncloud.android.presentation.ui.files.filelist.MainFileListViewModel import com.owncloud.android.presentation.ui.files.operations.FileOperationViewModel import com.owncloud.android.presentation.viewmodels.authentication.OCAuthenticationViewModel import com.owncloud.android.presentation.viewmodels.capabilities.OCCapabilityViewModel @@ -74,4 +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()) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/ImageViewExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/ImageViewExt.kt new file mode 100644 index 00000000000..c29c0a2fbb0 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/ImageViewExt.kt @@ -0,0 +1,34 @@ +/** + * ownCloud Android client application + * + * @author Fernando Sanz Velasco + * Copyright (C) 2021 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.extensions + +import android.annotation.SuppressLint +import android.widget.ImageView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy + +@SuppressLint("CheckResult") +fun ImageView.setPicture(imageToLoad: Int) { + Glide.with(this) + .load(imageToLoad) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .into(this) +} 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 new file mode 100644 index 00000000000..10f36472049 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt @@ -0,0 +1,113 @@ +/** + * ownCloud Android client application + * + * @author Fernando Sanz Velasco + * Copyright (C) 2021 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.adapters.filelist + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import androidx.core.content.ContextCompat +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import com.owncloud.android.R +import com.owncloud.android.databinding.ItemFileListBinding +import com.owncloud.android.domain.files.model.OCFile +import com.owncloud.android.extensions.setPicture +import com.owncloud.android.presentation.diffutils.FileListDiffCallback +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.MimetypeIconUtil + +class FileListAdapter( + private val context: Context +) : RecyclerView.Adapter() { + + private val files = mutableListOf() + + fun updateFileList(filesToAdd: List) { + val diffUtilCallback = FileListDiffCallback(oldList = files, newList = filesToAdd) + val diffResult = DiffUtil.calculateDiff(diffUtilCallback) + files.clear() + files.addAll(filesToAdd) + diffResult.dispatchUpdatesTo(this) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val binding = ItemFileListBinding.inflate(LayoutInflater.from(parent.context), parent, false) + + return ViewHolder(binding) + } + + override fun getItemCount(): Int = files.size + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getItemViewType(position: Int): Int = position + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + with(holder) { + with(files[position]) { + binding.Filename.text = this.fileName + binding.fileListSize.text = DisplayUtils.bytesToHumanReadable(this.length, context) + binding.fileListLastMod.text = DisplayUtils.getRelativeTimestamp(context, this.modificationTimestamp) + binding.localFileIndicator.isVisible = false //TODO Modify in the future, when we start the synchronization task. + binding.customCheckbox.isVisible = false //TODO Modify in the future, when we start the multi selection task. + binding.thumbnail.let { + it.tag = this.id + getThumbnailPicture(imageView = it, file = this) + } + //TODO Check this with FileListListAdapter.java and its viewType (LIST or GRID) + getSharedIcon(imageView = binding.sharedIcon, file = this) + } + } + } + + private fun getThumbnailPicture(imageView: ImageView, file: OCFile) { + if (file.isFolder) { + imageView.setPicture( + MimetypeIconUtil.getFolderTypeIconId(file.isSharedWithMe || file.sharedWithSharee ?: false, file.sharedByLink) + ) + } else { + // Set file icon depending on its mimetype. + imageView.setPicture(MimetypeIconUtil.getFileTypeIconId(file.mimeType, file.fileName)) + if (file.mimeType.contentEquals("image/png")) { + imageView.setBackgroundColor(ContextCompat.getColor(context, R.color.background_color)) + } + } + } + + private fun getSharedIcon(imageView: ImageView, file: OCFile) { + if (file.sharedByLink) { + imageView.setImageResource(R.drawable.ic_shared_by_link) + imageView.visibility = View.VISIBLE + imageView.bringToFront() + } else if (file.sharedWithSharee == true || file.isSharedWithMe) { + imageView.setImageResource(R.drawable.shared_via_users) + imageView.visibility = View.VISIBLE + imageView.bringToFront() + } else { + imageView.visibility = View.GONE + } + } + + inner class ViewHolder(val binding: ItemFileListBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/diffutils/FileListDiffCallback.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/diffutils/FileListDiffCallback.kt new file mode 100644 index 00000000000..d2c09afad6d --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/diffutils/FileListDiffCallback.kt @@ -0,0 +1,41 @@ +/** + * ownCloud Android client application + * + * @author Fernando Sanz Velasco + * Copyright (C) 2021 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.diffutils + +import androidx.recyclerview.widget.DiffUtil +import com.owncloud.android.domain.files.model.OCFile + +class FileListDiffCallback(private val oldList: List, private val newList: List) : DiffUtil.Callback() { + + override fun getOldListSize(): Int = oldList.size + + override fun getNewListSize(): Int = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return oldList[oldItemPosition].id === newList[newItemPosition].id + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val (_, value, name) = oldList[oldItemPosition] + val (_, value1, name1) = newList[newItemPosition] + return name == name1 && value == value1 + } +} 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 efaa9f1b797..747bb775eb2 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 @@ -25,23 +25,72 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels +import androidx.recyclerview.widget.LinearLayoutManager import com.owncloud.android.databinding.MainFileListFragmentBinding +import com.owncloud.android.domain.files.model.OCFile +import com.owncloud.android.domain.utils.Event +import com.owncloud.android.presentation.UIResult +import com.owncloud.android.presentation.adapters.filelist.FileListAdapter +import org.koin.androidx.viewmodel.ext.android.viewModel class MainFileListFragment : Fragment() { - private val viewModel by viewModels() + private val mainFileListViewModel by viewModel() private var _binding: MainFileListFragmentBinding? = null private val binding get() = _binding!! + private lateinit var fileListAdapter: FileListAdapter + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = MainFileListFragmentBinding.inflate(inflater, container, false) - return binding.root } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initViews() + subscribeToViewModels() + } + + private fun initViews() { + //Set RecyclerView and its adapter. + fileListAdapter = FileListAdapter(context = requireContext()) + binding.recyclerViewMainFileList.apply { + layoutManager = LinearLayoutManager(requireContext()) + adapter = fileListAdapter + + } + } + + private fun subscribeToViewModels() { + //Observe the action of retrieving the list of files. + mainFileListViewModel.getFilesListStatusLiveData.observe(viewLifecycleOwner, Event.EventObserver { result -> + when (result) { + is UIResult.Error -> {} //TODO Manage Error + is UIResult.Loading -> {} //TODO Manage Loading + is UIResult.Success -> fileListAdapter.updateFileList(filesToAdd = result.data ?: emptyList()) + } + }) + } + + fun listDirectory(directory: OCFile) { + mainFileListViewModel.listDirectory(directory = directory) + } + + override fun onDestroy() { + super.onDestroy() + _binding = null + } + + companion object { + fun newInstance(): MainFileListFragment { + val args = Bundle() + return MainFileListFragment().apply { arguments = args } + } + } } + 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 092788de184..84322752f87 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 @@ -1,7 +1,60 @@ +/** + * ownCloud Android client application + * + * @author Fernando Sanz Velasco + * Copyright (C) 2021 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.ui.files.filelist +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.owncloud.android.domain.UseCaseResult +import com.owncloud.android.domain.files.model.OCFile +import com.owncloud.android.domain.files.usecases.GetFolderContentUseCase +import com.owncloud.android.domain.utils.Event +import com.owncloud.android.presentation.UIResult +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class MainFileListViewModel( + private val getFolderContentUseCase: GetFolderContentUseCase +) : ViewModel() { + + private var file: OCFile? = null + + private val _getFilesListStatusLiveData = MutableLiveData>>>() + val getFilesListStatusLiveData: LiveData>>> + get() = _getFilesListStatusLiveData + + private fun getFilesList(folderId: Long) { + viewModelScope.launch(Dispatchers.IO) { + _getFilesListStatusLiveData.postValue(Event(UIResult.Loading())) + getFolderContentUseCase.execute(GetFolderContentUseCase.Params(folderId = folderId)).let { + when (it) { + is UseCaseResult.Error -> _getFilesListStatusLiveData.postValue(Event(UIResult.Error(it.getThrowableOrNull()))) + is UseCaseResult.Success -> _getFilesListStatusLiveData.postValue(Event(UIResult.Success(it.getDataOrNull()))) + } + } + } + } -class MainFileListViewModel : ViewModel() { - // TODO: Implement the ViewModel -} \ No newline at end of file + fun listDirectory(directory: OCFile) { + getFilesList(directory.id!!) + } +} 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 bbcde0ae65b..43ef57b5021 100644 --- a/owncloudApp/src/main/res/layout/main_file_list_fragment.xml +++ b/owncloudApp/src/main/res/layout/main_file_list_fragment.xml @@ -1,5 +1,4 @@ - - - - + - +