From e9afcc3bd92dcdc7022a8399a8be53aa7ae50c64 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 13 Dec 2024 15:47:46 +0100 Subject: [PATCH 01/30] add RecommendedFilesAdapter Signed-off-by: alperozturk --- .../android/ui/adapter/OCFileListAdapter.java | 67 ++++++++++++- .../ui/adapter/RecommendedFilesAdapter.kt | 96 +++++++++++++++++++ app/src/main/res/layout/list_header.xml | 6 ++ .../layout/recommended_files_list_item.xml | 41 ++++++++ 4 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt create mode 100644 app/src/main/res/layout/recommended_files_list_item.xml 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 4e9bc9bd1dec..ea4ddc4e634a 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 @@ -82,6 +82,7 @@ import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; @@ -97,6 +98,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import me.zhanghai.android.fastscroll.PopupTextProvider; @@ -433,6 +435,67 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi } else if (holder instanceof OCFileListHeaderViewHolder headerViewHolder) { String text = currentDirectory.getRichWorkspace(); + final var recommendedFiles = headerViewHolder.getBinding().recommendedFilesRecyclerView; + + final LinearLayoutManager layoutManager = new LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false); + recommendedFiles.setLayoutManager(layoutManager); + + // TODO use actual data + ArrayList mockData = new ArrayList<>(Arrays.asList( + new Recommendation( + 1L, + System.currentTimeMillis(), + "Document1", + "/documents", + "pdf", + "application/pdf", + true, + "Recently opened" + ), + new Recommendation( + 2L, + System.currentTimeMillis() - 3600000, + "Image1", + "/pictures", + "jpg", + "image/jpeg", + true, + "Frequently viewed" + ), + new Recommendation( + 3L, + System.currentTimeMillis() - 7200000, + "Presentation1", + "/presentations", + "pptx", + "application/vnd.ms-powerpoint", + false, + "Shared with you" + ), + new Recommendation( + 4L, + System.currentTimeMillis() - 86400000, + "Video1", + "/videos", + "mp4", + "video/mp4", + true, + "Recent download" + ), + new Recommendation( + 5L, + System.currentTimeMillis() - 604800000, + "Spreadsheet1", + "/spreadsheets", + "xlsx", + "application/vnd.ms-excel", + false, + "Marked as important" + ))); + + final var adapter = new RecommendedFilesAdapter(activity, viewThemeUtils, mockData); + recommendedFiles.setAdapter(adapter); + PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils); headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked()); } else { @@ -766,11 +829,13 @@ public boolean shouldShowHeader() { return false; } + // TODO add or condition for recommended files + if (currentDirectory.getRichWorkspace() == null) { return false; } - return !TextUtils.isEmpty(currentDirectory.getRichWorkspace().trim()); + return !TextUtils.isEmpty(currentDirectory.getRichWorkspace().trim()) || true; } /** diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt new file mode 100644 index 000000000000..24a31dc701d7 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -0,0 +1,96 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.adapter + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.res.ResourcesCompat +import androidx.recyclerview.widget.RecyclerView +import com.owncloud.android.R +import com.owncloud.android.databinding.RecommendedFilesListItemBinding +import com.owncloud.android.utils.BitmapUtils +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.MimeTypeUtil +import com.owncloud.android.utils.theme.ViewThemeUtils + +// TODO delete mock data +data class Recommendation( + val id: Long, + val timestamp: Long, + val name: String, + val directory: String, + val extension: String, + val mimeType: String, + val hasPreview: Boolean, + val reason: String +) + +class RecommendedFilesAdapter( + private val context: Context, + private val viewThemeUtils: ViewThemeUtils, + private val recommendations: List +) : RecyclerView.Adapter() { + + inner class RecommendedFilesViewHolder(val binding: RecommendedFilesListItemBinding) : + RecyclerView.ViewHolder(binding.root) { + init { + binding.root.setOnClickListener { + val position = bindingAdapterPosition + if (position != RecyclerView.NO_POSITION) { + // TODO onclick item + } + } + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecommendedFilesViewHolder { + val binding = RecommendedFilesListItemBinding + .inflate(LayoutInflater.from(parent.context), parent, false) + return RecommendedFilesViewHolder(binding) + } + + override fun getItemCount(): Int = recommendations.size + + override fun onBindViewHolder(holder: RecommendedFilesViewHolder, position: Int) { + val item = recommendations[position] + + holder.binding.name.text = item.name + holder.binding.timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) + + val thumbnail = getThumbnail(item) + holder.binding.icon.setImageBitmap(thumbnail) + } + + private fun getThumbnail(item: Recommendation): Bitmap { + var drawable = MimeTypeUtil.getFileTypeIcon( + item.mimeType, + item.name, + context, + viewThemeUtils + ) + + if (drawable == null) { + drawable = ResourcesCompat.getDrawable( + context.resources, + R.drawable.file_image, + null + ) + } + + if (drawable == null) { + drawable = ColorDrawable(Color.GRAY) + } + + val width = DisplayUtils.convertPixelToDp(40, context).toInt() + return BitmapUtils.drawableToBitmap(drawable, width / 2, width / 2) + } +} diff --git a/app/src/main/res/layout/list_header.xml b/app/src/main/res/layout/list_header.xml index eb000b18bb45..700e506c17cb 100644 --- a/app/src/main/res/layout/list_header.xml +++ b/app/src/main/res/layout/list_header.xml @@ -29,4 +29,10 @@ android:layout_height="match_parent" android:contentDescription="@null" android:src="@drawable/preview_markdown_gradient_shape" /> + + diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml new file mode 100644 index 000000000000..3b9b48cec64f --- /dev/null +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + From 619b63234bd1942197a59ccb17e3bc3b756ad4a4 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 13 Dec 2024 16:13:55 +0100 Subject: [PATCH 02/30] make items square Signed-off-by: alperozturk --- .../ui/adapter/RecommendedFilesAdapter.kt | 17 ++++++++++- app/src/main/res/layout/list_header.xml | 4 +++ .../layout/recommended_files_list_item.xml | 30 ++++++++----------- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index 24a31dc701d7..571549c40774 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -8,11 +8,13 @@ package com.owncloud.android.ui.adapter import android.content.Context +import android.content.res.ColorStateList import android.graphics.Bitmap import android.graphics.Color import android.graphics.drawable.ColorDrawable import android.view.LayoutInflater import android.view.ViewGroup +import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.R @@ -64,9 +66,22 @@ class RecommendedFilesAdapter( val item = recommendations[position] holder.binding.name.text = item.name - holder.binding.timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) + // holder.binding.timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) val thumbnail = getThumbnail(item) + + /* + val centerPixel = thumbnail.getPixel(thumbnail.width / 2, thumbnail.height / 2) + + val redValue = Color.red(centerPixel) + val blueValue = Color.blue(centerPixel) + val greenValue = Color.green(centerPixel) + + val centerColor = Color.argb(0.8f, redValue.toFloat(), greenValue.toFloat(), blueValue.toFloat()) + */ + + val containerColor = ContextCompat.getColor(context, R.color.primary) + holder.binding.container.backgroundTintList = ColorStateList.valueOf(containerColor) holder.binding.icon.setImageBitmap(thumbnail) } diff --git a/app/src/main/res/layout/list_header.xml b/app/src/main/res/layout/list_header.xml index 700e506c17cb..90d7a09e83f9 100644 --- a/app/src/main/res/layout/list_header.xml +++ b/app/src/main/res/layout/list_header.xml @@ -27,11 +27,15 @@ diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 3b9b48cec64f..906cfc184763 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -5,37 +5,33 @@ ~ SPDX-License-Identifier: AGPL-3.0-or-later --> - + android:gravity="center"> - - From c0a48d6eaffa0ae9fc772e4698fc8d3db84f8970 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 16 Dec 2024 15:00:08 +0100 Subject: [PATCH 03/30] add thumbnail generation logic and fix UI Signed-off-by: alperozturk --- .../android/ui/adapter/OCFileListAdapter.java | 5 +- .../android/ui/adapter/OCFileListDelegate.kt | 32 ++++++---- .../ui/adapter/RecommendedFilesAdapter.kt | 59 ++--------------- app/src/main/res/drawable/ic_circle.xml | 19 ++++++ .../main/res/drawable/ic_dots_horizontal.xml | 19 ++++++ app/src/main/res/layout/list_header.xml | 42 +++++++++--- .../layout/recommended_files_list_item.xml | 64 ++++++++++++++----- app/src/main/res/values/dims.xml | 1 + app/src/main/res/values/strings.xml | 1 + 9 files changed, 150 insertions(+), 92 deletions(-) create mode 100644 app/src/main/res/drawable/ic_circle.xml create mode 100644 app/src/main/res/drawable/ic_dots_horizontal.xml 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 ea4ddc4e634a..270d8df4e804 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 @@ -416,9 +416,6 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int LayoutInflater.from(parent.getContext()), parent, false); - ViewGroup.LayoutParams layoutParams = binding.headerView.getLayoutParams(); - layoutParams.height = (int) (parent.getHeight() * 0.3); - binding.headerView.setLayoutParams(layoutParams); return new OCFileListHeaderViewHolder(binding); } } @@ -493,7 +490,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi "Marked as important" ))); - final var adapter = new RecommendedFilesAdapter(activity, viewThemeUtils, mockData); + final var adapter = new RecommendedFilesAdapter(activity, mockData, ocFileListDelegate); recommendedFiles.setAdapter(adapter); PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils); 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 index 00254dfc15c7..19392cb4a597 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -195,28 +195,38 @@ class OCFileListDelegate( } } - fun bindGridViewHolder( - gridViewHolder: ListViewHolder, - file: OCFile, - currentDirectory: OCFile?, - searchType: SearchType? - ) { - // thumbnail - gridViewHolder.imageFileName?.text = file.fileName - gridViewHolder.thumbnail.tag = file.fileId + fun setThumbnailFromFileId(thumbnail: ImageView, shimmerThumbnail: LoaderImageView?, fileId: Long) { + storageManager.getFileById(fileId)?.let { file -> + setThumbnail(thumbnail, shimmerThumbnail, file) + } + } + + private fun setThumbnail(thumbnail: ImageView, shimmerThumbnail: LoaderImageView?, file: OCFile) { DisplayUtils.setThumbnail( file, - gridViewHolder.thumbnail, + thumbnail, user, storageManager, asyncTasks, gridView, context, - gridViewHolder.shimmerThumbnail, + shimmerThumbnail, preferences, viewThemeUtils, syncFolderProvider ) + } + + fun bindGridViewHolder( + gridViewHolder: ListViewHolder, + file: OCFile, + currentDirectory: OCFile?, + searchType: SearchType? + ) { + // thumbnail + gridViewHolder.imageFileName?.text = file.fileName + gridViewHolder.thumbnail.tag = file.fileId + setThumbnail(gridViewHolder.thumbnail, gridViewHolder.shimmerThumbnail, file) // item layout + click listeners bindGridItemLayout(file, gridViewHolder) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index 571549c40774..85521b57e862 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -8,21 +8,11 @@ package com.owncloud.android.ui.adapter import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Bitmap -import android.graphics.Color -import android.graphics.drawable.ColorDrawable import android.view.LayoutInflater import android.view.ViewGroup -import androidx.core.content.ContextCompat -import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView -import com.owncloud.android.R import com.owncloud.android.databinding.RecommendedFilesListItemBinding -import com.owncloud.android.utils.BitmapUtils import com.owncloud.android.utils.DisplayUtils -import com.owncloud.android.utils.MimeTypeUtil -import com.owncloud.android.utils.theme.ViewThemeUtils // TODO delete mock data data class Recommendation( @@ -38,8 +28,8 @@ data class Recommendation( class RecommendedFilesAdapter( private val context: Context, - private val viewThemeUtils: ViewThemeUtils, - private val recommendations: List + private val recommendations: List, + private val delegate: OCFileListDelegate ) : RecyclerView.Adapter() { inner class RecommendedFilesViewHolder(val binding: RecommendedFilesListItemBinding) : @@ -65,47 +55,10 @@ class RecommendedFilesAdapter( override fun onBindViewHolder(holder: RecommendedFilesViewHolder, position: Int) { val item = recommendations[position] - holder.binding.name.text = item.name - // holder.binding.timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) - - val thumbnail = getThumbnail(item) - - /* - val centerPixel = thumbnail.getPixel(thumbnail.width / 2, thumbnail.height / 2) - - val redValue = Color.red(centerPixel) - val blueValue = Color.blue(centerPixel) - val greenValue = Color.green(centerPixel) - - val centerColor = Color.argb(0.8f, redValue.toFloat(), greenValue.toFloat(), blueValue.toFloat()) - */ - - val containerColor = ContextCompat.getColor(context, R.color.primary) - holder.binding.container.backgroundTintList = ColorStateList.valueOf(containerColor) - holder.binding.icon.setImageBitmap(thumbnail) - } - - private fun getThumbnail(item: Recommendation): Bitmap { - var drawable = MimeTypeUtil.getFileTypeIcon( - item.mimeType, - item.name, - context, - viewThemeUtils - ) - - if (drawable == null) { - drawable = ResourcesCompat.getDrawable( - context.resources, - R.drawable.file_image, - null - ) + holder.binding.run { + name.text = item.name + timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) + delegate.setThumbnailFromFileId(thumbnail, shimmerThumbnail, item.id) } - - if (drawable == null) { - drawable = ColorDrawable(Color.GRAY) - } - - val width = DisplayUtils.convertPixelToDp(40, context).toInt() - return BitmapUtils.drawableToBitmap(drawable, width / 2, width / 2) } } diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml new file mode 100644 index 000000000000..75bd4b37dee2 --- /dev/null +++ b/app/src/main/res/drawable/ic_circle.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_dots_horizontal.xml b/app/src/main/res/drawable/ic_dots_horizontal.xml new file mode 100644 index 000000000000..432ba913f55d --- /dev/null +++ b/app/src/main/res/drawable/ic_dots_horizontal.xml @@ -0,0 +1,19 @@ + + + + + + + diff --git a/app/src/main/res/layout/list_header.xml b/app/src/main/res/layout/list_header.xml index 90d7a09e83f9..5be11bfdcb57 100644 --- a/app/src/main/res/layout/list_header.xml +++ b/app/src/main/res/layout/list_header.xml @@ -8,9 +8,10 @@ ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> @@ -26,17 +27,42 @@ - + android:orientation="vertical" + android:layout_height="wrap_content"> + + + + + + + diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 906cfc184763..3a4111381791 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -5,33 +5,65 @@ ~ SPDX-License-Identifier: AGPL-3.0-or-later --> - + android:gravity="start"> + + + + + + + + + + - + + diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index 5026b2e41a42..4a927d51844d 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -158,4 +158,5 @@ 108dp 18dp 18dp + 32dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f25a0f7eb402..986908bd5f4a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -74,6 +74,7 @@ Input Output + Recommended Files Assistant All files From fdf07cbeb48f51c7bd88f8ef014b651481d2ad5c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 16 Dec 2024 15:04:22 +0100 Subject: [PATCH 04/30] add dimens Signed-off-by: alperozturk --- .../main/res/layout/recommended_files_list_item.xml | 10 ++++------ app/src/main/res/values/dims.xml | 2 ++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 3a4111381791..529687bfd3cf 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -7,9 +7,9 @@ @@ -21,7 +21,7 @@ @@ -37,13 +37,12 @@ + android:layout_height="@dimen/recommended_files_thumbnail_height" /> 18dp 18dp 32dp + 120dp + 180dp From e7340755faaf8787cb2247ee19d213198d42c9cc Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 16 Dec 2024 15:19:08 +0100 Subject: [PATCH 05/30] setOnClickListener Signed-off-by: alperozturk --- .../android/ui/adapter/OCFileListAdapter.java | 14 ++++++++-- .../ui/adapter/RecommendedFilesAdapter.kt | 27 +++++++++++-------- .../layout/recommended_files_list_item.xml | 1 + 3 files changed, 29 insertions(+), 13 deletions(-) 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 270d8df4e804..6577efc9a627 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 @@ -108,7 +108,7 @@ */ public class OCFileListAdapter extends RecyclerView.Adapter implements DisplayUtils.AvatarGenerationListener, - CommonOCFileListAdapterInterface, PopupTextProvider { + CommonOCFileListAdapterInterface, PopupTextProvider, RecommendedFilesAdapter.OnItemClickListener { private static final int showFilenameColumnThreshold = 4; private final String userId; @@ -490,7 +490,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi "Marked as important" ))); - final var adapter = new RecommendedFilesAdapter(activity, mockData, ocFileListDelegate); + final var adapter = new RecommendedFilesAdapter(activity, mockData, ocFileListDelegate, this); recommendedFiles.setAdapter(adapter); PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils); @@ -1321,4 +1321,14 @@ public int getFilesCount() { public void notifyItemChanged(@NonNull OCFile file) { notifyItemChanged(getItemPosition(file)); } + + @Override + public void selectRecommendedFile(long fileId) { + // TODO Implement + } + + @Override + public void showRecommendedFileMoreActions(long fileId) { + // TODO Implement + } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index 85521b57e862..7c2698860d31 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -29,21 +29,18 @@ data class Recommendation( class RecommendedFilesAdapter( private val context: Context, private val recommendations: List, - private val delegate: OCFileListDelegate + private val delegate: OCFileListDelegate, + private val onItemClickListener: OnItemClickListener ) : RecyclerView.Adapter() { - inner class RecommendedFilesViewHolder(val binding: RecommendedFilesListItemBinding) : - RecyclerView.ViewHolder(binding.root) { - init { - binding.root.setOnClickListener { - val position = bindingAdapterPosition - if (position != RecyclerView.NO_POSITION) { - // TODO onclick item - } - } - } + interface OnItemClickListener { + fun selectRecommendedFile(fileId: Long) + fun showRecommendedFileMoreActions(fileId: Long) } + inner class RecommendedFilesViewHolder(val binding: RecommendedFilesListItemBinding) : + RecyclerView.ViewHolder(binding.root) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecommendedFilesViewHolder { val binding = RecommendedFilesListItemBinding .inflate(LayoutInflater.from(parent.context), parent, false) @@ -59,6 +56,14 @@ class RecommendedFilesAdapter( name.text = item.name timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) delegate.setThumbnailFromFileId(thumbnail, shimmerThumbnail, item.id) + + container.setOnClickListener { + onItemClickListener.selectRecommendedFile(item.id) + } + + moreAction.setOnClickListener { + onItemClickListener.showRecommendedFileMoreActions(item.id) + } } } } diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 529687bfd3cf..c4d18e463de4 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -27,6 +27,7 @@ android:src="@drawable/file" /> Date: Mon, 16 Dec 2024 15:31:14 +0100 Subject: [PATCH 06/30] add dimens Signed-off-by: alperozturk --- app/src/main/res/layout/list_header.xml | 2 +- app/src/main/res/values/dims.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/list_header.xml b/app/src/main/res/layout/list_header.xml index 5be11bfdcb57..9ca4f9b55c80 100644 --- a/app/src/main/res/layout/list_header.xml +++ b/app/src/main/res/layout/list_header.xml @@ -34,7 +34,7 @@ 32dp 120dp 180dp + 150dp From c08d44d65161373f2d90a9e4bdb17bbcb4818fcd Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 16 Dec 2024 16:19:21 +0100 Subject: [PATCH 07/30] set on click listener Signed-off-by: alperozturk --- .../android/ui/adapter/OCFileListAdapter.java | 43 ++++++++----------- .../android/ui/adapter/OCFileListDelegate.kt | 8 +--- .../ui/adapter/RecommendedFilesAdapter.kt | 19 +++++--- 3 files changed, 31 insertions(+), 39 deletions(-) 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 6577efc9a627..3643fc0ea7bf 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 @@ -440,7 +440,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi // TODO use actual data ArrayList mockData = new ArrayList<>(Arrays.asList( new Recommendation( - 1L, + 2124L, System.currentTimeMillis(), "Document1", "/documents", @@ -450,7 +450,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi "Recently opened" ), new Recommendation( - 2L, + 2130L, System.currentTimeMillis() - 3600000, "Image1", "/pictures", @@ -460,7 +460,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi "Frequently viewed" ), new Recommendation( - 3L, + 2131L, System.currentTimeMillis() - 7200000, "Presentation1", "/presentations", @@ -470,27 +470,18 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi "Shared with you" ), new Recommendation( - 4L, - System.currentTimeMillis() - 86400000, - "Video1", - "/videos", - "mp4", - "video/mp4", - true, - "Recent download" - ), - new Recommendation( - 5L, - System.currentTimeMillis() - 604800000, - "Spreadsheet1", - "/spreadsheets", - "xlsx", - "application/vnd.ms-excel", + 2126L, + System.currentTimeMillis() - 7200000, + "Presentation1", + "/presentations", + "pptx", + "application/vnd.ms-powerpoint", false, - "Marked as important" - ))); + "Shared with you" + )) + ); - final var adapter = new RecommendedFilesAdapter(activity, mockData, ocFileListDelegate, this); + final var adapter = new RecommendedFilesAdapter(activity, mockData, ocFileListDelegate, this, mStorageManager); recommendedFiles.setAdapter(adapter); PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils); @@ -1323,12 +1314,12 @@ public void notifyItemChanged(@NonNull OCFile file) { } @Override - public void selectRecommendedFile(long fileId) { - // TODO Implement + public void selectRecommendedFile(@NonNull OCFile file) { + ocFileListFragmentInterface.onItemClicked(file); } @Override - public void showRecommendedFileMoreActions(long fileId) { - // TODO Implement + public void showRecommendedFileMoreActions(@NonNull OCFile file, @NonNull View view) { + ocFileListFragmentInterface.onOverflowIconClicked(file, view); } } 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 index 19392cb4a597..de29534ac664 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -195,13 +195,7 @@ class OCFileListDelegate( } } - fun setThumbnailFromFileId(thumbnail: ImageView, shimmerThumbnail: LoaderImageView?, fileId: Long) { - storageManager.getFileById(fileId)?.let { file -> - setThumbnail(thumbnail, shimmerThumbnail, file) - } - } - - private fun setThumbnail(thumbnail: ImageView, shimmerThumbnail: LoaderImageView?, file: OCFile) { + fun setThumbnail(thumbnail: ImageView, shimmerThumbnail: LoaderImageView?, file: OCFile) { DisplayUtils.setThumbnail( file, thumbnail, diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index 7c2698860d31..c94fcaa7a43b 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -9,9 +9,12 @@ package com.owncloud.android.ui.adapter import android.content.Context import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.databinding.RecommendedFilesListItemBinding +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.DisplayUtils // TODO delete mock data @@ -30,12 +33,13 @@ class RecommendedFilesAdapter( private val context: Context, private val recommendations: List, private val delegate: OCFileListDelegate, - private val onItemClickListener: OnItemClickListener + private val onItemClickListener: OnItemClickListener, + private val storageManager: FileDataStorageManager ) : RecyclerView.Adapter() { interface OnItemClickListener { - fun selectRecommendedFile(fileId: Long) - fun showRecommendedFileMoreActions(fileId: Long) + fun selectRecommendedFile(file: OCFile) + fun showRecommendedFileMoreActions(file: OCFile, view: View) } inner class RecommendedFilesViewHolder(val binding: RecommendedFilesListItemBinding) : @@ -55,14 +59,17 @@ class RecommendedFilesAdapter( holder.binding.run { name.text = item.name timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) - delegate.setThumbnailFromFileId(thumbnail, shimmerThumbnail, item.id) + + val file = storageManager.getFileById(item.id) ?: return + + delegate.setThumbnail(thumbnail, shimmerThumbnail, file) container.setOnClickListener { - onItemClickListener.selectRecommendedFile(item.id) + onItemClickListener.selectRecommendedFile(file) } moreAction.setOnClickListener { - onItemClickListener.showRecommendedFileMoreActions(item.id) + onItemClickListener.showRecommendedFileMoreActions(file, holder.itemView) } } } From 6b51eb9463636b72ce0ed19cb8c94aff3749f3d3 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 16 Dec 2024 16:42:49 +0100 Subject: [PATCH 08/30] use library, remove mock data Signed-off-by: alperozturk --- .../android/ui/adapter/OCFileListAdapter.java | 81 ++++++------------- .../ui/adapter/RecommendedFilesAdapter.kt | 13 +-- .../ui/fragment/OCFileListFragment.java | 35 +++++++- 3 files changed, 58 insertions(+), 71 deletions(-) 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 3643fc0ea7bf..cb5663f13f09 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 @@ -31,6 +31,8 @@ import com.elyeproj.loaderviewlibrary.LoaderImageView; import com.nextcloud.android.common.ui.theme.utils.ColorRole; +import com.nextcloud.android.lib.resources.recommendations.GetRecommendationsRemoteOperation; +import com.nextcloud.android.lib.resources.recommendations.Recommendation; import com.nextcloud.client.account.User; import com.nextcloud.client.database.entity.OfflineOperationEntity; import com.nextcloud.client.jobs.upload.FileUploadHelper; @@ -82,7 +84,6 @@ import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; @@ -145,6 +146,8 @@ public class OCFileListAdapter extends RecyclerView.Adapter recommendedFiles; + public OCFileListAdapter( Activity activity, @NonNull User user, @@ -154,7 +157,9 @@ public OCFileListAdapter( OCFileListFragmentInterface ocFileListFragmentInterface, boolean argHideItemOptions, boolean gridView, - final ViewThemeUtils viewThemeUtils) { + final ViewThemeUtils viewThemeUtils, + final ArrayList recommendedFiles) { + this.recommendedFiles = recommendedFiles; this.ocFileListFragmentInterface = ocFileListFragmentInterface; this.activity = activity; this.preferences = preferences; @@ -412,6 +417,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int ); } case VIEW_TYPE_HEADER -> { + // TODO add height if recommended files is empty ListHeaderBinding binding = ListHeaderBinding.inflate( LayoutInflater.from(parent.getContext()), parent, @@ -431,61 +437,20 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi ocFileListFragmentInterface.isLoading() ? View.VISIBLE : View.GONE); } else if (holder instanceof OCFileListHeaderViewHolder headerViewHolder) { String text = currentDirectory.getRichWorkspace(); - - final var recommendedFiles = headerViewHolder.getBinding().recommendedFilesRecyclerView; - - final LinearLayoutManager layoutManager = new LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false); - recommendedFiles.setLayoutManager(layoutManager); - - // TODO use actual data - ArrayList mockData = new ArrayList<>(Arrays.asList( - new Recommendation( - 2124L, - System.currentTimeMillis(), - "Document1", - "/documents", - "pdf", - "application/pdf", - true, - "Recently opened" - ), - new Recommendation( - 2130L, - System.currentTimeMillis() - 3600000, - "Image1", - "/pictures", - "jpg", - "image/jpeg", - true, - "Frequently viewed" - ), - new Recommendation( - 2131L, - System.currentTimeMillis() - 7200000, - "Presentation1", - "/presentations", - "pptx", - "application/vnd.ms-powerpoint", - false, - "Shared with you" - ), - new Recommendation( - 2126L, - System.currentTimeMillis() - 7200000, - "Presentation1", - "/presentations", - "pptx", - "application/vnd.ms-powerpoint", - false, - "Shared with you" - )) - ); - - final var adapter = new RecommendedFilesAdapter(activity, mockData, ocFileListDelegate, this, mStorageManager); - recommendedFiles.setAdapter(adapter); - PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils); headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked()); + + ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesLayout, !recommendedFiles.isEmpty()); + + if (!recommendedFiles.isEmpty()) { + final var recommendedFilesRecyclerView = headerViewHolder.getBinding().recommendedFilesRecyclerView; + + final LinearLayoutManager layoutManager = new LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false); + recommendedFilesRecyclerView.setLayoutManager(layoutManager); + + final var adapter = new RecommendedFilesAdapter(activity, recommendedFiles, ocFileListDelegate, this, mStorageManager); + recommendedFilesRecyclerView.setAdapter(adapter); + } } else { ListViewHolder gridViewHolder = (ListViewHolder) holder; OCFile file = getItem(position); @@ -817,13 +782,15 @@ public boolean shouldShowHeader() { return false; } - // TODO add or condition for recommended files + if (!recommendedFiles.isEmpty()) { + return true; + } if (currentDirectory.getRichWorkspace() == null) { return false; } - return !TextUtils.isEmpty(currentDirectory.getRichWorkspace().trim()) || true; + return !TextUtils.isEmpty(currentDirectory.getRichWorkspace().trim()); } /** diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index c94fcaa7a43b..ddf36f8b75f6 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -12,23 +12,12 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.nextcloud.android.lib.resources.recommendations.Recommendation import com.owncloud.android.databinding.RecommendedFilesListItemBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.DisplayUtils -// TODO delete mock data -data class Recommendation( - val id: Long, - val timestamp: Long, - val name: String, - val directory: String, - val extension: String, - val mimeType: String, - val hasPreview: Boolean, - val reason: String -) - class RecommendedFilesAdapter( private val context: Context, private val recommendations: List, 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 5b38cf949374..42a5bf5ab94a 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 @@ -15,6 +15,7 @@ package com.owncloud.android.ui.fragment; import android.Manifest; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -41,6 +42,8 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; import com.nextcloud.android.lib.resources.files.ToggleFileLockRemoteOperation; +import com.nextcloud.android.lib.resources.recommendations.GetRecommendationsRemoteOperation; +import com.nextcloud.android.lib.resources.recommendations.Recommendation; import com.nextcloud.android.lib.richWorkspace.RichWorkspaceDirectEditingRemoteOperation; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; @@ -72,6 +75,8 @@ import com.owncloud.android.datamodel.e2e.v2.decrypted.DecryptedFolderMetadataFile; import com.owncloud.android.lib.common.Creator; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientFactory; +import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -150,6 +155,7 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import kotlin.collections.CollectionsKt; import static com.owncloud.android.datamodel.OCFile.ROOT_PATH; import static com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment.SETUP_ENCRYPTION_DIALOG_TAG; @@ -248,6 +254,7 @@ protected enum MenuItemAddRemove { protected MenuItemAddRemove menuItemAddRemoveValue = MenuItemAddRemove.ADD_GRID_AND_SORT_WITH_SEARCH; private List mOriginalMenuItems = new ArrayList<>(); + private ArrayList recommendedFiles = new ArrayList<>(); @Override public void onCreate(Bundle savedInstanceState) { @@ -382,7 +389,7 @@ public void onActivityCreated(Bundle savedInstanceState) { 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, "") : ""; - + fetchRecommendedFiles(); setAdapter(args); mHideFab = args != null && args.getBoolean(ARG_HIDE_FAB, false); @@ -431,6 +438,29 @@ public void onActivityCreated(Bundle savedInstanceState) { listDirectory(MainApp.isOnlyOnDevice(), false); } + + private void fetchRecommendedFiles() { + new Thread(() -> {{ + try { + User user = accountManager.getUser(); + final var client = OwnCloudClientFactory.createNextcloudClient(user.toPlatformAccount(), requireActivity()); + final var result = new GetRecommendationsRemoteOperation().execute(client); + if (result.isSuccess()) { + recommendedFiles.addAll(result.getResultData()); + requireActivity().runOnUiThread(new Runnable() { + @SuppressLint("NotifyDataSetChanged") + @Override + public void run() { + mAdapter.notifyDataSetChanged(); + } + }); + } + } catch (Exception e) { + Log_OC.d(TAG,"Error caught at fetchRecommendedFiles"); + } + }}).start(); + } + protected void setAdapter(Bundle args) { boolean hideItemOptions = args != null && args.getBoolean(ARG_HIDE_ITEM_OPTIONS, false); @@ -443,7 +473,8 @@ protected void setAdapter(Bundle args) { this, hideItemOptions, isGridViewPreferred(mFile), - viewThemeUtils + viewThemeUtils, + recommendedFiles ); setRecyclerViewAdapter(mAdapter); From e6aad1874b9f8dc16f105478fde0fcf2792df310 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 17 Dec 2024 11:23:57 +0100 Subject: [PATCH 09/30] notifyItemChanged Signed-off-by: alperozturk --- .../owncloud/android/ui/adapter/OCFileListAdapter.java | 3 +-- .../owncloud/android/ui/fragment/OCFileListFragment.java | 4 +--- gradle/verification-metadata.xml | 8 ++++++++ 3 files changed, 10 insertions(+), 5 deletions(-) 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 cb5663f13f09..1624a355e781 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 @@ -31,7 +31,6 @@ import com.elyeproj.loaderviewlibrary.LoaderImageView; import com.nextcloud.android.common.ui.theme.utils.ColorRole; -import com.nextcloud.android.lib.resources.recommendations.GetRecommendationsRemoteOperation; import com.nextcloud.android.lib.resources.recommendations.Recommendation; import com.nextcloud.client.account.User; import com.nextcloud.client.database.entity.OfflineOperationEntity; @@ -417,11 +416,11 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int ); } case VIEW_TYPE_HEADER -> { - // TODO add height if recommended files is empty ListHeaderBinding binding = ListHeaderBinding.inflate( LayoutInflater.from(parent.getContext()), parent, false); + return new OCFileListHeaderViewHolder(binding); } } 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 42a5bf5ab94a..c7971a1842cf 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 @@ -76,7 +76,6 @@ import com.owncloud.android.lib.common.Creator; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -155,7 +154,6 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import kotlin.collections.CollectionsKt; import static com.owncloud.android.datamodel.OCFile.ROOT_PATH; import static com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment.SETUP_ENCRYPTION_DIALOG_TAG; @@ -451,7 +449,7 @@ private void fetchRecommendedFiles() { @SuppressLint("NotifyDataSetChanged") @Override public void run() { - mAdapter.notifyDataSetChanged(); + mAdapter.notifyItemChanged(0); } }); } diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index f4ca67a254c0..6d0f10e21a97 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -9177,6 +9177,14 @@ + + + + + + + + From d02f61d61a57e7a45786061655e10a720bb0ac66 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 17 Dec 2024 11:24:04 +0100 Subject: [PATCH 10/30] notifyItemChanged Signed-off-by: alperozturk --- .../com/owncloud/android/ui/fragment/OCFileListFragment.java | 1 - 1 file changed, 1 deletion(-) 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 c7971a1842cf..d6640d10d8b4 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 @@ -436,7 +436,6 @@ public void onActivityCreated(Bundle savedInstanceState) { listDirectory(MainApp.isOnlyOnDevice(), false); } - private void fetchRecommendedFiles() { new Thread(() -> {{ try { From 6822dd52b57b609b6ec376b386ad9e3ad717f070 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 17 Dec 2024 12:21:53 +0100 Subject: [PATCH 11/30] visual fixes Signed-off-by: alperozturk --- app/src/main/res/layout/recommended_files_list_item.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index c4d18e463de4..06ef2082ac34 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -6,6 +6,7 @@ --> @@ -49,7 +51,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="start|center" - android:textSize="12sp" + android:textSize="14sp" android:textColor="@color/text_color" android:ellipsize="end" android:maxLines="1"/> @@ -60,7 +62,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="start|center" - android:textSize="11sp" + android:textSize="12sp" android:textColor="@color/secondary_text_color" android:ellipsize="end" android:maxLines="1"/> From 94eee00aa299e0ceacc3aabb04e8bf09c53c1300 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 17 Dec 2024 12:30:41 +0100 Subject: [PATCH 12/30] visual fixes Signed-off-by: alperozturk --- app/src/main/res/layout/recommended_files_list_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 06ef2082ac34..03ddf9701e7a 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -10,7 +10,7 @@ android:id="@+id/container" android:layout_width="@dimen/recommended_files_layout_width" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/standard_half_margin" + android:layout_marginEnd="@dimen/standard_margin" android:orientation="vertical" android:gravity="start"> From 4681ef50e6b93a1372773378f47496286460543a Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 17 Dec 2024 12:31:57 +0100 Subject: [PATCH 13/30] fix code analytics Signed-off-by: alperozturk --- .../com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index ddf36f8b75f6..ef0e4f2439ea 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -47,7 +47,7 @@ class RecommendedFilesAdapter( holder.binding.run { name.text = item.name - timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) + timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) val file = storageManager.getFileById(item.id) ?: return From 82ce90f5c312c66824223c0c778ac4447f85da8a Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 18 Dec 2024 07:49:35 +0100 Subject: [PATCH 14/30] update lib Signed-off-by: tobiasKaminsky --- .../com/owncloud/android/ui/fragment/OCFileListFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d6640d10d8b4..2263304c8ad2 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 @@ -443,7 +443,7 @@ private void fetchRecommendedFiles() { final var client = OwnCloudClientFactory.createNextcloudClient(user.toPlatformAccount(), requireActivity()); final var result = new GetRecommendationsRemoteOperation().execute(client); if (result.isSuccess()) { - recommendedFiles.addAll(result.getResultData()); + recommendedFiles.addAll(result.getResultData().getRecommendations()); requireActivity().runOnUiThread(new Runnable() { @SuppressLint("NotifyDataSetChanged") @Override From 27c2055cb804d46e2c383c548cea632542ded3d6 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 18 Dec 2024 11:21:15 +0100 Subject: [PATCH 15/30] use api Signed-off-by: alperozturk --- .../java/com/nextcloud/client/database/dao/FileDao.kt | 3 +++ .../android/datamodel/FileDataStorageManager.java | 9 +++++++++ .../owncloud/android/ui/adapter/OCFileListAdapter.java | 2 +- .../android/ui/adapter/RecommendedFilesAdapter.kt | 2 +- .../owncloud/android/ui/fragment/OCFileListFragment.java | 5 +++-- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt index 37ef4568c5be..509383e7eb00 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt @@ -17,6 +17,9 @@ interface FileDao { @Query("SELECT * FROM filelist WHERE _id = :id LIMIT 1") fun getFileById(id: Long): FileEntity? + @Query("SELECT * FROM filelist WHERE local_id = :localId LIMIT 1") + fun getFileByLocalId(localId: Long): FileEntity? + @Query("SELECT * FROM filelist WHERE path = :path AND file_owner = :fileOwner LIMIT 1") fun getFileByEncryptedRemotePath(path: String, fileOwner: String): FileEntity? diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index ac14c91c8cd2..526384b9eafa 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -358,6 +358,15 @@ OCFile getFileById(long id) { return null; } + public @Nullable + OCFile getFileByLocalId(long localId) { + FileEntity fileEntity = fileDao.getFileByLocalId(localId); + if (fileEntity != null) { + return createFileInstance(fileEntity); + } + return null; + } + public @Nullable OCFile getFileByLocalPath(String path) { FileEntity fileEntity = fileDao.getFileByLocalPath(path, user.getAccountName()); 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 1624a355e781..60535bedde94 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 @@ -781,7 +781,7 @@ public boolean shouldShowHeader() { return false; } - if (!recommendedFiles.isEmpty()) { + if (!recommendedFiles.isEmpty() && currentDirectory.isRootDirectory()) { return true; } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index ef0e4f2439ea..0152d39f6c2f 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -49,7 +49,7 @@ class RecommendedFilesAdapter( name.text = item.name timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) - val file = storageManager.getFileById(item.id) ?: return + val file = storageManager.getFileByLocalId(item.id) ?: return delegate.setThumbnail(thumbnail, shimmerThumbnail, file) 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 2263304c8ad2..aed73162452e 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 @@ -252,7 +252,7 @@ protected enum MenuItemAddRemove { protected MenuItemAddRemove menuItemAddRemoveValue = MenuItemAddRemove.ADD_GRID_AND_SORT_WITH_SEARCH; private List mOriginalMenuItems = new ArrayList<>(); - private ArrayList recommendedFiles = new ArrayList<>(); + private final ArrayList recommendedFiles = new ArrayList<>(); @Override public void onCreate(Bundle savedInstanceState) { @@ -443,7 +443,8 @@ private void fetchRecommendedFiles() { final var client = OwnCloudClientFactory.createNextcloudClient(user.toPlatformAccount(), requireActivity()); final var result = new GetRecommendationsRemoteOperation().execute(client); if (result.isSuccess()) { - recommendedFiles.addAll(result.getResultData().getRecommendations()); + final var recommendations = result.getResultData().getRecommendations(); + recommendedFiles.addAll(recommendations); requireActivity().runOnUiThread(new Runnable() { @SuppressLint("NotifyDataSetChanged") @Override From 984bce765dd5668b66a6e0b52102418ec8fa680c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 18 Dec 2024 11:41:30 +0100 Subject: [PATCH 16/30] fix ui Signed-off-by: alperozturk --- .../owncloud/android/ui/adapter/RecommendedFilesAdapter.kt | 3 ++- app/src/main/res/layout/list_header.xml | 4 +--- app/src/main/res/values/dims.xml | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index 0152d39f6c2f..235dbb0ec83e 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -47,7 +47,8 @@ class RecommendedFilesAdapter( holder.binding.run { name.text = item.name - timestamp.text = DisplayUtils.getRelativeTimestamp(context, item.timestamp) + val secondsInMillisecond = (item.timestamp * 1000) + timestamp.text = DisplayUtils.getRelativeTimestamp(context, secondsInMillisecond) val file = storageManager.getFileByLocalId(item.id) ?: return diff --git a/app/src/main/res/layout/list_header.xml b/app/src/main/res/layout/list_header.xml index 9ca4f9b55c80..6520f22591ef 100644 --- a/app/src/main/res/layout/list_header.xml +++ b/app/src/main/res/layout/list_header.xml @@ -43,10 +43,8 @@ 120dp 180dp 150dp + + 22sp From 292d9537efa320de23b9f427c155931f5e5500ae Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 18 Dec 2024 11:43:52 +0100 Subject: [PATCH 17/30] fix ui Signed-off-by: alperozturk --- .../owncloud/android/ui/adapter/RecommendedFilesAdapter.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index 235dbb0ec83e..96dad6b8b50b 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -47,8 +47,8 @@ class RecommendedFilesAdapter( holder.binding.run { name.text = item.name - val secondsInMillisecond = (item.timestamp * 1000) - timestamp.text = DisplayUtils.getRelativeTimestamp(context, secondsInMillisecond) + val modificationTimestamp = (item.timestamp * 1000) + timestamp.text = DisplayUtils.getRelativeTimestamp(context, modificationTimestamp) val file = storageManager.getFileByLocalId(item.id) ?: return From 7f17549bbe43c298e1030915ac4d1339e12f6cd4 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 18 Dec 2024 15:26:03 +0100 Subject: [PATCH 18/30] fix ui Signed-off-by: alperozturk --- app/src/main/res/drawable/ic_circle.xml | 20 +++++++------------ app/src/main/res/layout/list_header.xml | 14 ++++++++++++- .../layout/recommended_files_list_item.xml | 9 ++++++--- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml index 75bd4b37dee2..9b1d377bc3d2 100644 --- a/app/src/main/res/drawable/ic_circle.xml +++ b/app/src/main/res/drawable/ic_circle.xml @@ -4,16 +4,10 @@ ~ SPDX-FileCopyrightText: 2018-2024 Google LLC ~ SPDX-License-Identifier: Apache-2.0 --> - - - - - - + + + + diff --git a/app/src/main/res/layout/list_header.xml b/app/src/main/res/layout/list_header.xml index 6520f22591ef..ae2122408374 100644 --- a/app/src/main/res/layout/list_header.xml +++ b/app/src/main/res/layout/list_header.xml @@ -8,7 +8,6 @@ ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> + + diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 03ddf9701e7a..9f27e9bf844b 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -30,11 +30,14 @@ + android:padding="@dimen/standard_eight_padding" + android:layout_width="@dimen/activity_icon_radius" + android:layout_height="@dimen/activity_icon_radius"/> Date: Wed, 18 Dec 2024 15:47:32 +0100 Subject: [PATCH 19/30] fix ui Signed-off-by: alperozturk --- app/src/main/res/values/dims.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index 414637ce38a2..b256254be6d9 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -158,7 +158,6 @@ 108dp 18dp 18dp - 32dp 120dp 180dp 150dp From b6971675299de7615f52be2a6f85bdef6ca7555a Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 18 Dec 2024 15:48:33 +0100 Subject: [PATCH 20/30] fix ss test Signed-off-by: alperozturk --- ...agmentStaticServerIT_showRichWorkspace.png | Bin 39848 -> 27722 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.OCFileListFragmentStaticServerIT_showRichWorkspace.png index 5579f8b847f4243d718517cf2f390ddc6fd6cdfd..068491834c6a40f2ab49ce01a95508454dc41796 100644 GIT binary patch literal 27722 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 j-RiY@Z|VO(V^p(7 literal 39848 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
B=?#q#OUxX0-~90%G2GuVZ3Bh;!w_ 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 Date: Wed, 18 Dec 2024 16:34:22 +0100 Subject: [PATCH 21/30] shouldShowRecommendedFiles Signed-off-by: alperozturk --- .../owncloud/android/ui/adapter/OCFileListAdapter.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 60535bedde94..6504472bd43c 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 @@ -439,9 +439,9 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils); headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked()); - ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesLayout, !recommendedFiles.isEmpty()); + ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesLayout, shouldShowRecommendedFiles()); - if (!recommendedFiles.isEmpty()) { + if (shouldShowRecommendedFiles()) { final var recommendedFilesRecyclerView = headerViewHolder.getBinding().recommendedFilesRecyclerView; final LinearLayoutManager layoutManager = new LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false); @@ -481,6 +481,10 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi } } + private boolean shouldShowRecommendedFiles() { + return !recommendedFiles.isEmpty() && currentDirectory.isRootDirectory(); + } + private void checkVisibilityOfFileFeaturesLayout(ListViewHolder holder) { int fileFeaturesVisibility = View.GONE; LinearLayout fileFeaturesLayout = holder.getFileFeaturesLayout(); @@ -781,7 +785,7 @@ public boolean shouldShowHeader() { return false; } - if (!recommendedFiles.isEmpty() && currentDirectory.isRootDirectory()) { + if (shouldShowRecommendedFiles()) { return true; } From 17f48281dda3fe9cdf9d26edb05c5d1a2d0e445f Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 19 Dec 2024 15:48:25 +0100 Subject: [PATCH 22/30] fix code analytics Signed-off-by: alperozturk --- .../com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index 96dad6b8b50b..f4b2b488a11b 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -42,6 +42,7 @@ class RecommendedFilesAdapter( override fun getItemCount(): Int = recommendations.size + @Suppress("MagicNumber") override fun onBindViewHolder(holder: RecommendedFilesViewHolder, position: Int) { val item = recommendations[position] From 778540241fa847f2f7800e678814b03dccc09d4d Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 20 Dec 2024 09:06:39 +0100 Subject: [PATCH 23/30] change layout width Signed-off-by: alperozturk --- app/src/main/res/values/dims.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index b256254be6d9..eecf0fc567bf 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -159,7 +159,7 @@ 18dp 18dp 120dp - 180dp + 150dp 150dp 22sp From 3c50d72f313be52214636b4fc2d84099970bb678 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 7 Jan 2025 11:40:45 +0100 Subject: [PATCH 24/30] update lib Signed-off-by: alperozturk --- gradle/verification-metadata.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 6d0f10e21a97..18f003e5231b 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -9249,6 +9249,14 @@ + + + + + + + + From daf58603605b8877d4a28a5be2621b2cf62a0730 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 7 Jan 2025 16:42:04 +0100 Subject: [PATCH 25/30] fix duplicated item Signed-off-by: alperozturk --- .../com/owncloud/android/ui/adapter/OCFileListAdapter.java | 4 ++-- .../owncloud/android/ui/adapter/RecommendedFilesAdapter.kt | 4 ++-- .../com/owncloud/android/ui/fragment/OCFileListFragment.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) 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 6504472bd43c..c85c6895082b 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 @@ -145,7 +145,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter recommendedFiles; + private final Set recommendedFiles; public OCFileListAdapter( Activity activity, @@ -157,7 +157,7 @@ public OCFileListAdapter( boolean argHideItemOptions, boolean gridView, final ViewThemeUtils viewThemeUtils, - final ArrayList recommendedFiles) { + final Set recommendedFiles) { this.recommendedFiles = recommendedFiles; this.ocFileListFragmentInterface = ocFileListFragmentInterface; this.activity = activity; diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt index f4b2b488a11b..1d1a1127d452 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt @@ -20,7 +20,7 @@ import com.owncloud.android.utils.DisplayUtils class RecommendedFilesAdapter( private val context: Context, - private val recommendations: List, + private val recommendations: Set, private val delegate: OCFileListDelegate, private val onItemClickListener: OnItemClickListener, private val storageManager: FileDataStorageManager @@ -44,7 +44,7 @@ class RecommendedFilesAdapter( @Suppress("MagicNumber") override fun onBindViewHolder(holder: RecommendedFilesViewHolder, position: Int) { - val item = recommendations[position] + val item = recommendations.elementAt(position) holder.binding.run { name.text = item.name 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 aed73162452e..967123ccf04e 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 @@ -252,7 +252,7 @@ protected enum MenuItemAddRemove { protected MenuItemAddRemove menuItemAddRemoveValue = MenuItemAddRemove.ADD_GRID_AND_SORT_WITH_SEARCH; private List mOriginalMenuItems = new ArrayList<>(); - private final ArrayList recommendedFiles = new ArrayList<>(); + private final Set recommendedFiles = new HashSet<>(); @Override public void onCreate(Bundle savedInstanceState) { From b1fd72d110c793b6fd98b4b8c2c21b2214d0c061 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 7 Jan 2025 16:45:14 +0100 Subject: [PATCH 26/30] add more padding Signed-off-by: alperozturk --- app/src/main/res/layout/recommended_files_list_item.xml | 4 ++++ app/src/main/res/values/dims.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 9f27e9bf844b..e6a6981bd0a5 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -23,6 +23,7 @@ android:id="@+id/thumbnail" android:layout_width="match_parent" android:layout_height="@dimen/recommended_files_thumbnail_height" + android:padding="@dimen/standard_padding" android:background="@drawable/rounded_rect_8dp" android:contentDescription="@string/preview_image_description" android:src="@drawable/file" /> @@ -36,12 +37,15 @@ android:contentDescription="@string/preview_image_description" android:layout_gravity="top|end" android:padding="@dimen/standard_eight_padding" + android:layout_marginTop="@dimen/standard_half_margin" + android:layout_marginEnd="@dimen/standard_half_margin" android:layout_width="@dimen/activity_icon_radius" android:layout_height="@dimen/activity_icon_radius"/> diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index eecf0fc567bf..8625a8eb6fbe 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -160,7 +160,7 @@ 18dp 120dp 150dp - 150dp + 180dp 22sp From a0550866c80ba970c69102d6ccb813e9390f31c6 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 7 Jan 2025 16:59:43 +0100 Subject: [PATCH 27/30] better alignment, cleanup list_header Signed-off-by: alperozturk --- .../android/ui/adapter/OCFileListAdapter.java | 4 +- app/src/main/res/layout/list_header.xml | 75 +++++++------------ .../layout/recommended_files_list_item.xml | 1 + app/src/main/res/values/dims.xml | 2 - 4 files changed, 32 insertions(+), 50 deletions(-) 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 c85c6895082b..565d43d5232a 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 @@ -439,7 +439,9 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils); headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked()); - ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesLayout, shouldShowRecommendedFiles()); + ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesRecyclerView, shouldShowRecommendedFiles()); + ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().recommendedFilesTitle, shouldShowRecommendedFiles()); + ViewExtensionsKt.setVisibleIf(headerViewHolder.getBinding().allFilesTitle, shouldShowRecommendedFiles()); if (shouldShowRecommendedFiles()) { final var recommendedFilesRecyclerView = headerViewHolder.getBinding().recommendedFilesRecyclerView; diff --git a/app/src/main/res/layout/list_header.xml b/app/src/main/res/layout/list_header.xml index ae2122408374..63c0cbac1d3d 100644 --- a/app/src/main/res/layout/list_header.xml +++ b/app/src/main/res/layout/list_header.xml @@ -7,72 +7,53 @@ ~ SPDX-FileCopyrightText: 2019 Nextcloud GmbH ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> - - - - - - + android:layout_height="match_parent" + android:layout_marginBottom="@dimen/standard_margin" + android:textColor="@color/text_color" /> - + - + - + diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index e6a6981bd0a5..852eb863dc18 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -25,6 +25,7 @@ android:layout_height="@dimen/recommended_files_thumbnail_height" android:padding="@dimen/standard_padding" android:background="@drawable/rounded_rect_8dp" + android:backgroundTint="@color/transparent" android:contentDescription="@string/preview_image_description" android:src="@drawable/file" /> diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index 8625a8eb6fbe..6f57563d30b6 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -160,7 +160,5 @@ 18dp 120dp 150dp - 180dp - 22sp From 248d83c59f6d9a930736f4dd3d70c05c1fd7017c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 8 Jan 2025 09:08:32 +0100 Subject: [PATCH 28/30] change loader image view background color Signed-off-by: alperozturk --- app/src/main/res/layout/recommended_files_list_item.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/recommended_files_list_item.xml b/app/src/main/res/layout/recommended_files_list_item.xml index 852eb863dc18..fedd0f47478f 100644 --- a/app/src/main/res/layout/recommended_files_list_item.xml +++ b/app/src/main/res/layout/recommended_files_list_item.xml @@ -48,6 +48,7 @@ android:id="@+id/shimmer_thumbnail" android:padding="@dimen/standard_padding" android:layout_width="match_parent" + android:src="@color/transparent" android:layout_height="@dimen/recommended_files_thumbnail_height" /> From 06f4c9566d573c370e4051c7c5124939f7b019cd Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 16 Jan 2025 12:48:41 +0100 Subject: [PATCH 29/30] rebase Signed-off-by: alperozturk --- build.gradle | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ff2221a7087b..0f96909c57ba 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ */ buildscript { ext { - androidLibraryVersion ="fe2c9ff6e9caaff0f569776c332884958b827b63" + androidLibraryVersion ="a0c49a3960" androidPluginVersion = '8.8.0' androidxMediaVersion = '1.5.1' androidxTestVersion = "1.6.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 18f003e5231b..29c6ab44f4ad 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -9297,6 +9297,14 @@ + + + + + + + + From b043995b185f19e0dab37bfa689dfb62bdfdcf27 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Thu, 16 Jan 2025 13:25:03 +0100 Subject: [PATCH 30/30] update lib Signed-off-by: alperozturk --- build.gradle | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0f96909c57ba..775b25f2cd7f 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ */ buildscript { ext { - androidLibraryVersion ="a0c49a3960" + androidLibraryVersion ="80244b6283" androidPluginVersion = '8.8.0' androidxMediaVersion = '1.5.1' androidxTestVersion = "1.6.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 29c6ab44f4ad..b26defaa9139 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -9257,6 +9257,14 @@ + + + + + + + +