Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swipe to Refresh #3469

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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