mirror of
https://github.com/nextcloud/android.git
synced 2024-11-26 23:28:42 +03:00
Merge pull request #10050 from nextcloud/mediaTimeline
Group photos as timeline by month
This commit is contained in:
commit
c944203d93
29 changed files with 1627 additions and 798 deletions
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.datamodel
|
||||||
|
|
||||||
|
data class GalleryItems(val date: Long, val files: List<OCFile>)
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import com.nextcloud.client.account.User
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||||
|
import com.owncloud.android.datamodel.OCFile
|
||||||
|
import com.owncloud.android.utils.FileSortOrder
|
||||||
|
|
||||||
|
interface CommonOCFileListAdapterInterface {
|
||||||
|
fun isMultiSelect(): Boolean
|
||||||
|
fun cancelAllPendingTasks()
|
||||||
|
fun getItemPosition(file: OCFile): Int
|
||||||
|
fun swapDirectory(
|
||||||
|
user: User,
|
||||||
|
directory: OCFile,
|
||||||
|
storageManager: FileDataStorageManager,
|
||||||
|
onlyOnDevice: Boolean,
|
||||||
|
mLimitToMimeType: String
|
||||||
|
)
|
||||||
|
|
||||||
|
fun setHighlightedItem(file: OCFile)
|
||||||
|
fun setSortOrder(mFile: OCFile, sortOrder: FileSortOrder)
|
||||||
|
fun addCheckedFile(file: OCFile)
|
||||||
|
fun isCheckedFile(file: OCFile): Boolean
|
||||||
|
fun getCheckedItems(): Set<OCFile>
|
||||||
|
fun removeCheckedFile(file: OCFile)
|
||||||
|
fun notifyItemChanged(file: OCFile)
|
||||||
|
fun getFilesCount(): Int
|
||||||
|
fun setMultiSelect(boolean: Boolean)
|
||||||
|
fun clearCheckedItems()
|
||||||
|
}
|
|
@ -0,0 +1,249 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter
|
||||||
|
import com.afollestad.sectionedrecyclerview.SectionedViewHolder
|
||||||
|
import com.nextcloud.client.account.User
|
||||||
|
import com.nextcloud.client.preferences.AppPreferences
|
||||||
|
import com.owncloud.android.databinding.GalleryHeaderBinding
|
||||||
|
import com.owncloud.android.databinding.GridImageBinding
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||||
|
import com.owncloud.android.datamodel.GalleryItems
|
||||||
|
import com.owncloud.android.datamodel.OCFile
|
||||||
|
import com.owncloud.android.ui.activity.ComponentsGetter
|
||||||
|
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface
|
||||||
|
import com.owncloud.android.utils.DisplayUtils
|
||||||
|
import com.owncloud.android.utils.FileSortOrder
|
||||||
|
import com.owncloud.android.utils.FileStorageUtils
|
||||||
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView.SectionedAdapter
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
class GalleryAdapter(
|
||||||
|
val context: Context,
|
||||||
|
user: User,
|
||||||
|
ocFileListFragmentInterface: OCFileListFragmentInterface,
|
||||||
|
preferences: AppPreferences,
|
||||||
|
transferServiceGetter: ComponentsGetter
|
||||||
|
) : SectionedRecyclerViewAdapter<SectionedViewHolder>(), CommonOCFileListAdapterInterface, SectionedAdapter {
|
||||||
|
private var files: List<GalleryItems> = mutableListOf()
|
||||||
|
private var ocFileListDelegate: OCFileListDelegate
|
||||||
|
private var storageManager: FileDataStorageManager
|
||||||
|
|
||||||
|
init {
|
||||||
|
storageManager = transferServiceGetter.storageManager
|
||||||
|
|
||||||
|
ocFileListDelegate = OCFileListDelegate(
|
||||||
|
context,
|
||||||
|
ocFileListFragmentInterface,
|
||||||
|
user,
|
||||||
|
storageManager,
|
||||||
|
false,
|
||||||
|
preferences,
|
||||||
|
true,
|
||||||
|
transferServiceGetter,
|
||||||
|
showMetadata = false,
|
||||||
|
showShareAvatar = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun showFooters(): Boolean = false
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SectionedViewHolder {
|
||||||
|
return if (viewType == VIEW_TYPE_HEADER) {
|
||||||
|
GalleryHeaderViewHolder(
|
||||||
|
GalleryHeaderBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
GalleryItemViewHolder(
|
||||||
|
GridImageBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: SectionedViewHolder?,
|
||||||
|
section: Int,
|
||||||
|
relativePosition: Int,
|
||||||
|
absolutePosition: Int
|
||||||
|
) {
|
||||||
|
if (holder != null) {
|
||||||
|
val itemViewHolder = holder as GalleryItemViewHolder
|
||||||
|
val ocFile = files[section].files[relativePosition]
|
||||||
|
|
||||||
|
ocFileListDelegate.bindGridViewHolder(itemViewHolder, ocFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(section: Int): Int {
|
||||||
|
return files[section].files.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSectionCount(): Int {
|
||||||
|
return files.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getSectionName(position: Int): String {
|
||||||
|
return DisplayUtils.getDateByPattern(
|
||||||
|
files[getRelativePosition(position).section()].date,
|
||||||
|
context,
|
||||||
|
DisplayUtils.MONTH_YEAR_PATTERN
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindHeaderViewHolder(holder: SectionedViewHolder?, section: Int, expanded: Boolean) {
|
||||||
|
if (holder != null) {
|
||||||
|
val headerViewHolder = holder as GalleryHeaderViewHolder
|
||||||
|
val galleryItem = files[section]
|
||||||
|
|
||||||
|
headerViewHolder.binding.month.text = DisplayUtils.getDateByPattern(
|
||||||
|
galleryItem.date,
|
||||||
|
context,
|
||||||
|
DisplayUtils.MONTH_PATTERN
|
||||||
|
)
|
||||||
|
headerViewHolder.binding.year.text = DisplayUtils.getDateByPattern(
|
||||||
|
galleryItem.date,
|
||||||
|
context, DisplayUtils.YEAR_PATTERN
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindFooterViewHolder(holder: SectionedViewHolder?, section: Int) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun showAllGalleryItems(storageManager: FileDataStorageManager) {
|
||||||
|
val items = storageManager.allGalleryItems
|
||||||
|
|
||||||
|
files = items
|
||||||
|
.groupBy { firstOfMonth(it.modificationTimestamp) }
|
||||||
|
.map { GalleryItems(it.key, FileStorageUtils.sortOcFolderDescDateModifiedWithoutFavoritesFirst(it.value)) }
|
||||||
|
.sortedBy { it.date }.reversed()
|
||||||
|
|
||||||
|
Handler(Looper.getMainLooper()).post { notifyDataSetChanged() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun firstOfMonth(timestamp: Long): Long {
|
||||||
|
val cal = Calendar.getInstance()
|
||||||
|
cal.time = Date(timestamp)
|
||||||
|
cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH))
|
||||||
|
cal.set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
cal.set(Calendar.MINUTE, 0)
|
||||||
|
cal.set(Calendar.SECOND, 0)
|
||||||
|
|
||||||
|
return cal.timeInMillis
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isEmpty(): Boolean {
|
||||||
|
return files.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItem(position: Int): OCFile {
|
||||||
|
val itemCoord = getRelativePosition(position)
|
||||||
|
|
||||||
|
return files[itemCoord.section()].files[itemCoord.relativePos()]
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isMultiSelect(): Boolean {
|
||||||
|
return ocFileListDelegate.isMultiSelect
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancelAllPendingTasks() {
|
||||||
|
ocFileListDelegate.cancelAllPendingTasks()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemPosition(file: OCFile): Int {
|
||||||
|
val item = files.find { it.files.contains(file) }
|
||||||
|
return getAbsolutePosition(files.indexOf(item), item?.files?.indexOf(file) ?: 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun swapDirectory(
|
||||||
|
user: User,
|
||||||
|
directory: OCFile,
|
||||||
|
storageManager: FileDataStorageManager,
|
||||||
|
onlyOnDevice: Boolean,
|
||||||
|
mLimitToMimeType: String
|
||||||
|
) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setHighlightedItem(file: OCFile) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setSortOrder(mFile: OCFile, sortOrder: FileSortOrder) {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun addCheckedFile(file: OCFile) {
|
||||||
|
ocFileListDelegate.addCheckedFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCheckedFile(file: OCFile): Boolean {
|
||||||
|
return ocFileListDelegate.isCheckedFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getCheckedItems(): Set<OCFile> {
|
||||||
|
return ocFileListDelegate.checkedItems
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeCheckedFile(file: OCFile) {
|
||||||
|
ocFileListDelegate.removeCheckedFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun notifyItemChanged(file: OCFile) {
|
||||||
|
notifyItemChanged(getItemPosition(file))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFilesCount(): Int {
|
||||||
|
return files.fold(0) { acc, item -> acc + item.files.size }
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
override fun setMultiSelect(boolean: Boolean) {
|
||||||
|
ocFileListDelegate.isMultiSelect = boolean
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clearCheckedItems() {
|
||||||
|
ocFileListDelegate.clearCheckedItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun addFiles(items: List<GalleryItems>) {
|
||||||
|
files = items
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import com.afollestad.sectionedrecyclerview.SectionedViewHolder
|
||||||
|
import com.owncloud.android.databinding.GalleryHeaderBinding
|
||||||
|
|
||||||
|
class GalleryHeaderViewHolder(val binding: GalleryHeaderBinding) : SectionedViewHolder(binding.root)
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.afollestad.sectionedrecyclerview.SectionedViewHolder
|
||||||
|
import com.elyeproj.loaderviewlibrary.LoaderImageView
|
||||||
|
import com.owncloud.android.databinding.GridImageBinding
|
||||||
|
|
||||||
|
class GalleryItemViewHolder(val binding: GridImageBinding) :
|
||||||
|
SectionedViewHolder(binding.root), ListGridImageViewHolder {
|
||||||
|
override val thumbnail: ImageView
|
||||||
|
get() = binding.thumbnail
|
||||||
|
|
||||||
|
override val shimmerThumbnail: LoaderImageView
|
||||||
|
get() = binding.thumbnailShimmer
|
||||||
|
|
||||||
|
override val favorite: ImageView
|
||||||
|
get() = binding.favoriteAction
|
||||||
|
|
||||||
|
override val localFileIndicator: ImageView
|
||||||
|
get() = binding.localFileIndicator
|
||||||
|
|
||||||
|
override val shared: ImageView
|
||||||
|
get() = binding.sharedIcon
|
||||||
|
|
||||||
|
override val checkbox: ImageView
|
||||||
|
get() = binding.customCheckbox
|
||||||
|
|
||||||
|
override val itemLayout: View
|
||||||
|
get() = binding.ListItemLayout
|
||||||
|
|
||||||
|
override val unreadComments: ImageView
|
||||||
|
get() = binding.unreadComments
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import com.elyeproj.loaderviewlibrary.LoaderImageView
|
||||||
|
|
||||||
|
interface ListGridImageViewHolder {
|
||||||
|
val thumbnail: ImageView
|
||||||
|
val shimmerThumbnail: LoaderImageView
|
||||||
|
val favorite: ImageView
|
||||||
|
val localFileIndicator: ImageView
|
||||||
|
val shared: ImageView
|
||||||
|
val checkbox: ImageView
|
||||||
|
val itemLayout: View
|
||||||
|
val unreadComments: ImageView
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.widget.TextView
|
||||||
|
|
||||||
|
internal interface ListGridItemViewHolder : ListGridImageViewHolder {
|
||||||
|
val fileName: TextView
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import com.owncloud.android.ui.AvatarGroupLayout
|
||||||
|
|
||||||
|
internal interface ListItemViewHolder : ListGridItemViewHolder {
|
||||||
|
val fileSize: TextView
|
||||||
|
val fileSizeSeparator: View
|
||||||
|
val lastModification: TextView
|
||||||
|
val overflowMenu: ImageView
|
||||||
|
val sharedAvatars: AvatarGroupLayout
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.View
|
||||||
|
import com.nextcloud.client.account.User
|
||||||
|
import com.nextcloud.client.preferences.AppPreferences
|
||||||
|
import com.owncloud.android.R
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||||
|
import com.owncloud.android.datamodel.OCFile
|
||||||
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGenerationTask
|
||||||
|
import com.owncloud.android.lib.common.utils.Log_OC
|
||||||
|
import com.owncloud.android.ui.activity.ComponentsGetter
|
||||||
|
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface
|
||||||
|
import com.owncloud.android.utils.DisplayUtils
|
||||||
|
import com.owncloud.android.utils.theme.ThemeColorUtils
|
||||||
|
import com.owncloud.android.utils.theme.ThemeDrawableUtils
|
||||||
|
|
||||||
|
@Suppress("LongParameterList", "TooManyFunctions")
|
||||||
|
class OCFileListDelegate(
|
||||||
|
private val context: Context,
|
||||||
|
private val ocFileListFragmentInterface: OCFileListFragmentInterface,
|
||||||
|
private val user: User,
|
||||||
|
private val storageManager: FileDataStorageManager,
|
||||||
|
private val hideItemOptions: Boolean,
|
||||||
|
private val preferences: AppPreferences,
|
||||||
|
private val gridView: Boolean,
|
||||||
|
private val transferServiceGetter: ComponentsGetter,
|
||||||
|
private val showMetadata: Boolean,
|
||||||
|
private var showShareAvatar: Boolean
|
||||||
|
) {
|
||||||
|
private val checkedFiles: MutableSet<OCFile> = HashSet()
|
||||||
|
private var highlightedItem: OCFile? = null
|
||||||
|
var isMultiSelect = false
|
||||||
|
private val asyncTasks: MutableList<ThumbnailGenerationTask> = ArrayList()
|
||||||
|
fun setHighlightedItem(highlightedItem: OCFile?) {
|
||||||
|
this.highlightedItem = highlightedItem
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isCheckedFile(file: OCFile): Boolean {
|
||||||
|
return checkedFiles.contains(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addCheckedFile(file: OCFile) {
|
||||||
|
checkedFiles.add(file)
|
||||||
|
highlightedItem = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeCheckedFile(file: OCFile) {
|
||||||
|
checkedFiles.remove(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addToCheckedFiles(files: List<OCFile>?) {
|
||||||
|
checkedFiles.addAll(files!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
val checkedItems: Set<OCFile>
|
||||||
|
get() = checkedFiles
|
||||||
|
|
||||||
|
fun setCheckedItem(files: Set<OCFile>?) {
|
||||||
|
checkedFiles.clear()
|
||||||
|
checkedFiles.addAll(files!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearCheckedItems() {
|
||||||
|
checkedFiles.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bindGridViewHolder(gridViewHolder: ListGridImageViewHolder, file: OCFile) {
|
||||||
|
// thumbnail
|
||||||
|
gridViewHolder.thumbnail.tag = file.fileId
|
||||||
|
DisplayUtils.setThumbnail(
|
||||||
|
file,
|
||||||
|
gridViewHolder.thumbnail,
|
||||||
|
user,
|
||||||
|
storageManager,
|
||||||
|
asyncTasks,
|
||||||
|
gridView,
|
||||||
|
context,
|
||||||
|
gridViewHolder.shimmerThumbnail,
|
||||||
|
preferences
|
||||||
|
)
|
||||||
|
// item layout + click listeners
|
||||||
|
bindGridItemLayout(file, gridViewHolder)
|
||||||
|
|
||||||
|
// unread comments
|
||||||
|
bindUnreadComments(file, gridViewHolder)
|
||||||
|
|
||||||
|
// multiSelect (Checkbox)
|
||||||
|
if (isMultiSelect) {
|
||||||
|
gridViewHolder.checkbox.visibility = View.VISIBLE
|
||||||
|
} else {
|
||||||
|
gridViewHolder.checkbox.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
// download state
|
||||||
|
gridViewHolder.localFileIndicator.visibility = View.INVISIBLE // default first
|
||||||
|
|
||||||
|
// metadata (downloaded, favorite)
|
||||||
|
bindGridMetadataViews(file, gridViewHolder)
|
||||||
|
|
||||||
|
// shares
|
||||||
|
val shouldHideShare = gridView || hideItemOptions || file.isFolder && !file.canReshare()
|
||||||
|
if (shouldHideShare) {
|
||||||
|
gridViewHolder.shared.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
showShareIcon(gridViewHolder, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindUnreadComments(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
|
||||||
|
if (file.unreadCommentsCount > 0) {
|
||||||
|
gridViewHolder.unreadComments.visibility = View.VISIBLE
|
||||||
|
gridViewHolder.unreadComments.setOnClickListener {
|
||||||
|
ocFileListFragmentInterface
|
||||||
|
.showActivityDetailView(file)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gridViewHolder.unreadComments.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindGridItemLayout(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
|
||||||
|
if (highlightedItem != null && file.fileId == highlightedItem!!.fileId) {
|
||||||
|
gridViewHolder.itemLayout.setBackgroundColor(
|
||||||
|
context.resources
|
||||||
|
.getColor(R.color.selected_item_background)
|
||||||
|
)
|
||||||
|
} else if (isCheckedFile(file)) {
|
||||||
|
gridViewHolder.itemLayout.setBackgroundColor(
|
||||||
|
context.resources
|
||||||
|
.getColor(R.color.selected_item_background)
|
||||||
|
)
|
||||||
|
gridViewHolder.checkbox.setImageDrawable(
|
||||||
|
ThemeDrawableUtils.tintDrawable(
|
||||||
|
R.drawable.ic_checkbox_marked,
|
||||||
|
ThemeColorUtils.primaryColor(context)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
gridViewHolder.itemLayout.setBackgroundColor(context.resources.getColor(R.color.bg_default))
|
||||||
|
gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline)
|
||||||
|
}
|
||||||
|
gridViewHolder.itemLayout.setOnClickListener { ocFileListFragmentInterface.onItemClicked(file) }
|
||||||
|
if (!hideItemOptions) {
|
||||||
|
gridViewHolder.itemLayout.isLongClickable = true
|
||||||
|
gridViewHolder.itemLayout.setOnLongClickListener {
|
||||||
|
ocFileListFragmentInterface.onLongItemClicked(
|
||||||
|
file
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindGridMetadataViews(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
|
||||||
|
if (showMetadata) {
|
||||||
|
showLocalFileIndicator(file, gridViewHolder)
|
||||||
|
gridViewHolder.favorite.visibility = if (file.isFavorite) View.VISIBLE else View.GONE
|
||||||
|
} else {
|
||||||
|
gridViewHolder.localFileIndicator.visibility = View.GONE
|
||||||
|
gridViewHolder.favorite.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showLocalFileIndicator(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
|
||||||
|
val operationsServiceBinder = transferServiceGetter.operationsServiceBinder
|
||||||
|
val fileDownloaderBinder = transferServiceGetter.fileDownloaderBinder
|
||||||
|
val fileUploaderBinder = transferServiceGetter.fileUploaderBinder
|
||||||
|
when {
|
||||||
|
operationsServiceBinder?.isSynchronizing(user, file) == true ||
|
||||||
|
fileDownloaderBinder?.isDownloading(user, file) == true ||
|
||||||
|
fileUploaderBinder?.isUploading(user, file) == true -> {
|
||||||
|
// synchronizing, downloading or uploading
|
||||||
|
gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing)
|
||||||
|
gridViewHolder.localFileIndicator.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
file.etagInConflict != null -> {
|
||||||
|
// conflict
|
||||||
|
gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing_error)
|
||||||
|
gridViewHolder.localFileIndicator.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
file.isDown -> {
|
||||||
|
// downloaded
|
||||||
|
gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synced)
|
||||||
|
gridViewHolder.localFileIndicator.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showShareIcon(gridViewHolder: ListGridImageViewHolder, file: OCFile) {
|
||||||
|
val sharedIconView = gridViewHolder.shared
|
||||||
|
if (gridViewHolder is OCFileListItemViewHolder || file.unreadCommentsCount == 0) {
|
||||||
|
sharedIconView.visibility = View.VISIBLE
|
||||||
|
if (file.isSharedWithSharee || file.isSharedWithMe) {
|
||||||
|
if (showShareAvatar) {
|
||||||
|
sharedIconView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
sharedIconView.visibility = View.VISIBLE
|
||||||
|
sharedIconView.setImageResource(R.drawable.shared_via_users)
|
||||||
|
sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared)
|
||||||
|
}
|
||||||
|
} else if (file.isSharedViaLink) {
|
||||||
|
sharedIconView.setImageResource(R.drawable.shared_via_link)
|
||||||
|
sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared_via_link)
|
||||||
|
} else {
|
||||||
|
sharedIconView.setImageResource(R.drawable.ic_unshared)
|
||||||
|
sharedIconView.contentDescription = context.getString(R.string.shared_icon_share)
|
||||||
|
}
|
||||||
|
sharedIconView.setOnClickListener { ocFileListFragmentInterface.onShareIconClick(file) }
|
||||||
|
} else {
|
||||||
|
sharedIconView.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelAllPendingTasks() {
|
||||||
|
for (task in asyncTasks) {
|
||||||
|
task.cancel(true)
|
||||||
|
if (task.getMethod != null) {
|
||||||
|
Log_OC.d(TAG, "cancel: abort get method directly")
|
||||||
|
task.getMethod.abort()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
asyncTasks.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setShowShareAvatar(bool: Boolean) {
|
||||||
|
showShareAvatar = bool
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = OCFileListDelegate::class.java.simpleName
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.owncloud.android.databinding.ListFooterBinding
|
||||||
|
|
||||||
|
internal class OCFileListFooterViewHolder(var binding: ListFooterBinding) : RecyclerView.ViewHolder(
|
||||||
|
binding.root
|
||||||
|
) {
|
||||||
|
val footerText
|
||||||
|
get() = binding.footerText
|
||||||
|
|
||||||
|
val loadingProgressBar
|
||||||
|
get() = binding.loadingProgressBar
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.elyeproj.loaderviewlibrary.LoaderImageView
|
||||||
|
import com.owncloud.android.databinding.GridImageBinding
|
||||||
|
|
||||||
|
internal class OCFileListGridImageViewHolder(var binding: GridImageBinding) :
|
||||||
|
RecyclerView.ViewHolder(
|
||||||
|
binding.root
|
||||||
|
),
|
||||||
|
ListGridImageViewHolder {
|
||||||
|
override val thumbnail: ImageView
|
||||||
|
get() = binding.thumbnail
|
||||||
|
override val shimmerThumbnail: LoaderImageView
|
||||||
|
get() = binding.thumbnailShimmer
|
||||||
|
override val favorite: ImageView
|
||||||
|
get() = binding.favoriteAction
|
||||||
|
override val localFileIndicator: ImageView
|
||||||
|
get() = binding.localFileIndicator
|
||||||
|
override val shared: ImageView
|
||||||
|
get() = binding.sharedIcon
|
||||||
|
override val checkbox: ImageView
|
||||||
|
get() = binding.customCheckbox
|
||||||
|
override val itemLayout: View
|
||||||
|
get() = binding.ListItemLayout
|
||||||
|
override val unreadComments: ImageView
|
||||||
|
get() = binding.unreadComments
|
||||||
|
|
||||||
|
init {
|
||||||
|
binding.favoriteAction.drawable.mutate()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.elyeproj.loaderviewlibrary.LoaderImageView
|
||||||
|
import com.owncloud.android.databinding.GridItemBinding
|
||||||
|
|
||||||
|
internal class OCFileListGridItemViewHolder(var binding: GridItemBinding) :
|
||||||
|
RecyclerView.ViewHolder(
|
||||||
|
binding.root
|
||||||
|
),
|
||||||
|
ListGridItemViewHolder {
|
||||||
|
override val fileName: TextView
|
||||||
|
get() = binding.Filename
|
||||||
|
override val thumbnail: ImageView
|
||||||
|
get() = binding.thumbnail
|
||||||
|
override val shimmerThumbnail: LoaderImageView
|
||||||
|
get() = binding.thumbnailShimmer
|
||||||
|
override val favorite: ImageView
|
||||||
|
get() = binding.favoriteAction
|
||||||
|
override val localFileIndicator: ImageView
|
||||||
|
get() = binding.localFileIndicator
|
||||||
|
override val shared: ImageView
|
||||||
|
get() = binding.sharedIcon
|
||||||
|
override val checkbox: ImageView
|
||||||
|
get() = binding.customCheckbox
|
||||||
|
override val itemLayout: View
|
||||||
|
get() = binding.ListItemLayout
|
||||||
|
override val unreadComments: ImageView
|
||||||
|
get() = binding.unreadComments
|
||||||
|
|
||||||
|
init {
|
||||||
|
binding.favoriteAction.drawable.mutate()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.owncloud.android.databinding.ListHeaderBinding
|
||||||
|
|
||||||
|
internal class OCFileListHeaderViewHolder(var binding: ListHeaderBinding) : RecyclerView.ViewHolder(
|
||||||
|
binding.root
|
||||||
|
) {
|
||||||
|
val headerText
|
||||||
|
get() = binding.headerText
|
||||||
|
|
||||||
|
val headerView
|
||||||
|
get() = binding.headerView
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.elyeproj.loaderviewlibrary.LoaderImageView
|
||||||
|
import com.owncloud.android.databinding.ListItemBinding
|
||||||
|
import com.owncloud.android.ui.AvatarGroupLayout
|
||||||
|
|
||||||
|
internal class OCFileListItemViewHolder(private var binding: ListItemBinding) :
|
||||||
|
RecyclerView.ViewHolder(
|
||||||
|
binding.root
|
||||||
|
),
|
||||||
|
ListItemViewHolder {
|
||||||
|
override val fileSize: TextView
|
||||||
|
get() = binding.fileSize
|
||||||
|
override val fileSizeSeparator: View
|
||||||
|
get() = binding.fileSeparator
|
||||||
|
override val lastModification: TextView
|
||||||
|
get() = binding.lastMod
|
||||||
|
override val overflowMenu: ImageView
|
||||||
|
get() = binding.overflowMenu
|
||||||
|
override val sharedAvatars: AvatarGroupLayout
|
||||||
|
get() = binding.sharedAvatars
|
||||||
|
override val fileName: TextView
|
||||||
|
get() = binding.Filename
|
||||||
|
override val thumbnail: ImageView
|
||||||
|
get() = binding.thumbnail
|
||||||
|
override val shimmerThumbnail: LoaderImageView
|
||||||
|
get() = binding.thumbnailShimmer
|
||||||
|
override val favorite: ImageView
|
||||||
|
get() = binding.favoriteAction
|
||||||
|
override val localFileIndicator: ImageView
|
||||||
|
get() = binding.localFileIndicator
|
||||||
|
override val shared: ImageView
|
||||||
|
get() = binding.sharedIcon
|
||||||
|
override val checkbox: ImageView
|
||||||
|
get() = binding.customCheckbox
|
||||||
|
override val itemLayout: View
|
||||||
|
get() = binding.ListItemLayout
|
||||||
|
override val unreadComments: ImageView
|
||||||
|
get() = binding.unreadComments
|
||||||
|
|
||||||
|
init {
|
||||||
|
binding.favoriteAction.drawable.mutate()
|
||||||
|
}
|
||||||
|
}
|
|
@ -100,7 +100,7 @@ public class GallerySearchTask extends AsyncTask<Void, Void, GallerySearchTask.R
|
||||||
boolean emptySearch = parseMedia(startDate, endDate, result.getData());
|
boolean emptySearch = parseMedia(startDate, endDate, result.getData());
|
||||||
long lastTimeStamp = findLastTimestamp(result.getData());
|
long lastTimeStamp = findLastTimestamp(result.getData());
|
||||||
|
|
||||||
photoFragment.getAdapter().showAllGalleryItems(storageManager);
|
photoFragment.showAllGalleryItems();
|
||||||
|
|
||||||
return new Result(result.isSuccess(), emptySearch, lastTimeStamp);
|
return new Result(result.isSuccess(), emptySearch, lastTimeStamp);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -37,7 +37,6 @@ import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||||
import com.owncloud.android.ui.adapter.LocalFileListAdapter;
|
import com.owncloud.android.ui.adapter.LocalFileListAdapter;
|
||||||
import com.owncloud.android.ui.adapter.OCFileListAdapter;
|
|
||||||
import com.owncloud.android.utils.DisplayUtils;
|
import com.owncloud.android.utils.DisplayUtils;
|
||||||
import com.owncloud.android.utils.theme.ThemeButtonUtils;
|
import com.owncloud.android.utils.theme.ThemeButtonUtils;
|
||||||
import com.owncloud.android.utils.theme.ThemeCheckableUtils;
|
import com.owncloud.android.utils.theme.ThemeCheckableUtils;
|
||||||
|
@ -197,7 +196,7 @@ public class ConflictsResolveDialog extends DialogFragment {
|
||||||
existingFile.getModificationTimestamp()));
|
existingFile.getModificationTimestamp()));
|
||||||
|
|
||||||
binding.existingThumbnail.setTag(existingFile.getFileId());
|
binding.existingThumbnail.setTag(existingFile.getFileId());
|
||||||
OCFileListAdapter.setThumbnail(existingFile,
|
DisplayUtils.setThumbnail(existingFile,
|
||||||
binding.existingThumbnail,
|
binding.existingThumbnail,
|
||||||
user,
|
user,
|
||||||
new FileDataStorageManager(user,
|
new FileDataStorageManager(user,
|
||||||
|
|
|
@ -31,6 +31,8 @@ import com.owncloud.android.R;
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.lib.common.utils.Log_OC;
|
import com.owncloud.android.lib.common.utils.Log_OC;
|
||||||
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||||
|
import com.owncloud.android.ui.adapter.CommonOCFileListAdapterInterface;
|
||||||
|
import com.owncloud.android.ui.adapter.GalleryAdapter;
|
||||||
import com.owncloud.android.ui.asynctasks.GallerySearchTask;
|
import com.owncloud.android.ui.asynctasks.GallerySearchTask;
|
||||||
import com.owncloud.android.ui.events.ChangeMenuEvent;
|
import com.owncloud.android.ui.events.ChangeMenuEvent;
|
||||||
|
|
||||||
|
@ -50,6 +52,7 @@ public class GalleryFragment extends OCFileListFragment {
|
||||||
private long endDate;
|
private long endDate;
|
||||||
private long daySpan = 30;
|
private long daySpan = 30;
|
||||||
private int limit = 300;
|
private int limit = 300;
|
||||||
|
private GalleryAdapter mAdapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -87,18 +90,45 @@ public class GalleryFragment extends OCFileListFragment {
|
||||||
@Override
|
@Override
|
||||||
public void onActivityCreated(Bundle savedInstanceState) {
|
public void onActivityCreated(Bundle savedInstanceState) {
|
||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
mAdapter.setShowMetadata(false);
|
|
||||||
|
|
||||||
currentSearchType = SearchType.GALLERY_SEARCH;
|
currentSearchType = SearchType.GALLERY_SEARCH;
|
||||||
|
|
||||||
switchToGridView();
|
|
||||||
|
|
||||||
menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT;
|
menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT;
|
||||||
requireActivity().invalidateOptionsMenu();
|
requireActivity().invalidateOptionsMenu();
|
||||||
|
|
||||||
handleSearchEvent();
|
handleSearchEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setAdapter(Bundle args) {
|
||||||
|
mAdapter = new GalleryAdapter(requireContext(),
|
||||||
|
accountManager.getUser(),
|
||||||
|
this,
|
||||||
|
preferences,
|
||||||
|
mContainerActivity);
|
||||||
|
|
||||||
|
// val spacing = resources.getDimensionPixelSize(R.dimen.media_grid_spacing)
|
||||||
|
// binding.list.addItemDecoration(MediaGridItemDecoration(spacing))
|
||||||
|
setRecyclerViewAdapter(mAdapter);
|
||||||
|
|
||||||
|
|
||||||
|
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), getColumnsCount());
|
||||||
|
// ((GridLayoutManager) layoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||||
|
// @Override
|
||||||
|
// public int getSpanSize(int position) {
|
||||||
|
// if (position == getAdapter().getItemCount() - 1 ||
|
||||||
|
// position == 0 && getAdapter().shouldShowHeader()) {
|
||||||
|
// return ((GridLayoutManager) layoutManager).getSpanCount();
|
||||||
|
// } else {
|
||||||
|
// return 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
mAdapter.setLayoutManager(layoutManager);
|
||||||
|
getRecyclerView().setLayoutManager(layoutManager);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
super.onRefresh();
|
super.onRefresh();
|
||||||
|
@ -106,6 +136,11 @@ public class GalleryFragment extends OCFileListFragment {
|
||||||
handleSearchEvent();
|
handleSearchEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommonOCFileListAdapterInterface getCommonAdapter() {
|
||||||
|
return mAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
@ -161,7 +196,7 @@ public class GalleryFragment extends OCFileListFragment {
|
||||||
setEmptyListMessage(SearchType.GALLERY_SEARCH);
|
setEmptyListMessage(SearchType.GALLERY_SEARCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (emptySearch && getAdapter().getItemCount() > 0) {
|
if (emptySearch && mAdapter.getItemCount() > 0) {
|
||||||
Log_OC.d(this, "End gallery search");
|
Log_OC.d(this, "End gallery search");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -234,8 +269,7 @@ public class GalleryFragment extends OCFileListFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void showAllGalleryItems() {
|
||||||
public boolean isGalleryFragment() {
|
mAdapter.showAllGalleryItems(mContainerActivity.getStorageManager());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,6 +78,7 @@ import com.owncloud.android.ui.activity.FileDisplayActivity;
|
||||||
import com.owncloud.android.ui.activity.FolderPickerActivity;
|
import com.owncloud.android.ui.activity.FolderPickerActivity;
|
||||||
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
|
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
|
||||||
import com.owncloud.android.ui.activity.UploadFilesActivity;
|
import com.owncloud.android.ui.activity.UploadFilesActivity;
|
||||||
|
import com.owncloud.android.ui.adapter.CommonOCFileListAdapterInterface;
|
||||||
import com.owncloud.android.ui.adapter.OCFileListAdapter;
|
import com.owncloud.android.ui.adapter.OCFileListAdapter;
|
||||||
import com.owncloud.android.ui.dialog.ChooseRichDocumentsTemplateDialogFragment;
|
import com.owncloud.android.ui.dialog.ChooseRichDocumentsTemplateDialogFragment;
|
||||||
import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment;
|
import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment;
|
||||||
|
@ -189,7 +190,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
protected FileFragment.ContainerActivity mContainerActivity;
|
protected FileFragment.ContainerActivity mContainerActivity;
|
||||||
|
|
||||||
protected OCFile mFile;
|
protected OCFile mFile;
|
||||||
protected OCFileListAdapter mAdapter;
|
private OCFileListAdapter mAdapter;
|
||||||
protected boolean mOnlyFoldersClickable;
|
protected boolean mOnlyFoldersClickable;
|
||||||
protected boolean mFileSelectable;
|
protected boolean mFileSelectable;
|
||||||
|
|
||||||
|
@ -326,7 +327,9 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
if (mAdapter != null) {
|
||||||
mAdapter.cancelAllPendingTasks();
|
mAdapter.cancelAllPendingTasks();
|
||||||
|
}
|
||||||
|
|
||||||
if (getActivity() != null) {
|
if (getActivity() != null) {
|
||||||
getActivity().getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT);
|
getActivity().getIntent().removeExtra(OCFileListFragment.SEARCH_EVENT);
|
||||||
|
@ -349,18 +352,8 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
mOnlyFoldersClickable = args != null && args.getBoolean(ARG_ONLY_FOLDERS_CLICKABLE, false);
|
mOnlyFoldersClickable = args != null && args.getBoolean(ARG_ONLY_FOLDERS_CLICKABLE, false);
|
||||||
mFileSelectable = args != null && args.getBoolean(ARG_FILE_SELECTABLE, false);
|
mFileSelectable = args != null && args.getBoolean(ARG_FILE_SELECTABLE, false);
|
||||||
mLimitToMimeType = args != null ? args.getString(ARG_MIMETYPE, "") : "";
|
mLimitToMimeType = args != null ? args.getString(ARG_MIMETYPE, "") : "";
|
||||||
boolean hideItemOptions = args != null && args.getBoolean(ARG_HIDE_ITEM_OPTIONS, false);
|
|
||||||
|
|
||||||
mAdapter = new OCFileListAdapter(
|
setAdapter(args);
|
||||||
getActivity(),
|
|
||||||
accountManager.getUser(),
|
|
||||||
preferences,
|
|
||||||
mContainerActivity,
|
|
||||||
this,
|
|
||||||
hideItemOptions,
|
|
||||||
isGridViewPreferred(mFile)
|
|
||||||
);
|
|
||||||
setRecyclerViewAdapter(mAdapter);
|
|
||||||
|
|
||||||
mHideFab = args != null && args.getBoolean(ARG_HIDE_FAB, false);
|
mHideFab = args != null && args.getBoolean(ARG_HIDE_FAB, false);
|
||||||
|
|
||||||
|
@ -406,6 +399,22 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
listDirectory(false, false);
|
listDirectory(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setAdapter(Bundle args) {
|
||||||
|
boolean hideItemOptions = args != null && args.getBoolean(ARG_HIDE_ITEM_OPTIONS, false);
|
||||||
|
|
||||||
|
mAdapter = new OCFileListAdapter(
|
||||||
|
getActivity(),
|
||||||
|
accountManager.getUser(),
|
||||||
|
preferences,
|
||||||
|
mContainerActivity,
|
||||||
|
this,
|
||||||
|
hideItemOptions,
|
||||||
|
isGridViewPreferred(mFile)
|
||||||
|
);
|
||||||
|
|
||||||
|
setRecyclerViewAdapter(mAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
protected void prepareCurrentSearch(SearchEvent event) {
|
protected void prepareCurrentSearch(SearchEvent event) {
|
||||||
if (isSearchEventSet(event)) {
|
if (isSearchEventSet(event)) {
|
||||||
|
|
||||||
|
@ -691,7 +700,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
// hide FAB in multi selection mode
|
// hide FAB in multi selection mode
|
||||||
setFabVisible(false);
|
setFabVisible(false);
|
||||||
|
|
||||||
mAdapter.setMultiSelect(true);
|
getCommonAdapter().setMultiSelect(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,12 +709,12 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
final int checkedCount = mAdapter.getCheckedItems().size();
|
Set<OCFile> checkedFiles = getCommonAdapter().getCheckedItems();
|
||||||
Set<OCFile> checkedFiles = mAdapter.getCheckedItems();
|
final int checkedCount = checkedFiles.size();
|
||||||
String title = getResources().getQuantityString(R.plurals.items_selected_count, checkedCount, checkedCount);
|
String title = getResources().getQuantityString(R.plurals.items_selected_count, checkedCount, checkedCount);
|
||||||
mode.setTitle(title);
|
mode.setTitle(title);
|
||||||
FileMenuFilter mf = new FileMenuFilter(
|
FileMenuFilter mf = new FileMenuFilter(
|
||||||
mAdapter.getFiles().size(),
|
getCommonAdapter().getFilesCount(),
|
||||||
checkedFiles,
|
checkedFiles,
|
||||||
mContainerActivity,
|
mContainerActivity,
|
||||||
getActivity(),
|
getActivity(),
|
||||||
|
@ -728,7 +737,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
Set<OCFile> checkedFiles = mAdapter.getCheckedItems();
|
Set<OCFile> checkedFiles = getCommonAdapter().getCheckedItems();
|
||||||
return onFileActionChosen(item, checkedFiles);
|
return onFileActionChosen(item, checkedFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -744,8 +753,8 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
setFabVisible(true);
|
setFabVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
mAdapter.setMultiSelect(false);
|
getCommonAdapter().setMultiSelect(false);
|
||||||
mAdapter.clearCheckedItems();
|
getCommonAdapter().clearCheckedItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void storeStateIn(Bundle outState) {
|
public void storeStateIn(Bundle outState) {
|
||||||
|
@ -865,10 +874,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
* @param file The concerned OCFile by the selection/deselection
|
* @param file The concerned OCFile by the selection/deselection
|
||||||
*/
|
*/
|
||||||
private void toggleItemToCheckedList(OCFile file) {
|
private void toggleItemToCheckedList(OCFile file) {
|
||||||
if (getAdapter().isCheckedFile(file)) {
|
if (getCommonAdapter().isCheckedFile(file)) {
|
||||||
getAdapter().removeCheckedFile(file);
|
getCommonAdapter().removeCheckedFile(file);
|
||||||
} else {
|
} else {
|
||||||
getAdapter().addCheckedFile(file);
|
getCommonAdapter().addCheckedFile(file);
|
||||||
}
|
}
|
||||||
updateActionModeFile(file);
|
updateActionModeFile(file);
|
||||||
}
|
}
|
||||||
|
@ -881,7 +890,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
mIsActionModeNew = false;
|
mIsActionModeNew = false;
|
||||||
if (mActiveActionMode != null) {
|
if (mActiveActionMode != null) {
|
||||||
mActiveActionMode.invalidate();
|
mActiveActionMode.invalidate();
|
||||||
mAdapter.notifyItemChanged(getAdapter().getItemPosition(file));
|
getCommonAdapter().notifyItemChanged(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -894,7 +903,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
toggleItemToCheckedList(file);
|
toggleItemToCheckedList(file);
|
||||||
} else {
|
} else {
|
||||||
actionBarActivity.startActionMode(mMultiChoiceModeListener);
|
actionBarActivity.startActionMode(mMultiChoiceModeListener);
|
||||||
getAdapter().addCheckedFile(file);
|
getCommonAdapter().addCheckedFile(file);
|
||||||
}
|
}
|
||||||
updateActionModeFile(file);
|
updateActionModeFile(file);
|
||||||
}
|
}
|
||||||
|
@ -904,11 +913,11 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClicked(OCFile file) {
|
public void onItemClicked(OCFile file) {
|
||||||
if (getAdapter().isMultiSelect()) {
|
if (getCommonAdapter().isMultiSelect()) {
|
||||||
toggleItemToCheckedList(file);
|
toggleItemToCheckedList(file);
|
||||||
} else {
|
} else {
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
int position = mAdapter.getItemPosition(file);
|
int position = getCommonAdapter().getItemPosition(file);
|
||||||
|
|
||||||
if (file.isFolder()) {
|
if (file.isFolder()) {
|
||||||
resetHeaderScrollingState();
|
resetHeaderScrollingState();
|
||||||
|
@ -1229,6 +1238,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
if (!directory.isFolder()) {
|
if (!directory.isFolder()) {
|
||||||
Log_OC.w(TAG, "You see, that is not a directory -> " + directory);
|
Log_OC.w(TAG, "You see, that is not a directory -> " + directory);
|
||||||
directory = storageManager.getFileById(directory.getParentId());
|
directory = storageManager.getFileById(directory.getParentId());
|
||||||
|
|
||||||
|
if (directory == null) {
|
||||||
|
return; // no files, wait for sync
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (searchView != null && !searchView.isIconified() && !fromSearch) {
|
if (searchView != null && !searchView.isIconified() && !fromSearch) {
|
||||||
|
@ -1379,6 +1392,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
getAdapter().notifyDataSetChanged();
|
getAdapter().notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommonOCFileListAdapterInterface getCommonAdapter() {
|
||||||
|
return mAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
public OCFileListAdapter getAdapter() {
|
public OCFileListAdapter getAdapter() {
|
||||||
return mAdapter;
|
return mAdapter;
|
||||||
}
|
}
|
||||||
|
@ -1587,7 +1604,11 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
|
|
||||||
|
|
||||||
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
|
FileDataStorageManager storageManager = mContainerActivity.getStorageManager();
|
||||||
int position = mAdapter.getItemPosition(storageManager.getFileByRemoteId(event.remoteId));
|
OCFile file = storageManager.getFileByRemoteId(event.remoteId);
|
||||||
|
int position = -1;
|
||||||
|
if (file != null) {
|
||||||
|
position = mAdapter.getItemPosition(file);
|
||||||
|
}
|
||||||
SetupEncryptionDialogFragment dialog = SetupEncryptionDialogFragment.newInstance(user, position);
|
SetupEncryptionDialogFragment dialog = SetupEncryptionDialogFragment.newInstance(user, position);
|
||||||
dialog.setTargetFragment(this, SetupEncryptionDialogFragment.SETUP_ENCRYPTION_REQUEST_CODE);
|
dialog.setTargetFragment(this, SetupEncryptionDialogFragment.SETUP_ENCRYPTION_REQUEST_CODE);
|
||||||
dialog.show(getParentFragmentManager(), SetupEncryptionDialogFragment.SETUP_ENCRYPTION_DIALOG_TAG);
|
dialog.show(getParentFragmentManager(), SetupEncryptionDialogFragment.SETUP_ENCRYPTION_DIALOG_TAG);
|
||||||
|
@ -1656,11 +1677,6 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
return searchFragment;
|
return searchFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isGalleryFragment() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* De-/select all elements in the current list view.
|
* De-/select all elements in the current list view.
|
||||||
*
|
*
|
||||||
|
@ -1672,7 +1688,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
|
||||||
if (select) {
|
if (select) {
|
||||||
ocFileListAdapter.addAllFilesToCheckedFiles();
|
ocFileListAdapter.addAllFilesToCheckedFiles();
|
||||||
} else {
|
} else {
|
||||||
ocFileListAdapter.removeAllFilesFromCheckedFiles();
|
ocFileListAdapter.clearCheckedItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < mAdapter.getItemCount(); i++) {
|
for (int i = 0; i < mAdapter.getItemCount(); i++) {
|
||||||
|
|
|
@ -69,7 +69,7 @@ class OCFileListSearchAsyncTask(
|
||||||
if (remoteOperationResult.resultData.isNullOrEmpty()) {
|
if (remoteOperationResult.resultData.isNullOrEmpty()) {
|
||||||
fragment.setEmptyView(event)
|
fragment.setEmptyView(event)
|
||||||
} else {
|
} else {
|
||||||
fragment.mAdapter.setData(
|
fragment.adapter.setData(
|
||||||
remoteOperationResult.resultData,
|
remoteOperationResult.resultData,
|
||||||
fragment.currentSearchType,
|
fragment.currentSearchType,
|
||||||
fileDataStorageManager,
|
fileDataStorageManager,
|
||||||
|
@ -85,7 +85,7 @@ class OCFileListSearchAsyncTask(
|
||||||
fragmentReference.get()?.let { fragment ->
|
fragmentReference.get()?.let { fragment ->
|
||||||
fragment.isLoading = false
|
fragment.isLoading = false
|
||||||
if (!isCancelled) {
|
if (!isCancelled) {
|
||||||
fragment.mAdapter.notifyDataSetChanged()
|
fragment.adapter.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ class SharedListFragment : OCFileListFragment(), Injectable {
|
||||||
|
|
||||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||||
super.onActivityCreated(savedInstanceState)
|
super.onActivityCreated(savedInstanceState)
|
||||||
mAdapter.setShowMetadata(false)
|
adapter.setShowMetadata(false)
|
||||||
currentSearchType = SearchType.SHARED_FILTER
|
currentSearchType = SearchType.SHARED_FILTER
|
||||||
searchEvent = SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER)
|
searchEvent = SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER)
|
||||||
menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT
|
menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT
|
||||||
|
|
|
@ -48,8 +48,4 @@ public interface OCFileListFragmentInterface {
|
||||||
boolean isLoading();
|
boolean isLoading();
|
||||||
|
|
||||||
void onHeaderClicked();
|
void onHeaderClicked();
|
||||||
|
|
||||||
boolean isSearchFragment();
|
|
||||||
|
|
||||||
boolean isGalleryFragment();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import java.security.SecureRandom;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data holder utility to store & retrieve stuff
|
* Data holder utility to store & retrieve stuff
|
||||||
*/
|
*/
|
||||||
|
@ -39,6 +41,7 @@ public class DataHolderUtil {
|
||||||
@SuppressLint("TrulyRandom")
|
@SuppressLint("TrulyRandom")
|
||||||
private SecureRandom random = new SecureRandom();
|
private SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
|
@SuppressFBWarnings("MS_EXPOSE_REP")
|
||||||
public static synchronized DataHolderUtil getInstance() {
|
public static synchronized DataHolderUtil getInstance() {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
instance = new DataHolderUtil();
|
instance = new DataHolderUtil();
|
||||||
|
|
|
@ -46,6 +46,9 @@ import android.text.style.StyleSpan;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.bumptech.glide.GenericRequestBuilder;
|
import com.bumptech.glide.GenericRequestBuilder;
|
||||||
|
@ -56,13 +59,16 @@ import com.bumptech.glide.load.resource.file.FileToStreamDecoder;
|
||||||
import com.bumptech.glide.request.target.SimpleTarget;
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
import com.bumptech.glide.request.target.Target;
|
import com.bumptech.glide.request.target.Target;
|
||||||
import com.caverock.androidsvg.SVG;
|
import com.caverock.androidsvg.SVG;
|
||||||
|
import com.elyeproj.loaderviewlibrary.LoaderImageView;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.nextcloud.client.account.CurrentAccountProvider;
|
import com.nextcloud.client.account.CurrentAccountProvider;
|
||||||
import com.nextcloud.client.account.User;
|
import com.nextcloud.client.account.User;
|
||||||
import com.nextcloud.client.network.ClientFactory;
|
import com.nextcloud.client.network.ClientFactory;
|
||||||
|
import com.nextcloud.client.preferences.AppPreferences;
|
||||||
import com.owncloud.android.MainApp;
|
import com.owncloud.android.MainApp;
|
||||||
import com.owncloud.android.R;
|
import com.owncloud.android.R;
|
||||||
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
import com.owncloud.android.datamodel.ArbitraryDataProvider;
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager;
|
||||||
import com.owncloud.android.datamodel.OCFile;
|
import com.owncloud.android.datamodel.OCFile;
|
||||||
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
|
||||||
import com.owncloud.android.lib.common.OwnCloudAccount;
|
import com.owncloud.android.lib.common.OwnCloudAccount;
|
||||||
|
@ -89,17 +95,19 @@ import java.math.BigDecimal;
|
||||||
import java.net.IDN;
|
import java.net.IDN;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.appcompat.widget.AppCompatDrawableManager;
|
import androidx.appcompat.widget.AppCompatDrawableManager;
|
||||||
import androidx.core.content.res.ResourcesCompat;
|
import androidx.core.content.res.ResourcesCompat;
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.fragment.app.FragmentTransaction;
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
|
|
||||||
|
@ -130,6 +138,10 @@ public final class DisplayUtils {
|
||||||
private static final double BYTE_SIZE_DIVIDER_DOUBLE = 1024.0;
|
private static final double BYTE_SIZE_DIVIDER_DOUBLE = 1024.0;
|
||||||
private static final int DATE_TIME_PARTS_SIZE = 2;
|
private static final int DATE_TIME_PARTS_SIZE = 2;
|
||||||
|
|
||||||
|
public static final String MONTH_YEAR_PATTERN = "MMMM yyyy";
|
||||||
|
public static final String MONTH_PATTERN = "MMMM";
|
||||||
|
public static final String YEAR_PATTERN = "yyyy";
|
||||||
|
|
||||||
private static Map<String, String> mimeType2HumanReadable;
|
private static Map<String, String> mimeType2HumanReadable;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -820,4 +832,178 @@ public final class DisplayUtils {
|
||||||
return R.string.menu_item_sort_by_name_a_z;
|
return R.string.menu_item_sort_by_name_a_z;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getDateByPattern(long timestamp, Context context, String pattern) {
|
||||||
|
DateFormat df = new SimpleDateFormat(pattern, context.getResources().getConfiguration().locale);
|
||||||
|
df.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID()));
|
||||||
|
|
||||||
|
return df.format(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setThumbnail(OCFile file,
|
||||||
|
ImageView thumbnailView,
|
||||||
|
User user,
|
||||||
|
FileDataStorageManager storageManager,
|
||||||
|
List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks,
|
||||||
|
boolean gridView,
|
||||||
|
Context context) {
|
||||||
|
setThumbnail(file,
|
||||||
|
thumbnailView,
|
||||||
|
user,
|
||||||
|
storageManager,
|
||||||
|
asyncTasks,
|
||||||
|
gridView,
|
||||||
|
context,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setThumbnail(OCFile file,
|
||||||
|
ImageView thumbnailView,
|
||||||
|
User user,
|
||||||
|
FileDataStorageManager storageManager,
|
||||||
|
List<ThumbnailsCacheManager.ThumbnailGenerationTask> asyncTasks,
|
||||||
|
boolean gridView,
|
||||||
|
Context context,
|
||||||
|
LoaderImageView shimmerThumbnail,
|
||||||
|
AppPreferences preferences) {
|
||||||
|
if (file.isFolder()) {
|
||||||
|
stopShimmer(shimmerThumbnail, thumbnailView);
|
||||||
|
thumbnailView.setImageDrawable(MimeTypeUtil
|
||||||
|
.getFolderTypeIcon(file.isSharedWithMe() || file.isSharedWithSharee(),
|
||||||
|
file.isSharedViaLink(), file.isEncrypted(),
|
||||||
|
file.getMountType(), context));
|
||||||
|
} else {
|
||||||
|
if (file.getRemoteId() != null && file.isPreviewAvailable()) {
|
||||||
|
// Thumbnail in cache?
|
||||||
|
Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
|
||||||
|
ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.getRemoteId()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (thumbnail != null && !file.isUpdateThumbnailNeeded()) {
|
||||||
|
stopShimmer(shimmerThumbnail, thumbnailView);
|
||||||
|
|
||||||
|
if (MimeTypeUtil.isVideo(file)) {
|
||||||
|
Bitmap withOverlay = ThumbnailsCacheManager.addVideoOverlay(thumbnail);
|
||||||
|
thumbnailView.setImageBitmap(withOverlay);
|
||||||
|
} else {
|
||||||
|
if (gridView) {
|
||||||
|
BitmapUtils.setRoundedBitmapForGridMode(thumbnail, thumbnailView);
|
||||||
|
} else {
|
||||||
|
BitmapUtils.setRoundedBitmap(thumbnail, thumbnailView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// generate new thumbnail
|
||||||
|
if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) {
|
||||||
|
try {
|
||||||
|
final ThumbnailsCacheManager.ThumbnailGenerationTask task =
|
||||||
|
new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView,
|
||||||
|
storageManager,
|
||||||
|
user,
|
||||||
|
asyncTasks,
|
||||||
|
gridView);
|
||||||
|
if (thumbnail == null) {
|
||||||
|
Drawable drawable = MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
|
||||||
|
file.getFileName(),
|
||||||
|
user,
|
||||||
|
context);
|
||||||
|
if (drawable == null) {
|
||||||
|
drawable = ResourcesCompat.getDrawable(context.getResources(),
|
||||||
|
R.drawable.file_image,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
int px = ThumbnailsCacheManager.getThumbnailDimension();
|
||||||
|
thumbnail = BitmapUtils.drawableToBitmap(drawable, px, px);
|
||||||
|
}
|
||||||
|
final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable =
|
||||||
|
new ThumbnailsCacheManager.AsyncThumbnailDrawable(context.getResources(),
|
||||||
|
thumbnail, task);
|
||||||
|
|
||||||
|
if (shimmerThumbnail != null && shimmerThumbnail.getVisibility() == View.GONE) {
|
||||||
|
if (gridView) {
|
||||||
|
configShimmerGridImageSize(shimmerThumbnail, preferences.getGridColumns());
|
||||||
|
}
|
||||||
|
startShimmer(shimmerThumbnail, thumbnailView);
|
||||||
|
}
|
||||||
|
|
||||||
|
task.setListener(new ThumbnailsCacheManager.ThumbnailGenerationTask.Listener() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess() {
|
||||||
|
stopShimmer(shimmerThumbnail, thumbnailView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError() {
|
||||||
|
stopShimmer(shimmerThumbnail, thumbnailView);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
thumbnailView.setImageDrawable(asyncDrawable);
|
||||||
|
asyncTasks.add(task);
|
||||||
|
task.execute(new ThumbnailsCacheManager.ThumbnailGenerationTaskObject(file,
|
||||||
|
file.getRemoteId()));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Log_OC.d(TAG, "ThumbnailGenerationTask : " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("image/png".equalsIgnoreCase(file.getMimeType())) {
|
||||||
|
thumbnailView.setBackgroundColor(context.getResources().getColor(R.color.bg_default));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stopShimmer(shimmerThumbnail, thumbnailView);
|
||||||
|
thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
|
||||||
|
file.getFileName(),
|
||||||
|
user,
|
||||||
|
context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void startShimmer(LoaderImageView thumbnailShimmer, ImageView thumbnailView) {
|
||||||
|
thumbnailShimmer.setImageResource(R.drawable.background);
|
||||||
|
thumbnailShimmer.resetLoader();
|
||||||
|
thumbnailView.setVisibility(View.GONE);
|
||||||
|
thumbnailShimmer.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void stopShimmer(@Nullable LoaderImageView thumbnailShimmer, ImageView thumbnailView) {
|
||||||
|
if (thumbnailShimmer != null) {
|
||||||
|
thumbnailShimmer.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
thumbnailView.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void configShimmerGridImageSize(LoaderImageView thumbnailShimmer, float gridColumns) {
|
||||||
|
FrameLayout.LayoutParams targetLayoutParams = (FrameLayout.LayoutParams) thumbnailShimmer.getLayoutParams();
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Point screenSize = getScreenSize(thumbnailShimmer.getContext());
|
||||||
|
final int marginLeftAndRight = targetLayoutParams.leftMargin + targetLayoutParams.rightMargin;
|
||||||
|
final int size = Math.round(screenSize.x / gridColumns - marginLeftAndRight);
|
||||||
|
|
||||||
|
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(size, size);
|
||||||
|
params.setMargins(targetLayoutParams.leftMargin,
|
||||||
|
targetLayoutParams.topMargin,
|
||||||
|
targetLayoutParams.rightMargin,
|
||||||
|
targetLayoutParams.bottomMargin);
|
||||||
|
thumbnailShimmer.setLayoutParams(params);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
Log_OC.e("ConfigShimmer", exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Point getScreenSize(Context context) throws Exception {
|
||||||
|
final WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
if (windowManager != null) {
|
||||||
|
final Point displaySize = new Point();
|
||||||
|
windowManager.getDefaultDisplay().getSize(displaySize);
|
||||||
|
return displaySize;
|
||||||
|
} else {
|
||||||
|
throw new Exception("WindowManager not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
48
app/src/main/res/layout/gallery_header.xml
Normal file
48
app/src/main/res/layout/gallery_header.xml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
~
|
||||||
|
~ Nextcloud Android client application
|
||||||
|
~
|
||||||
|
~ @author Tobias Kaminsky
|
||||||
|
~ Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
~ Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
~
|
||||||
|
~ This program is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU Affero General Public License as published by
|
||||||
|
~ the Free Software Foundation, either version 3 of the License, or
|
||||||
|
~ (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ 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 Affero General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU Affero General Public License
|
||||||
|
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="@dimen/alternate_half_padding"
|
||||||
|
android:paddingTop="@dimen/alternate_half_padding"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/month"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="@dimen/standard_margin"
|
||||||
|
android:layout_marginTop="@dimen/standard_margin"
|
||||||
|
android:layout_marginEnd="@dimen/standard_half_margin"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="July" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/year"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/standard_margin"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
tools:text="2016" />
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Nextcloud Android client application
|
||||||
|
*
|
||||||
|
* @author Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Tobias Kaminsky
|
||||||
|
* Copyright (C) 2022 Nextcloud GmbH
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* 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 Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.owncloud.android.ui.adapter
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.nextcloud.client.account.User
|
||||||
|
import com.nextcloud.client.preferences.AppPreferences
|
||||||
|
import com.owncloud.android.datamodel.FileDataStorageManager
|
||||||
|
import com.owncloud.android.datamodel.GalleryItems
|
||||||
|
import com.owncloud.android.datamodel.OCFile
|
||||||
|
import com.owncloud.android.ui.activity.ComponentsGetter
|
||||||
|
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface
|
||||||
|
import junit.framework.Assert.assertEquals
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
|
||||||
|
class GalleryAdapterTest {
|
||||||
|
@Mock
|
||||||
|
lateinit var context: Context
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var user: User
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var ocFileListFragmentInterface: OCFileListFragmentInterface
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var preferences: AppPreferences
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var transferServiceGetter: ComponentsGetter
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
lateinit var storageManager: FileDataStorageManager
|
||||||
|
|
||||||
|
private lateinit var mocks: AutoCloseable
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
mocks = MockitoAnnotations.openMocks(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mocks.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testItemCount() {
|
||||||
|
whenever(transferServiceGetter.storageManager) doReturn storageManager
|
||||||
|
|
||||||
|
val sut = GalleryAdapter(
|
||||||
|
context,
|
||||||
|
user,
|
||||||
|
ocFileListFragmentInterface,
|
||||||
|
preferences,
|
||||||
|
transferServiceGetter
|
||||||
|
)
|
||||||
|
|
||||||
|
val list = listOf(
|
||||||
|
GalleryItems(1649317247, listOf(OCFile("/1.md"), OCFile("/2.md"))),
|
||||||
|
GalleryItems(1649317247, listOf(OCFile("/1.md"), OCFile("/2.md")))
|
||||||
|
)
|
||||||
|
|
||||||
|
sut.addFiles(list)
|
||||||
|
|
||||||
|
assertEquals(4, sut.getFilesCount())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
592
|
536
|
|
@ -1,2 +1,2 @@
|
||||||
DO NOT TOUCH; GENERATED BY DRONE
|
DO NOT TOUCH; GENERATED BY DRONE
|
||||||
<span class="mdl-layout-title">Lint Report: 92 warnings</span>
|
<span class="mdl-layout-title">Lint Report: 89 warnings</span>
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
<Bug pattern="ANDROID_EXTERNAL_FILE_ACCESS" />
|
<Bug pattern="ANDROID_EXTERNAL_FILE_ACCESS" />
|
||||||
<Bug pattern="BAS_BLOATED_ASSIGNMENT_SCOPE" />
|
<Bug pattern="BAS_BLOATED_ASSIGNMENT_SCOPE" />
|
||||||
<Bug pattern="IMC_IMMATURE_CLASS_BAD_SERIALVERSIONUID" />
|
<Bug pattern="IMC_IMMATURE_CLASS_BAD_SERIALVERSIONUID" />
|
||||||
|
<Bug pattern="EI_EXPOSE_REP" />
|
||||||
<Bug pattern="EI_EXPOSE_REP2" />
|
<Bug pattern="EI_EXPOSE_REP2" />
|
||||||
|
|
||||||
<!-- This is unmanageable for now due to large amount of interconnected static state -->
|
<!-- This is unmanageable for now due to large amount of interconnected static state -->
|
||||||
|
|
Loading…
Reference in a new issue