Skip to content

Commit

Permalink
Merge pull request #3469 from owncloud/new_arch/file_list_isolation_s…
Browse files Browse the repository at this point in the history
…wipe

Swipe to Refresh
  • Loading branch information
jabarros authored Dec 9, 2021
2 parents ff7090d + bb63e2b commit e3ca7ad
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 164 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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()) }
viewModel { MainFileListViewModel(get(), get(), get()) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* 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 <http://www.gnu.org/licenses/>.
*
*/

package com.owncloud.android.extensions

import androidx.swiperefreshlayout.widget.SwipeRefreshLayout

fun SwipeRefreshLayout.cancel() {
this.isRefreshing = false
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,61 +31,92 @@ 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.databinding.ListFooterBinding
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.MimetypeIconUtil

class FileListAdapter(
private val context: Context,
private val listener: FileListAdapterListener
) : RecyclerView.Adapter<FileListAdapter.ViewHolder>() {
private val isShowingJustFolders: Boolean,
private val listener: FileListAdapterListener,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

private val files = mutableListOf<OCFile>()
private val files = mutableListOf<Any>()
private lateinit var viewHolder: RecyclerView.ViewHolder

private val TYPE_ITEMS = 0
private val TYPE_FOOTER = 1

fun updateFileList(filesToAdd: List<OCFile>) {
val diffUtilCallback = FileListDiffCallback(oldList = files, newList = filesToAdd)
val diffResult = DiffUtil.calculateDiff(diffUtilCallback)
files.clear()
files.addAll(filesToAdd)
if (filesToAdd.isNotEmpty()) {
files.add(OCFooterFile(manageListOfFilesAndGenerateText(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 onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
TYPE_ITEMS -> {
val binding = ItemFileListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
viewHolder = ViewHolder(binding)
}
TYPE_FOOTER -> {
val binding = ListFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false)
viewHolder = FooterViewHolder(binding)
}
}
return viewHolder
}

override fun getItemCount(): Int = files.size

override fun getItemId(position: Int): Long = position.toLong()

fun getItem(position: Int): Any? {
return if (files.size <= position) {
null
} else files[position]
override fun getItemViewType(position: Int): Int {
return if (position == files.size.minus(1)) {
TYPE_FOOTER
} else {
TYPE_ITEMS
}
}

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)
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) {
TYPE_ITEMS -> {
with(holder as ViewHolder) {
with(files[position] as OCFile) {
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)
binding.root.setOnClickListener {
listener.clickItem(this)
}
}
}
//TODO Check this with FileListListAdapter.java and its viewType (LIST or GRID)
getSharedIcon(imageView = binding.sharedIcon, file = this)
binding.root.setOnClickListener {
listener.clickItem(this)
}
TYPE_FOOTER -> {
if (!isShowingJustFolders) {
with(holder as FooterViewHolder) {
with(files[position] as OCFooterFile) {
binding.footerText.text = this.text
}
}
}
}
}
Expand Down Expand Up @@ -119,9 +150,75 @@ class FileListAdapter(
}
}

private fun manageListOfFilesAndGenerateText(list: List<OCFile>): String {
var filesCount = 0
var foldersCount = 0
val count: Int = list.size
var file: OCFile
for (i in 0 until count) {
file = list[i]
if (file.isFolder) {
foldersCount++
} else {
if (!file.isHidden) {
filesCount++
}
}
}

return generateFooterText(filesCount, foldersCount)
}

private fun generateFooterText(filesCount: Int, foldersCount: Int): String {
when {
filesCount <= 0 -> {
return when {
foldersCount <= 0 -> {
""
}
foldersCount == 1 -> {
context.getString(R.string.file_list__footer__folder)
}
else -> { // foldersCount > 1
context.getString(R.string.file_list__footer__folders, foldersCount)
}
}
}
filesCount == 1 -> {
return when {
foldersCount <= 0 -> {
context.getString(R.string.file_list__footer__file)
}
foldersCount == 1 -> {
context.getString(R.string.file_list__footer__file_and_folder)
}
else -> { // foldersCount > 1
context.getString(R.string.file_list__footer__file_and_folders, foldersCount)
}
}
}
else -> { // filesCount > 1
return when {
foldersCount <= 0 -> {
context.getString(R.string.file_list__footer__files, filesCount)
}
foldersCount == 1 -> {
context.getString(R.string.file_list__footer__files_and_folder, filesCount)
}
else -> { // foldersCount > 1
context.getString(
R.string.file_list__footer__files_and_folders, filesCount, foldersCount
)
}
}
}
}
}

interface FileListAdapterListener {
fun clickItem(ocFile: OCFile)
}

inner class ViewHolder(val binding: ItemFileListBinding) : RecyclerView.ViewHolder(binding.root)
inner class FooterViewHolder(val binding: ListFooterBinding) : RecyclerView.ViewHolder(binding.root)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,37 @@ package com.owncloud.android.presentation.diffutils

import androidx.recyclerview.widget.DiffUtil
import com.owncloud.android.domain.files.model.OCFile
import com.owncloud.android.domain.files.model.OCFooterFile

class FileListDiffCallback(private val oldList: List<OCFile>, private val newList: List<OCFile>) : DiffUtil.Callback() {
class FileListDiffCallback(private val oldList: List<Any>, private val newList: List<Any>) : 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
}
val oldItem = oldList[oldItemPosition]
val newItem = newList[newItemPosition]

if (oldItem is Unit && newItem is Unit) {
return true
}

if (oldItem is Boolean && newItem is Boolean) {
return true
}

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val (_, value, name) = oldList[oldItemPosition]
val (_, value1, name1) = newList[newItemPosition]
return name == name1 && value == value1
if (oldItem is OCFile && newItem is OCFile) {
return oldItem.id == newItem.id
}

if (oldItem is OCFooterFile && newItem is OCFooterFile) {
return oldItem.text == newItem.text
}

return false
}

override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
oldList[oldItemPosition] == newList[newItemPosition]
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ 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.extensions.cancel
import com.owncloud.android.presentation.adapters.filelist.FileListAdapter
import com.owncloud.android.presentation.observers.EmptyDataObserver
import com.owncloud.android.presentation.onSuccess
Expand Down Expand Up @@ -59,7 +60,7 @@ class MainFileListFragment : Fragment() {

private fun initViews() {
//Set RecyclerView and its adapter.
fileListAdapter = FileListAdapter(context = requireContext(), listener = object :
fileListAdapter = FileListAdapter(context = requireContext(), isShowingJustFolders = isShowingJustFolders(), listener = object :
FileListAdapter.FileListAdapterListener {
override fun clickItem(ocFile: OCFile) {
if (ocFile.isFolder) {
Expand All @@ -70,35 +71,24 @@ class MainFileListFragment : Fragment() {
}
}

} )
})
binding.recyclerViewMainFileList.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = fileListAdapter
}

// Set Swipe to refresh and its listener
binding.swipeRefreshMainFileList.setOnRefreshListener { mainFileListViewModel.refreshDirectory() }
}

private fun subscribeToViewModels() {
//Observe the action of retrieving the list of files from BBDD.
// Observe the action of retrieving the list of files from DB.
mainFileListViewModel.getFilesListStatusLiveData.observe(viewLifecycleOwner, Event.EventObserver {
it.onSuccess { data ->
val files = data ?: emptyList()
fileListAdapter.updateFileList(filesToAdd = files)
registerListAdapterDataObserver()
mainFileListViewModel.manageListOfFiles(files)
}
})

mainFileListViewModel.numberOfFilesPerType.observe(viewLifecycleOwner, Event.EventObserver {
it.onSuccess { data ->
if (!isShowingJustFolders()) {
mainFileListViewModel.generateFooterText(data!!.first, data.second)
}
}
})

mainFileListViewModel.footerText.observe(viewLifecycleOwner, Event.EventObserver {
it.onSuccess { data ->
setFooterText(data)
binding.swipeRefreshMainFileList.cancel()
}
})
}
Expand All @@ -107,16 +97,6 @@ class MainFileListFragment : Fragment() {
mainFileListViewModel.listDirectory(directory = directory)
}

private fun setFooterText(text: String?) {
if (text?.isNotEmpty() == true) {
binding.footerMainFileList.footerText.text = text
// TODO Manage footer enable/disable options
//setFooterEnabled(true)
} else {
//setFooterEnabled(false)
}
}

private fun isShowingJustFolders(): Boolean {
val args = arguments
return args != null && args.getBoolean(ARG_JUST_FOLDERS, false)
Expand Down
Loading

0 comments on commit e3ca7ad

Please sign in to comment.