Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature - Recommended Files #14178

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e9afcc3
add RecommendedFilesAdapter
alperozturk96 Dec 13, 2024
619b632
make items square
alperozturk96 Dec 13, 2024
c0a48d6
add thumbnail generation logic and fix UI
alperozturk96 Dec 16, 2024
fdf07cb
add dimens
alperozturk96 Dec 16, 2024
e734075
setOnClickListener
alperozturk96 Dec 16, 2024
c64541a
add dimens
alperozturk96 Dec 16, 2024
c08d44d
set on click listener
alperozturk96 Dec 16, 2024
6b51eb9
use library, remove mock data
alperozturk96 Dec 16, 2024
e6aad18
notifyItemChanged
alperozturk96 Dec 17, 2024
d02f61d
notifyItemChanged
alperozturk96 Dec 17, 2024
6822dd5
visual fixes
alperozturk96 Dec 17, 2024
94eee00
visual fixes
alperozturk96 Dec 17, 2024
4681ef5
fix code analytics
alperozturk96 Dec 17, 2024
82ce90f
update lib
tobiasKaminsky Dec 18, 2024
27c2055
use api
alperozturk96 Dec 18, 2024
984bce7
fix ui
alperozturk96 Dec 18, 2024
292d953
fix ui
alperozturk96 Dec 18, 2024
7f17549
fix ui
alperozturk96 Dec 18, 2024
80a3659
fix ui
alperozturk96 Dec 18, 2024
b697167
fix ss test
alperozturk96 Dec 18, 2024
36c43d2
shouldShowRecommendedFiles
alperozturk96 Dec 18, 2024
17f4828
fix code analytics
alperozturk96 Dec 19, 2024
7785402
change layout width
alperozturk96 Dec 20, 2024
3c50d72
update lib
alperozturk96 Jan 7, 2025
daf5860
fix duplicated item
alperozturk96 Jan 7, 2025
b1fd72d
add more padding
alperozturk96 Jan 7, 2025
a055086
better alignment, cleanup list_header
alperozturk96 Jan 7, 2025
248d83c
change loader image view background color
alperozturk96 Jan 8, 2025
06f4c95
rebase
alperozturk96 Jan 16, 2025
b043995
update lib
alperozturk96 Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

import com.elyeproj.loaderviewlibrary.LoaderImageView;
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
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;
Expand Down Expand Up @@ -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;
Expand All @@ -106,7 +108,7 @@
*/
public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
implements DisplayUtils.AvatarGenerationListener,
CommonOCFileListAdapterInterface, PopupTextProvider {
CommonOCFileListAdapterInterface, PopupTextProvider, RecommendedFilesAdapter.OnItemClickListener {

private static final int showFilenameColumnThreshold = 4;
private final String userId;
Expand Down Expand Up @@ -143,6 +145,8 @@ public class OCFileListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
private final long headerId = UUID.randomUUID().getLeastSignificantBits();
private final SyncedFolderProvider syncedFolderProvider;

private final Set<Recommendation> recommendedFiles;

public OCFileListAdapter(
Activity activity,
@NonNull User user,
Expand All @@ -152,7 +156,9 @@ public OCFileListAdapter(
OCFileListFragmentInterface ocFileListFragmentInterface,
boolean argHideItemOptions,
boolean gridView,
final ViewThemeUtils viewThemeUtils) {
final ViewThemeUtils viewThemeUtils,
final Set<Recommendation> recommendedFiles) {
this.recommendedFiles = recommendedFiles;
this.ocFileListFragmentInterface = ocFileListFragmentInterface;
this.activity = activity;
this.preferences = preferences;
Expand Down Expand Up @@ -414,9 +420,7 @@ 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);
}
}
Expand All @@ -432,9 +436,22 @@ 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();

PreviewTextFragment.setText(headerViewHolder.getHeaderText(), text, null, activity, true, true, viewThemeUtils);
headerViewHolder.getHeaderView().setOnClickListener(v -> ocFileListFragmentInterface.onHeaderClicked());

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;

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);
Expand Down Expand Up @@ -466,6 +483,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();
Expand Down Expand Up @@ -766,6 +787,10 @@ public boolean shouldShowHeader() {
return false;
}

if (shouldShowRecommendedFiles()) {
return true;
}

if (currentDirectory.getRichWorkspace() == null) {
return false;
}
Expand Down Expand Up @@ -1259,4 +1284,14 @@ public int getFilesCount() {
public void notifyItemChanged(@NonNull OCFile file) {
notifyItemChanged(getItemPosition(file));
}

@Override
public void selectRecommendedFile(@NonNull OCFile file) {
ocFileListFragmentInterface.onItemClicked(file);
}

@Override
public void showRecommendedFileMoreActions(@NonNull OCFile file, @NonNull View view) {
ocFileListFragmentInterface.onOverflowIconClicked(file, view);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -195,28 +195,32 @@ class OCFileListDelegate(
}
}

fun bindGridViewHolder(
gridViewHolder: ListViewHolder,
file: OCFile,
currentDirectory: OCFile?,
searchType: SearchType?
) {
// thumbnail
gridViewHolder.imageFileName?.text = file.fileName
gridViewHolder.thumbnail.tag = file.fileId
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Alper Ozturk <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

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.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

class RecommendedFilesAdapter(
private val context: Context,
private val recommendations: Set<Recommendation>,
private val delegate: OCFileListDelegate,
private val onItemClickListener: OnItemClickListener,
private val storageManager: FileDataStorageManager
) : RecyclerView.Adapter<RecommendedFilesAdapter.RecommendedFilesViewHolder>() {

interface OnItemClickListener {
fun selectRecommendedFile(file: OCFile)
fun showRecommendedFileMoreActions(file: OCFile, view: View)
}

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)
return RecommendedFilesViewHolder(binding)
}

override fun getItemCount(): Int = recommendations.size

@Suppress("MagicNumber")
override fun onBindViewHolder(holder: RecommendedFilesViewHolder, position: Int) {
val item = recommendations.elementAt(position)

holder.binding.run {
name.text = item.name
val modificationTimestamp = (item.timestamp * 1000)
timestamp.text = DisplayUtils.getRelativeTimestamp(context, modificationTimestamp)

val file = storageManager.getFileByLocalId(item.id) ?: return

delegate.setThumbnail(thumbnail, shimmerThumbnail, file)

container.setOnClickListener {
onItemClickListener.selectRecommendedFile(file)
}

moreAction.setOnClickListener {
onItemClickListener.showRecommendedFileMoreActions(file, holder.itemView)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -72,6 +75,7 @@
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.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.utils.Log_OC;
Expand Down Expand Up @@ -248,6 +252,7 @@ protected enum MenuItemAddRemove {
protected MenuItemAddRemove menuItemAddRemoveValue = MenuItemAddRemove.ADD_GRID_AND_SORT_WITH_SEARCH;

private List<MenuItem> mOriginalMenuItems = new ArrayList<>();
private final Set<Recommendation> recommendedFiles = new HashSet<>();

@Override
public void onCreate(Bundle savedInstanceState) {
Expand Down Expand Up @@ -382,7 +387,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);
Expand Down Expand Up @@ -431,6 +436,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()) {
final var recommendations = result.getResultData().getRecommendations();
recommendedFiles.addAll(recommendations);
requireActivity().runOnUiThread(new Runnable() {
@SuppressLint("NotifyDataSetChanged")
@Override
public void run() {
mAdapter.notifyItemChanged(0);
}
});
}
} 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);

Expand All @@ -443,7 +471,8 @@ protected void setAdapter(Bundle args) {
this,
hideItemOptions,
isGridViewPreferred(mFile),
viewThemeUtils
viewThemeUtils,
recommendedFiles
);

setRecyclerViewAdapter(mAdapter);
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/res/drawable/ic_circle.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
~ SPDX-License-Identifier: Apache-2.0
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/bg_default" />
<stroke
android:width="1dp"
android:color="@color/text_color" />
</shape>
19 changes: 19 additions & 0 deletions app/src/main/res/drawable/ic_dots_horizontal.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2018-2024 Google LLC
~ SPDX-License-Identifier: Apache-2.0
-->

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">

<path
android:fillColor="@android:color/white"
android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />

</vector>
Loading
Loading