diff --git a/app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt b/app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt
new file mode 100644
index 0000000000..4baa14dc93
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/datamodel/GalleryItems.kt
@@ -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 .
+ */
+
+package com.owncloud.android.datamodel
+
+data class GalleryItems(val date: Long, val files: List)
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/CommonOCFileListAdapterInterface.kt b/app/src/main/java/com/owncloud/android/ui/adapter/CommonOCFileListAdapterInterface.kt
new file mode 100644
index 0000000000..35313769da
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/CommonOCFileListAdapterInterface.kt
@@ -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 .
+ */
+
+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
+ fun removeCheckedFile(file: OCFile)
+ fun notifyItemChanged(file: OCFile)
+ fun getFilesCount(): Int
+ fun setMultiSelect(boolean: Boolean)
+ fun clearCheckedItems()
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt
new file mode 100644
index 0000000000..dd72629c4f
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt
@@ -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 .
+ */
+
+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(), CommonOCFileListAdapterInterface, SectionedAdapter {
+ private var files: List = 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 {
+ 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) {
+ files = items
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryHeaderViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryHeaderViewHolder.kt
new file mode 100644
index 0000000000..d781a4dc2e
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryHeaderViewHolder.kt
@@ -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 .
+ */
+
+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)
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryItemViewHolder.kt
new file mode 100644
index 0000000000..0cdb993d3a
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryItemViewHolder.kt
@@ -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 .
+ */
+
+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
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt
new file mode 100644
index 0000000000..fa36ef8664
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt
@@ -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 .
+ */
+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
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridItemViewHolder.kt
new file mode 100644
index 0000000000..b53e329ccc
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridItemViewHolder.kt
@@ -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 .
+ */
+package com.owncloud.android.ui.adapter
+
+import android.widget.TextView
+
+internal interface ListGridItemViewHolder : ListGridImageViewHolder {
+ val fileName: TextView
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ListItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ListItemViewHolder.kt
new file mode 100644
index 0000000000..e084283d86
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/ListItemViewHolder.kt
@@ -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 .
+ */
+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
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
index 33a271976e..8b36196780 100644
--- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java
@@ -25,12 +25,10 @@
package com.owncloud.android.ui.adapter;
import android.accounts.AccountManager;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentValues;
-import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -39,11 +37,7 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.Filter;
-import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.TextView;
import com.elyeproj.loaderviewlibrary.LoaderImageView;
import com.nextcloud.client.account.User;
@@ -60,8 +54,6 @@ import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
import com.owncloud.android.datamodel.VirtualFolderType;
import com.owncloud.android.db.ProviderMeta;
-import com.owncloud.android.files.services.FileDownloader;
-import com.owncloud.android.files.services.FileUploader;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.model.RemoteFile;
@@ -70,20 +62,16 @@ import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.shares.ShareeUser;
import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.operations.RemoteOperationFailedException;
-import com.owncloud.android.services.OperationsService;
-import com.owncloud.android.ui.AvatarGroupLayout;
import com.owncloud.android.ui.activity.ComponentsGetter;
import com.owncloud.android.ui.fragment.SearchType;
import com.owncloud.android.ui.interfaces.OCFileListFragmentInterface;
import com.owncloud.android.ui.preview.PreviewTextFragment;
-import com.owncloud.android.utils.BitmapUtils;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.FileSortOrder;
import com.owncloud.android.utils.FileStorageUtils;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.theme.CapabilityUtils;
import com.owncloud.android.utils.theme.ThemeColorUtils;
-import com.owncloud.android.utils.theme.ThemeDrawableUtils;
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
import java.io.File;
@@ -91,26 +79,25 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
-import java.util.Vector;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import androidx.core.content.res.ResourcesCompat;
import androidx.recyclerview.widget.RecyclerView;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* This Adapter populates a RecyclerView with all files and folders in a Nextcloud instance.
*/
public class OCFileListAdapter extends RecyclerView.Adapter
- implements DisplayUtils.AvatarGenerationListener, FastScrollRecyclerView.SectionedAdapter {
+ implements DisplayUtils.AvatarGenerationListener,
+ CommonOCFileListAdapterInterface,
+ FastScrollRecyclerView.SectionedAdapter {
private static final int showFilenameColumnThreshold = 4;
- private final ComponentsGetter transferServiceGetter;
private final String userId;
private Activity activity;
private AppPreferences preferences;
@@ -119,14 +106,11 @@ public class OCFileListAdapter extends RecyclerView.Adapter checkedFiles;
private FileDataStorageManager mStorageManager;
private User user;
private OCFileListFragmentInterface ocFileListFragmentInterface;
- private FilesFilter mFilesFilter;
private OCFile currentDirectory;
private static final String TAG = OCFileListAdapter.class.getSimpleName();
@@ -135,18 +119,15 @@ public class OCFileListAdapter extends RecyclerView.Adapter asyncTasks = new ArrayList<>();
private boolean onlyOnDevice;
- private boolean showShareAvatar = false;
- private OCFile highlightedItem;
- private boolean showMetadata = true;
+ private final OCFileListDelegate ocFileListDelegate;
private FileSortOrder sortOrder;
private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM yyyy", Locale.getDefault());
public OCFileListAdapter(
Activity activity,
- User user,
+ @NonNull User user,
AppPreferences preferences,
ComponentsGetter transferServiceGetter,
OCFileListFragmentInterface ocFileListFragmentInterface,
@@ -159,53 +140,54 @@ public class OCFileListAdapter extends RecyclerView.Adapter();
+ mStorageManager = transferServiceGetter.getStorageManager();
- this.transferServiceGetter = transferServiceGetter;
-
- if (this.user != null) {
- AccountManager platformAccountManager = AccountManager.get(this.activity);
- userId = platformAccountManager.getUserData(this.user.toPlatformAccount(),
- com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
- } else {
- userId = "";
+ if (mStorageManager == null) {
+ mStorageManager = new FileDataStorageManager(user, activity.getContentResolver());
}
+ userId = AccountManager
+ .get(activity)
+ .getUserData(this.user.toPlatformAccount(),
+ com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
+
+ ocFileListDelegate = new OCFileListDelegate(activity,
+ ocFileListFragmentInterface,
+ user,
+ mStorageManager,
+ hideItemOptions,
+ preferences,
+ gridView,
+ transferServiceGetter,
+ true,
+ CapabilityUtils
+ .getCapability(activity)
+ .getVersion()
+ .isShareesOnDavSupported());
+
// initialise thumbnails cache on background thread
new ThumbnailsCacheManager.InitDiskCacheTask().execute();
}
public boolean isMultiSelect() {
- return multiSelect;
+ return ocFileListDelegate.isMultiSelect();
}
+ @SuppressLint("NotifyDataSetChanged")
public void setMultiSelect(boolean bool) {
- multiSelect = bool;
+ ocFileListDelegate.setMultiSelect(bool);
notifyDataSetChanged();
}
- public boolean isCheckedFile(OCFile file) {
- return checkedFiles.contains(file);
- }
-
- public void removeCheckedFile(OCFile file) {
- checkedFiles.remove(file);
- }
-
- public void addCheckedFile(OCFile file) {
- checkedFiles.add(file);
- highlightedItem = null;
+ public void removeCheckedFile(@NonNull OCFile file) {
+ ocFileListDelegate.removeCheckedFile(file);
}
public void addAllFilesToCheckedFiles() {
- checkedFiles.addAll(mFiles);
+ ocFileListDelegate.addToCheckedFiles(mFiles);
}
- public void removeAllFilesFromCheckedFiles() {
- checkedFiles.clear();
- }
-
- public int getItemPosition(OCFile file) {
+ public int getItemPosition(@NonNull OCFile file) {
int position = mFiles.indexOf(file);
if (shouldShowHeader()) {
@@ -357,23 +339,23 @@ public class OCFileListAdapter extends RecyclerView.Adapter ocFileListFragmentInterface.onHeaderClicked());
+ PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true);
+ headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked());
} else {
ListGridImageViewHolder gridViewHolder = (ListGridImageViewHolder) holder;
-
OCFile file = getItem(position);
if (file == null) {
@@ -381,311 +363,113 @@ public class OCFileListAdapter extends RecyclerView.Adapter ocFileListFragmentInterface.onItemClicked(file));
-
- if (!hideItemOptions) {
- gridViewHolder.getItemLayout().setLongClickable(true);
- gridViewHolder.getItemLayout().setOnLongClickListener(v ->
- ocFileListFragmentInterface.onLongItemClicked(file));
- }
-
- // unread comments
- if (file.getUnreadCommentsCount() > 0) {
- gridViewHolder.getUnreadComments().setVisibility(View.VISIBLE);
- gridViewHolder.getUnreadComments().setOnClickListener(view -> ocFileListFragmentInterface
- .showActivityDetailView(file));
- } else {
- gridViewHolder.getUnreadComments().setVisibility(View.GONE);
- }
+ ocFileListDelegate.bindGridViewHolder(gridViewHolder, file);
if (holder instanceof ListItemViewHolder) {
- ListItemViewHolder itemViewHolder = (ListItemViewHolder) holder;
-
- if ((file.isSharedWithMe() || file.isSharedWithSharee()) && !multiSelect && !gridView &&
- !hideItemOptions) {
- itemViewHolder.getSharedAvatars().setVisibility(View.VISIBLE);
- itemViewHolder.getSharedAvatars().removeAllViews();
-
- String fileOwner = file.getOwnerId();
- List sharees = file.getSharees();
-
- // use fileOwner if not oneself, then add at first
- ShareeUser fileOwnerSharee = new ShareeUser(fileOwner, file.getOwnerDisplayName(), ShareType.USER);
- if (!TextUtils.isEmpty(fileOwner) &&
- !fileOwner.equals(userId) &&
- !sharees.contains(fileOwnerSharee)) {
- sharees.add(fileOwnerSharee);
- }
-
- Collections.reverse(sharees);
-
- Log_OC.d(this, "sharees of " + file.getFileName() + ": " + sharees);
-
- itemViewHolder.getSharedAvatars().setAvatars(user, sharees);
- itemViewHolder.getSharedAvatars().setOnClickListener(
- view -> ocFileListFragmentInterface.onShareIconClick(file));
- } else {
- itemViewHolder.getSharedAvatars().setVisibility(View.GONE);
- itemViewHolder.getSharedAvatars().removeAllViews();
- }
-
- // npe fix: looks like file without local storage path somehow get here
- final String storagePath = file.getStoragePath();
- if (onlyOnDevice && storagePath != null) {
- File localFile = new File(storagePath);
- long localSize;
- if (localFile.isDirectory()) {
- localSize = FileStorageUtils.getFolderSize(localFile);
- } else {
- localSize = localFile.length();
- }
-
- itemViewHolder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(localSize));
- itemViewHolder.getFileSize().setVisibility(View.VISIBLE);
- itemViewHolder.getFileSizeSeparator().setVisibility(View.VISIBLE);
- } else {
- final long fileLength = file.getFileLength();
- if (fileLength >= 0) {
- itemViewHolder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(fileLength));
- itemViewHolder.getFileSize().setVisibility(View.VISIBLE);
- itemViewHolder.getFileSizeSeparator().setVisibility(View.VISIBLE);
- } else {
- itemViewHolder.getFileSize().setVisibility(View.GONE);
- itemViewHolder.getFileSizeSeparator().setVisibility(View.GONE);
- }
- }
-
- final long modificationTimestamp = file.getModificationTimestamp();
- if (modificationTimestamp > 0) {
- itemViewHolder.getLastModification().setText(DisplayUtils.getRelativeTimestamp(activity,
- modificationTimestamp));
- itemViewHolder.getLastModification().setVisibility(View.VISIBLE);
- } else if (file.getFirstShareTimestamp() > 0) {
- itemViewHolder.getLastModification().setText(
- DisplayUtils.getRelativeTimestamp(activity, file.getFirstShareTimestamp())
- );
- itemViewHolder.getLastModification().setVisibility(View.VISIBLE);
- } else {
- itemViewHolder.getLastModification().setVisibility(View.GONE);
- }
-
-
- if (multiSelect || gridView || hideItemOptions) {
- itemViewHolder.getOverflowMenu().setVisibility(View.GONE);
- } else {
- itemViewHolder.getOverflowMenu().setVisibility(View.VISIBLE);
- itemViewHolder.getOverflowMenu().setOnClickListener(view -> ocFileListFragmentInterface
- .onOverflowIconClicked(file, view));
- }
- }
-
- gridViewHolder.getLocalFileIndicator().setVisibility(View.INVISIBLE); // default first
-
- if (showMetadata) {
- OperationsService.OperationsServiceBinder operationsServiceBinder = transferServiceGetter.getOperationsServiceBinder();
- FileDownloader.FileDownloaderBinder fileDownloaderBinder = transferServiceGetter.getFileDownloaderBinder();
- FileUploader.FileUploaderBinder fileUploaderBinder = transferServiceGetter.getFileUploaderBinder();
- if (operationsServiceBinder != null && operationsServiceBinder.isSynchronizing(user, file)) {
- //synchronizing
- gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
- gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
- } else if (fileDownloaderBinder != null && fileDownloaderBinder.isDownloading(user, file)) {
- // downloading
- gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
- gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
- } else if (fileUploaderBinder != null && fileUploaderBinder.isUploading(user, file)) {
- //uploading
- gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing);
- gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
- } else if (file.getEtagInConflict() != null) {
- // conflict
- gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synchronizing_error);
- gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
-
- } else if (file.isDown()) {
- gridViewHolder.getLocalFileIndicator().setImageResource(R.drawable.ic_synced);
- gridViewHolder.getLocalFileIndicator().setVisibility(View.VISIBLE);
- }
-
- gridViewHolder.getFavorite().setVisibility(file.isFavorite() ? View.VISIBLE : View.GONE);
- } else {
- gridViewHolder.getLocalFileIndicator().setVisibility(View.GONE);
- gridViewHolder.getFavorite().setVisibility(View.GONE);
- }
-
- if (multiSelect) {
- gridViewHolder.getCheckbox().setVisibility(View.VISIBLE);
- } else {
- gridViewHolder.getCheckbox().setVisibility(View.GONE);
+ bindListItemViewHolder((ListItemViewHolder) gridViewHolder, file);
}
if (holder instanceof ListGridItemViewHolder) {
- ListGridItemViewHolder gridItemViewHolder = (ListGridItemViewHolder) holder;
-
- gridItemViewHolder.getFileName().setText(file.getDecryptedFileName());
-
- if (gridView && gridImage) {
- gridItemViewHolder.getFileName().setVisibility(View.GONE);
- } else {
- if (gridView && ocFileListFragmentInterface.getColumnsCount() > showFilenameColumnThreshold) {
- gridItemViewHolder.getFileName().setVisibility(View.GONE);
- } else {
- gridItemViewHolder.getFileName().setVisibility(View.VISIBLE);
- }
- }
- }
-
- if (gridView || hideItemOptions || (file.isFolder() && !file.canReshare())) {
- gridViewHolder.getShared().setVisibility(View.GONE);
- } else {
- showShareIcon(gridViewHolder, file);
+ bindListGridItemViewHolder((ListGridItemViewHolder) holder, file);
}
}
}
- public static void setThumbnail(OCFile file,
- ImageView thumbnailView,
- User user,
- FileDataStorageManager storageManager,
- List asyncTasks,
- boolean gridView,
- Context context) {
- setThumbnail(file, thumbnailView, user, storageManager, asyncTasks, gridView, context, null, null);
- }
+ private void bindListItemViewHolder(ListItemViewHolder holder, OCFile file) {
+ if ((file.isSharedWithMe() || file.isSharedWithSharee()) && !isMultiSelect() && !gridView &&
+ !hideItemOptions) {
+ holder.getSharedAvatars().setVisibility(View.VISIBLE);
+ holder.getSharedAvatars().removeAllViews();
- private static void setThumbnail(OCFile file,
- ImageView thumbnailView,
- User user,
- FileDataStorageManager storageManager,
- List 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));
+ String fileOwner = file.getOwnerId();
+ List sharees = file.getSharees();
+
+ // use fileOwner if not oneself, then add at first
+ ShareeUser fileOwnerSharee = new ShareeUser(fileOwner, file.getOwnerDisplayName(), ShareType.USER);
+ if (!TextUtils.isEmpty(fileOwner) &&
+ !fileOwner.equals(userId) &&
+ !sharees.contains(fileOwnerSharee)) {
+ sharees.add(fileOwnerSharee);
+ }
+
+ Collections.reverse(sharees);
+
+ Log_OC.d(this, "sharees of " + file.getFileName() + ": " + sharees);
+
+ holder.getSharedAvatars().setAvatars(user, sharees);
+ holder.getSharedAvatars().setOnClickListener(
+ view -> ocFileListFragmentInterface.onShareIconClick(file));
} else {
- if (file.getRemoteId() != null && file.isPreviewAvailable()) {
- // Thumbnail in cache?
- Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
- ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.getRemoteId()
- );
+ holder.getSharedAvatars().setVisibility(View.GONE);
+ holder.getSharedAvatars().removeAllViews();
+ }
- 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));
- }
+ // npe fix: looks like file without local storage path somehow get here
+ final String storagePath = file.getStoragePath();
+ if (onlyOnDevice && storagePath != null) {
+ File localFile = new File(storagePath);
+ long localSize;
+ if (localFile.isDirectory()) {
+ localSize = FileStorageUtils.getFolderSize(localFile);
} else {
- stopShimmer(shimmerThumbnail, thumbnailView);
- thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(),
- file.getFileName(),
- user,
- context));
+ localSize = localFile.length();
+ }
+
+ holder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(localSize));
+ holder.getFileSize().setVisibility(View.VISIBLE);
+ holder.getFileSizeSeparator().setVisibility(View.VISIBLE);
+ } else {
+ final long fileLength = file.getFileLength();
+ if (fileLength >= 0) {
+ holder.getFileSize().setText(DisplayUtils.bytesToHumanReadable(fileLength));
+ holder.getFileSize().setVisibility(View.VISIBLE);
+ holder.getFileSizeSeparator().setVisibility(View.VISIBLE);
+ } else {
+ holder.getFileSize().setVisibility(View.GONE);
+ holder.getFileSizeSeparator().setVisibility(View.GONE);
+ }
+ }
+
+ final long modificationTimestamp = file.getModificationTimestamp();
+ if (modificationTimestamp > 0) {
+ holder.getLastModification().setText(DisplayUtils.getRelativeTimestamp(activity,
+ modificationTimestamp));
+ holder.getLastModification().setVisibility(View.VISIBLE);
+ } else if (file.getFirstShareTimestamp() > 0) {
+ holder.getLastModification().setText(
+ DisplayUtils.getRelativeTimestamp(activity, file.getFirstShareTimestamp())
+ );
+ holder.getLastModification().setVisibility(View.VISIBLE);
+ } else {
+ holder.getLastModification().setVisibility(View.GONE);
+ }
+
+
+ if (isMultiSelect() || gridView || hideItemOptions) {
+ holder.getOverflowMenu().setVisibility(View.GONE);
+ } else {
+ holder.getOverflowMenu().setVisibility(View.VISIBLE);
+ holder.getOverflowMenu().setOnClickListener(view -> ocFileListFragmentInterface
+ .onOverflowIconClicked(file, view));
+ }
+ }
+
+ private void bindListGridItemViewHolder(ListGridItemViewHolder holder, OCFile file) {
+ holder.getFileName().setText(file.getDecryptedFileName());
+
+ boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file);
+ if (gridView && gridImage) {
+ holder.getFileName().setVisibility(View.GONE);
+ } else {
+ if (gridView && ocFileListFragmentInterface.getColumnsCount() > showFilenameColumnThreshold) {
+ holder.getFileName().setVisibility(View.GONE);
+ } else {
+ holder.getFileName().setVisibility(View.VISIBLE);
}
}
}
+
@Override
public void onViewAttachedToWindow(@NonNull RecyclerView.ViewHolder holder) {
if (holder instanceof ListGridImageViewHolder) {
@@ -697,51 +481,6 @@ public class OCFileListAdapter extends RecyclerView.Adapter ocFileListFragmentInterface.onShareIconClick(file));
- } else {
- sharedIconView.setVisibility(View.GONE);
- }
- }
-
/**
* Change the adapted directory for a new one
*
@@ -874,17 +586,18 @@ public class OCFileListAdapter extends RecyclerView.Adapter getCheckedItems() {
- return checkedFiles;
+ return ocFileListDelegate.getCheckedItems();
}
public void setCheckedItem(Set files) {
- checkedFiles.clear();
- checkedFiles.addAll(files);
+ ocFileListDelegate.setCheckedItem(files);
}
public void clearCheckedItems() {
- checkedFiles.clear();
+ ocFileListDelegate.clearCheckedItems();
+ }
+
+ public void setFiles(List files) {
+ mFiles = files;
}
public List getFiles() {
return mFiles;
}
- public Filter getFilter() {
- if (mFilesFilter == null) {
- mFilesFilter = new FilesFilter();
- }
- return mFilesFilter;
- }
-
public void resetLastTimestamp() {
lastTimestamp = -1;
}
@@ -1134,50 +833,16 @@ public class OCFileListAdapter extends RecyclerView.Adapter filteredFiles = new Vector<>();
+ public void addCheckedFile(OCFile file) {
+ ocFileListDelegate.addCheckedFile(file);
+ }
- if (!TextUtils.isEmpty(constraint)) {
- for (OCFile file : mFilesAll) {
- if (file.getParentRemotePath().equals(currentDirectory.getRemotePath()) &&
- file.getFileName().toLowerCase(Locale.getDefault()).contains(
- constraint.toString().toLowerCase(Locale.getDefault())) &&
- !filteredFiles.contains(file)) {
- filteredFiles.add(file);
- }
- }
- }
-
- results.values = filteredFiles;
- results.count = filteredFiles.size();
-
- return results;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
-
- Vector ocFiles = (Vector) results.values;
- mFiles.clear();
- if (ocFiles != null && ocFiles.size() > 0) {
- mFiles.addAll(ocFiles);
- if (!preferences.isShowHiddenFilesEnabled()) {
- mFiles = filterHiddenFiles(mFiles);
- }
- sortOrder = preferences.getSortOrderByFolder(currentDirectory);
- mFiles = sortOrder.sortCloudFiles(mFiles);
- }
-
- notifyDataSetChanged();
- }
+ public void setHighlightedItem(OCFile file) {
+ ocFileListDelegate.setHighlightedItem(file);
}
/**
@@ -1211,17 +876,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter getAllFiles() {
+ return mFilesAll;
}
- static class OCFileListGridItemViewHolder extends RecyclerView.ViewHolder implements ListGridItemViewHolder {
- protected GridItemBinding binding;
-
- private OCFileListGridItemViewHolder(GridItemBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
- this.binding.favoriteAction.getDrawable().mutate();
- }
-
- @Override
- public TextView getFileName() {
- return binding.Filename;
- }
-
- @Override
- public ImageView getThumbnail() {
- return binding.thumbnail;
- }
-
- @Override
- public LoaderImageView getShimmerThumbnail() {
- return binding.thumbnailShimmer;
- }
-
- @Override
- public ImageView getFavorite() {
- return binding.favoriteAction;
- }
-
- @Override
- public ImageView getLocalFileIndicator() {
- return binding.localFileIndicator;
- }
-
- @Override
- public ImageView getShared() {
- return binding.sharedIcon;
- }
-
- @Override
- public ImageView getCheckbox() {
- return binding.customCheckbox;
- }
-
- @Override
- public View getItemLayout() {
- return binding.ListItemLayout;
- }
-
- @Override
- public ImageView getUnreadComments() {
- return binding.unreadComments;
- }
+ public OCFile getCurrentDirectory() {
+ return currentDirectory;
}
- static class OCFileListGridImageViewHolder extends RecyclerView.ViewHolder implements ListGridImageViewHolder {
- protected GridImageBinding binding;
-
- private OCFileListGridImageViewHolder(GridImageBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
- this.binding.favoriteAction.getDrawable().mutate();
- }
-
- @Override
- public ImageView getThumbnail() {
- return binding.thumbnail;
- }
-
- @Override
- public LoaderImageView getShimmerThumbnail() {
- return binding.thumbnailShimmer;
- }
-
- @Override
- public ImageView getFavorite() {
- return binding.favoriteAction;
- }
-
- @Override
- public ImageView getLocalFileIndicator() {
- return binding.localFileIndicator;
- }
-
- @Override
- public ImageView getShared() {
- return binding.sharedIcon;
- }
-
- @Override
- public ImageView getCheckbox() {
- return binding.customCheckbox;
- }
-
- @Override
- public View getItemLayout() {
- return binding.ListItemLayout;
- }
-
- @Override
- public ImageView getUnreadComments() {
- return binding.unreadComments;
- }
+ @Override
+ public int getFilesCount() {
+ return mFiles.size();
}
- static class OCFileListFooterViewHolder extends RecyclerView.ViewHolder {
- protected ListFooterBinding binding;
-
- private OCFileListFooterViewHolder(ListFooterBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
- }
- }
-
- static class OCFileListHeaderViewHolder extends RecyclerView.ViewHolder {
- protected ListHeaderBinding binding;
-
- private OCFileListHeaderViewHolder(ListHeaderBinding binding) {
- super(binding.getRoot());
- this.binding = binding;
- }
- }
-
- interface ListGridImageViewHolder {
- ImageView getThumbnail();
-
- LoaderImageView getShimmerThumbnail();
-
- ImageView getFavorite();
-
- ImageView getLocalFileIndicator();
-
- ImageView getShared();
-
- ImageView getCheckbox();
-
- View getItemLayout();
-
- ImageView getUnreadComments();
- }
-
- interface ListGridItemViewHolder extends ListGridImageViewHolder {
- TextView getFileName();
- }
-
- interface ListItemViewHolder extends ListGridItemViewHolder {
- TextView getFileSize();
-
- View getFileSizeSeparator();
-
- TextView getLastModification();
-
- ImageView getOverflowMenu();
-
- AvatarGroupLayout getSharedAvatars();
+ @Override
+ public void notifyItemChanged(@NonNull OCFile file) {
+ notifyItemChanged(getItemPosition(file));
}
}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt
new file mode 100644
index 0000000000..476ee49242
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt
@@ -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 .
+ */
+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 = HashSet()
+ private var highlightedItem: OCFile? = null
+ var isMultiSelect = false
+ private val asyncTasks: MutableList = 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?) {
+ checkedFiles.addAll(files!!)
+ }
+
+ val checkedItems: Set
+ get() = checkedFiles
+
+ fun setCheckedItem(files: Set?) {
+ 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
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListFooterViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListFooterViewHolder.kt
new file mode 100644
index 0000000000..b319aff85a
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListFooterViewHolder.kt
@@ -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 .
+ */
+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
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt
new file mode 100644
index 0000000000..42064aa275
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt
@@ -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 .
+ */
+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()
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt
new file mode 100644
index 0000000000..6e7c8faf59
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt
@@ -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 .
+ */
+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()
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListHeaderViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListHeaderViewHolder.kt
new file mode 100644
index 0000000000..6ddc6320c5
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListHeaderViewHolder.kt
@@ -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 .
+ */
+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
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt
new file mode 100644
index 0000000000..70a725ea69
--- /dev/null
+++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt
@@ -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 .
+ */
+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()
+ }
+}
diff --git a/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.java b/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.java
index 9500922e6e..1d91f56073 100644
--- a/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.java
+++ b/app/src/main/java/com/owncloud/android/ui/asynctasks/GallerySearchTask.java
@@ -100,7 +100,7 @@ public class GallerySearchTask extends AsyncTask {
positiveButton.setEnabled(binding.newCheckbox.isChecked() || binding.existingCheckbox.isChecked());
diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java
index 82a64cf6d7..9ac137c916 100644
--- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java
+++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java
@@ -31,6 +31,8 @@ import com.owncloud.android.R;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.utils.Log_OC;
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.events.ChangeMenuEvent;
@@ -50,6 +52,7 @@ public class GalleryFragment extends OCFileListFragment {
private long endDate;
private long daySpan = 30;
private int limit = 300;
+ private GalleryAdapter mAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -87,18 +90,45 @@ public class GalleryFragment extends OCFileListFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- mAdapter.setShowMetadata(false);
currentSearchType = SearchType.GALLERY_SEARCH;
- switchToGridView();
-
menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT;
requireActivity().invalidateOptionsMenu();
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
public void onRefresh() {
super.onRefresh();
@@ -106,6 +136,11 @@ public class GalleryFragment extends OCFileListFragment {
handleSearchEvent();
}
+ @Override
+ public CommonOCFileListAdapterInterface getCommonAdapter() {
+ return mAdapter;
+ }
+
@Override
public void onResume() {
super.onResume();
@@ -161,7 +196,7 @@ public class GalleryFragment extends OCFileListFragment {
setEmptyListMessage(SearchType.GALLERY_SEARCH);
}
- if (emptySearch && getAdapter().getItemCount() > 0) {
+ if (emptySearch && mAdapter.getItemCount() > 0) {
Log_OC.d(this, "End gallery search");
return;
}
@@ -234,8 +269,7 @@ public class GalleryFragment extends OCFileListFragment {
}
}
- @Override
- public boolean isGalleryFragment() {
- return true;
+ public void showAllGalleryItems() {
+ mAdapter.showAllGalleryItems(mContainerActivity.getStorageManager());
}
}
diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
index dd5d2e5287..6788ba6375 100644
--- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
+++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java
@@ -78,6 +78,7 @@ import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.activity.FolderPickerActivity;
import com.owncloud.android.ui.activity.OnEnforceableRefreshListener;
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.dialog.ChooseRichDocumentsTemplateDialogFragment;
import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment;
@@ -189,7 +190,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
protected FileFragment.ContainerActivity mContainerActivity;
protected OCFile mFile;
- protected OCFileListAdapter mAdapter;
+ private OCFileListAdapter mAdapter;
protected boolean mOnlyFoldersClickable;
protected boolean mFileSelectable;
@@ -326,7 +327,9 @@ public class OCFileListFragment extends ExtendedListFragment implements
@Override
public void onPause() {
super.onPause();
- mAdapter.cancelAllPendingTasks();
+ if (mAdapter != null) {
+ mAdapter.cancelAllPendingTasks();
+ }
if (getActivity() != null) {
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);
mFileSelectable = args != null && args.getBoolean(ARG_FILE_SELECTABLE, false);
mLimitToMimeType = args != null ? args.getString(ARG_MIMETYPE, "") : "";
- 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);
+ setAdapter(args);
mHideFab = args != null && args.getBoolean(ARG_HIDE_FAB, false);
@@ -406,6 +399,22 @@ public class OCFileListFragment extends ExtendedListFragment implements
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) {
if (isSearchEventSet(event)) {
@@ -691,7 +700,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
// hide FAB in multi selection mode
setFabVisible(false);
- mAdapter.setMultiSelect(true);
+ getCommonAdapter().setMultiSelect(true);
return true;
}
@@ -700,12 +709,12 @@ public class OCFileListFragment extends ExtendedListFragment implements
*/
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- final int checkedCount = mAdapter.getCheckedItems().size();
- Set checkedFiles = mAdapter.getCheckedItems();
+ Set checkedFiles = getCommonAdapter().getCheckedItems();
+ final int checkedCount = checkedFiles.size();
String title = getResources().getQuantityString(R.plurals.items_selected_count, checkedCount, checkedCount);
mode.setTitle(title);
FileMenuFilter mf = new FileMenuFilter(
- mAdapter.getFiles().size(),
+ getCommonAdapter().getFilesCount(),
checkedFiles,
mContainerActivity,
getActivity(),
@@ -728,7 +737,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
*/
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- Set checkedFiles = mAdapter.getCheckedItems();
+ Set checkedFiles = getCommonAdapter().getCheckedItems();
return onFileActionChosen(item, checkedFiles);
}
@@ -738,14 +747,14 @@ public class OCFileListFragment extends ExtendedListFragment implements
@Override
public void onDestroyActionMode(ActionMode mode) {
mActiveActionMode = null;
-
+
// show FAB on multi selection mode exit
if (!mHideFab && !searchFragment) {
setFabVisible(true);
}
- mAdapter.setMultiSelect(false);
- mAdapter.clearCheckedItems();
+ getCommonAdapter().setMultiSelect(false);
+ getCommonAdapter().clearCheckedItems();
}
public void storeStateIn(Bundle outState) {
@@ -865,10 +874,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
* @param file The concerned OCFile by the selection/deselection
*/
private void toggleItemToCheckedList(OCFile file) {
- if (getAdapter().isCheckedFile(file)) {
- getAdapter().removeCheckedFile(file);
+ if (getCommonAdapter().isCheckedFile(file)) {
+ getCommonAdapter().removeCheckedFile(file);
} else {
- getAdapter().addCheckedFile(file);
+ getCommonAdapter().addCheckedFile(file);
}
updateActionModeFile(file);
}
@@ -881,7 +890,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
mIsActionModeNew = false;
if (mActiveActionMode != null) {
mActiveActionMode.invalidate();
- mAdapter.notifyItemChanged(getAdapter().getItemPosition(file));
+ getCommonAdapter().notifyItemChanged(file);
}
}
@@ -894,7 +903,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
toggleItemToCheckedList(file);
} else {
actionBarActivity.startActionMode(mMultiChoiceModeListener);
- getAdapter().addCheckedFile(file);
+ getCommonAdapter().addCheckedFile(file);
}
updateActionModeFile(file);
}
@@ -904,11 +913,11 @@ public class OCFileListFragment extends ExtendedListFragment implements
@Override
public void onItemClicked(OCFile file) {
- if (getAdapter().isMultiSelect()) {
+ if (getCommonAdapter().isMultiSelect()) {
toggleItemToCheckedList(file);
} else {
if (file != null) {
- int position = mAdapter.getItemPosition(file);
+ int position = getCommonAdapter().getItemPosition(file);
if (file.isFolder()) {
resetHeaderScrollingState();
@@ -1229,6 +1238,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
if (!directory.isFolder()) {
Log_OC.w(TAG, "You see, that is not a directory -> " + directory);
directory = storageManager.getFileById(directory.getParentId());
+
+ if (directory == null) {
+ return; // no files, wait for sync
+ }
}
if (searchView != null && !searchView.isIconified() && !fromSearch) {
@@ -1379,6 +1392,10 @@ public class OCFileListFragment extends ExtendedListFragment implements
getAdapter().notifyDataSetChanged();
}
+ public CommonOCFileListAdapterInterface getCommonAdapter() {
+ return mAdapter;
+ }
+
public OCFileListAdapter getAdapter() {
return mAdapter;
}
@@ -1587,7 +1604,11 @@ public class OCFileListFragment extends ExtendedListFragment implements
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);
dialog.setTargetFragment(this, SetupEncryptionDialogFragment.SETUP_ENCRYPTION_REQUEST_CODE);
dialog.show(getParentFragmentManager(), SetupEncryptionDialogFragment.SETUP_ENCRYPTION_DIALOG_TAG);
@@ -1656,11 +1677,6 @@ public class OCFileListFragment extends ExtendedListFragment implements
return searchFragment;
}
- @Override
- public boolean isGalleryFragment() {
- return false;
- }
-
/**
* De-/select all elements in the current list view.
*
@@ -1672,7 +1688,7 @@ public class OCFileListFragment extends ExtendedListFragment implements
if (select) {
ocFileListAdapter.addAllFilesToCheckedFiles();
} else {
- ocFileListAdapter.removeAllFilesFromCheckedFiles();
+ ocFileListAdapter.clearCheckedItems();
}
for (int i = 0; i < mAdapter.getItemCount(); i++) {
diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchAsyncTask.kt b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchAsyncTask.kt
index ee83961390..e2bdb8aa4a 100644
--- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchAsyncTask.kt
+++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListSearchAsyncTask.kt
@@ -69,7 +69,7 @@ class OCFileListSearchAsyncTask(
if (remoteOperationResult.resultData.isNullOrEmpty()) {
fragment.setEmptyView(event)
} else {
- fragment.mAdapter.setData(
+ fragment.adapter.setData(
remoteOperationResult.resultData,
fragment.currentSearchType,
fileDataStorageManager,
@@ -85,7 +85,7 @@ class OCFileListSearchAsyncTask(
fragmentReference.get()?.let { fragment ->
fragment.isLoading = false
if (!isCancelled) {
- fragment.mAdapter.notifyDataSetChanged()
+ fragment.adapter.notifyDataSetChanged()
}
}
}
diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt
index 5cf91566cc..9d53afe3a0 100644
--- a/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt
+++ b/app/src/main/java/com/owncloud/android/ui/fragment/SharedListFragment.kt
@@ -59,7 +59,7 @@ class SharedListFragment : OCFileListFragment(), Injectable {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
- mAdapter.setShowMetadata(false)
+ adapter.setShowMetadata(false)
currentSearchType = SearchType.SHARED_FILTER
searchEvent = SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER)
menuItemAddRemoveValue = MenuItemAddRemove.REMOVE_GRID_AND_SORT
diff --git a/app/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java b/app/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java
index 3b35dc19bb..5b2fd546d5 100644
--- a/app/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java
+++ b/app/src/main/java/com/owncloud/android/ui/interfaces/OCFileListFragmentInterface.java
@@ -48,8 +48,4 @@ public interface OCFileListFragmentInterface {
boolean isLoading();
void onHeaderClicked();
-
- boolean isSearchFragment();
-
- boolean isGalleryFragment();
}
diff --git a/app/src/main/java/com/owncloud/android/utils/DataHolderUtil.java b/app/src/main/java/com/owncloud/android/utils/DataHolderUtil.java
index 0cf9eef818..24f0e71681 100644
--- a/app/src/main/java/com/owncloud/android/utils/DataHolderUtil.java
+++ b/app/src/main/java/com/owncloud/android/utils/DataHolderUtil.java
@@ -27,6 +27,8 @@ import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
/**
* Data holder utility to store & retrieve stuff
*/
@@ -39,6 +41,7 @@ public class DataHolderUtil {
@SuppressLint("TrulyRandom")
private SecureRandom random = new SecureRandom();
+ @SuppressFBWarnings("MS_EXPOSE_REP")
public static synchronized DataHolderUtil getInstance() {
if (instance == null) {
instance = new DataHolderUtil();
diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java
index 96e79871d8..c450212aba 100644
--- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java
+++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java
@@ -46,6 +46,9 @@ import android.text.style.StyleSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.Toast;
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.Target;
import com.caverock.androidsvg.SVG;
+import com.elyeproj.loaderviewlibrary.LoaderImageView;
import com.google.android.material.snackbar.Snackbar;
import com.nextcloud.client.account.CurrentAccountProvider;
import com.nextcloud.client.account.User;
import com.nextcloud.client.network.ClientFactory;
+import com.nextcloud.client.preferences.AppPreferences;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.datamodel.ArbitraryDataProvider;
+import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
import com.owncloud.android.lib.common.OwnCloudAccount;
@@ -89,17 +95,19 @@ import java.math.BigDecimal;
import java.net.IDN;
import java.nio.charset.Charset;
import java.text.DateFormat;
+import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.AppCompatDrawableManager;
import androidx.core.content.res.ResourcesCompat;
-import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
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 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 mimeType2HumanReadable;
static {
@@ -820,4 +832,178 @@ public final class DisplayUtils {
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 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 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");
+ }
+ }
}
diff --git a/app/src/main/res/layout/gallery_header.xml b/app/src/main/res/layout/gallery_header.xml
new file mode 100644
index 0000000000..1d21ae25b4
--- /dev/null
+++ b/app/src/main/res/layout/gallery_header.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/com/owncloud/android/ui/adapter/GalleryAdapterTest.kt b/app/src/test/java/com/owncloud/android/ui/adapter/GalleryAdapterTest.kt
new file mode 100644
index 0000000000..0e9cb9a12a
--- /dev/null
+++ b/app/src/test/java/com/owncloud/android/ui/adapter/GalleryAdapterTest.kt
@@ -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 .
+ */
+
+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())
+ }
+}
diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt
index 7cbee132c8..db3a73389d 100644
--- a/scripts/analysis/findbugs-results.txt
+++ b/scripts/analysis/findbugs-results.txt
@@ -1 +1 @@
-592
+536
\ No newline at end of file
diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt
index 2d857a44dc..a0c1e5f3ef 100644
--- a/scripts/analysis/lint-results.txt
+++ b/scripts/analysis/lint-results.txt
@@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE
- Lint Report: 92 warnings
+ Lint Report: 89 warnings
diff --git a/spotbugs-filter.xml b/spotbugs-filter.xml
index ad1844bbe2..0f19982980 100644
--- a/spotbugs-filter.xml
+++ b/spotbugs-filter.xml
@@ -56,6 +56,7 @@
+