Skip to content

Commit 3b4eee4

Browse files
committed
feat: add recommended item view holder
Signed-off-by: alperozturk <[email protected]>
1 parent a4b5712 commit 3b4eee4

File tree

8 files changed

+292
-29
lines changed

8 files changed

+292
-29
lines changed

app/src/main/java/com/nextcloud/client/database/entity/RecommendedFileEntity.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,7 @@ fun ArrayList<Recommendation>.toEntity(accountName: String): List<RecommendedFil
6161
}
6262

6363
fun List<RecommendedFileEntity>.toOCFile(storageManager: FileDataStorageManager): ArrayList<OCFile> =
64-
mapNotNull { entity -> entity.id.let { storageManager.getFileByLocalId(it) } }
64+
mapNotNull { entity ->
65+
entity.id.let { storageManager.getFileByLocalId(it).apply { this?.reason = entity.reason } }
66+
}
6567
.toCollection(ArrayList())

app/src/main/java/com/owncloud/android/datamodel/OCFile.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
127127
private List<Tag> tags = new ArrayList<>();
128128
private Long internalFolderSyncTimestamp = -1L;
129129
private String internalFolderSyncResult = "";
130+
private String reason = "";
130131

131132
/**
132133
* URI to the local path of the file contents, if stored in the device; cached after first call to
@@ -1140,4 +1141,12 @@ public boolean exists() {
11401141
final String storagePath = getStoragePath();
11411142
return storagePath != null && new File(storagePath).exists();
11421143
}
1144+
1145+
public void setReason(String value) {
1146+
reason = value;
1147+
}
1148+
1149+
public String getReason() {
1150+
return reason;
1151+
}
11431152
}

app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
474474
}
475475
}
476476

477-
public void bindRecommendedFilesHolder(OCFileListGridItemViewHolder holder, @NonNull OCFile file) {
477+
public void bindRecommendedFilesHolder(OCFileListRecommendedItemViewHolder holder, @NonNull OCFile file) {
478478
bindHolder(holder, holder, file);
479479
}
480480

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2025 Alper Ozturk <[email protected]>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.owncloud.android.ui.adapter
9+
10+
import android.view.View
11+
import android.widget.ImageButton
12+
import android.widget.ImageView
13+
import android.widget.LinearLayout
14+
import android.widget.TextView
15+
import androidx.recyclerview.widget.RecyclerView
16+
import com.elyeproj.loaderviewlibrary.LoaderImageView
17+
import com.owncloud.android.databinding.RecommendedFileItemBinding
18+
19+
class OCFileListRecommendedItemViewHolder(var binding: RecommendedFileItemBinding) :
20+
RecyclerView.ViewHolder(
21+
binding.root
22+
),
23+
ListGridItemViewHolder {
24+
val reason: TextView
25+
get() = binding.reason
26+
override val fileName: TextView
27+
get() = binding.filename
28+
override val extension: TextView?
29+
get() = binding.extension
30+
override val thumbnail: ImageView
31+
get() = binding.thumbnail
32+
33+
override fun showVideoOverlay() {
34+
binding.videoOverlay.visibility = View.VISIBLE
35+
}
36+
override val shimmerThumbnail: LoaderImageView
37+
get() = binding.thumbnailShimmer
38+
override val favorite: ImageView
39+
get() = binding.favoriteAction
40+
override val localFileIndicator: ImageView
41+
get() = binding.localFileIndicator
42+
override val imageFileName: TextView?
43+
get() = null
44+
override val shared: ImageView
45+
get() = binding.sharedIcon
46+
override val checkbox: ImageView
47+
get() = binding.customCheckbox
48+
override val itemLayout: View
49+
get() = binding.recommendedFileItemLayout
50+
override val unreadComments: ImageView
51+
get() = binding.unreadComments
52+
override val gridLivePhotoIndicator: ImageView?
53+
get() = null
54+
override val livePhotoIndicator: TextView?
55+
get() = null
56+
override val livePhotoIndicatorSeparator: TextView?
57+
get() = null
58+
override val fileFeaturesLayout: LinearLayout
59+
get() = binding.fileFeaturesLayout
60+
override val more: ImageButton
61+
get() = binding.more
62+
63+
init {
64+
binding.favoriteAction.drawable.mutate()
65+
}
66+
}

app/src/main/java/com/owncloud/android/ui/adapter/RecommendedFilesAdapter.kt

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,50 +7,30 @@
77

88
package com.owncloud.android.ui.adapter
99

10-
import android.util.TypedValue
1110
import android.view.LayoutInflater
1211
import android.view.ViewGroup
13-
import androidx.constraintlayout.widget.ConstraintLayout
1412
import androidx.recyclerview.widget.RecyclerView
15-
import com.owncloud.android.databinding.GridItemBinding
13+
import com.owncloud.android.databinding.RecommendedFileItemBinding
1614
import com.owncloud.android.datamodel.OCFile
1715

1816
class RecommendedFilesAdapter(
1917
private val fileListAdapter: OCFileListAdapter,
2018
private val recommendations: ArrayList<OCFile>
21-
) : RecyclerView.Adapter<OCFileListGridItemViewHolder>() {
22-
23-
companion object {
24-
private const val LAYOUT_ITEM_WIDTH = 120f
25-
}
19+
) : RecyclerView.Adapter<OCFileListRecommendedItemViewHolder>() {
2620

2721
fun getItemPosition(file: OCFile): Int = recommendations.indexOf(file)
2822

29-
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OCFileListGridItemViewHolder {
30-
val binding = GridItemBinding
23+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OCFileListRecommendedItemViewHolder {
24+
val binding = RecommendedFileItemBinding
3125
.inflate(LayoutInflater.from(parent.context), parent, false)
32-
return OCFileListGridItemViewHolder(binding).apply {
33-
binding.ListItemLayout.setLayoutItemWidth()
34-
}
26+
return OCFileListRecommendedItemViewHolder(binding)
3527
}
3628

3729
override fun getItemCount(): Int = recommendations.size
3830

39-
@Suppress("MagicNumber")
40-
override fun onBindViewHolder(holder: OCFileListGridItemViewHolder, position: Int) {
31+
override fun onBindViewHolder(holder: OCFileListRecommendedItemViewHolder, position: Int) {
4132
val item = recommendations[position]
4233
fileListAdapter.bindRecommendedFilesHolder(holder, item)
34+
holder.reason.text = item.reason
4335
}
44-
45-
// region Private Methods
46-
private fun ConstraintLayout.setLayoutItemWidth() {
47-
layoutParams = layoutParams.apply {
48-
width = TypedValue.applyDimension(
49-
TypedValue.COMPLEX_UNIT_DIP,
50-
LAYOUT_ITEM_WIDTH,
51-
resources.displayMetrics
52-
).toInt()
53-
}
54-
}
55-
// endregion
5636
}
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!--
3+
~ Nextcloud - Android Client
4+
~
5+
~ SPDX-FileCopyrightText: 2025 Alper Ozturk <[email protected]>
6+
~ SPDX-License-Identifier: AGPL-3.0-or-later
7+
-->
8+
<androidx.constraintlayout.widget.ConstraintLayout
9+
xmlns:android="http://schemas.android.com/apk/res/android"
10+
xmlns:app="http://schemas.android.com/apk/res-auto"
11+
xmlns:tools="http://schemas.android.com/tools"
12+
android:id="@+id/recommended_file_item_layout"
13+
android:layout_width="@dimen/grid_container_width"
14+
android:layout_height="@dimen/grid_container_height"
15+
android:layout_margin="@dimen/grid_container_margin">
16+
17+
<com.elyeproj.loaderviewlibrary.LoaderImageView
18+
android:id="@+id/thumbnail_shimmer"
19+
android:layout_width="0dp"
20+
android:layout_height="0dp"
21+
android:contentDescription="@null"
22+
android:src="@drawable/folder"
23+
android:visibility="gone"
24+
app:layout_constraintBottom_toBottomOf="@id/thumbnail"
25+
app:layout_constraintEnd_toEndOf="@id/thumbnail"
26+
app:layout_constraintStart_toStartOf="@id/thumbnail"
27+
app:layout_constraintTop_toTopOf="@id/thumbnail"
28+
tools:visibility="visible" />
29+
30+
<com.owncloud.android.ui.SquareImageView
31+
android:id="@+id/thumbnail"
32+
android:layout_width="@dimen/standard_list_item_size"
33+
android:layout_height="@dimen/standard_list_item_size"
34+
android:layout_marginTop="@dimen/standard_half_margin"
35+
android:contentDescription="@null"
36+
android:src="@drawable/folder"
37+
app:layout_constraintEnd_toEndOf="parent"
38+
app:layout_constraintStart_toStartOf="parent"
39+
app:layout_constraintTop_toTopOf="parent"
40+
tools:visibility="visible" />
41+
42+
<LinearLayout
43+
android:id="@+id/file_features_layout"
44+
android:layout_width="wrap_content"
45+
android:layout_height="wrap_content"
46+
android:alpha="0.9"
47+
android:background="@drawable/rounded_rect"
48+
android:backgroundTint="@color/grid_file_features_background_color"
49+
android:gravity="center"
50+
android:orientation="horizontal"
51+
android:padding="@dimen/standard_quarter_padding"
52+
android:translationZ="4dp"
53+
app:layout_constraintBottom_toBottomOf="@+id/thumbnail"
54+
app:layout_constraintEnd_toEndOf="parent"
55+
app:layout_constraintHorizontal_bias="0.85"
56+
app:layout_constraintStart_toStartOf="parent"
57+
app:layout_constraintTop_toTopOf="parent"
58+
app:layout_constraintVertical_bias="0.85">
59+
60+
<ImageView
61+
android:id="@+id/favorite_action"
62+
android:layout_width="@dimen/grid_layout_item_size"
63+
android:layout_height="@dimen/grid_layout_item_size"
64+
android:layout_marginEnd="@dimen/grid_layout_margin_end"
65+
android:contentDescription="@string/favorite_icon"
66+
android:src="@drawable/favorite"
67+
tools:visibility="visible" />
68+
69+
<ImageView
70+
android:id="@+id/videoOverlay"
71+
android:layout_width="@dimen/grid_layout_item_size"
72+
android:layout_height="@dimen/grid_layout_item_size"
73+
android:layout_marginEnd="@dimen/grid_layout_margin_end"
74+
android:contentDescription="@string/video_overlay_icon"
75+
android:src="@drawable/video_white"
76+
android:visibility="gone"
77+
app:tint="@color/grid_file_features_icon_color"
78+
tools:visibility="visible" />
79+
80+
<ImageView
81+
android:id="@+id/sharedIcon"
82+
android:layout_width="@dimen/grid_layout_item_size"
83+
android:layout_height="@dimen/grid_layout_item_size"
84+
android:layout_marginEnd="@dimen/grid_layout_margin_end"
85+
android:contentDescription="@string/shared_icon_shared_via_link"
86+
android:src="@drawable/shared_via_link"
87+
app:tint="@color/grid_file_features_icon_color"
88+
tools:visibility="visible" />
89+
90+
<ImageView
91+
android:id="@+id/unreadComments"
92+
android:layout_width="@dimen/grid_layout_item_size"
93+
android:layout_height="@dimen/grid_layout_item_size"
94+
android:layout_marginEnd="@dimen/grid_layout_margin_end"
95+
android:clickable="true"
96+
android:contentDescription="@string/unread_comments"
97+
android:focusable="true"
98+
android:src="@drawable/ic_comment_grid"
99+
android:visibility="gone"
100+
app:tint="@color/grid_file_features_icon_color"
101+
tools:ignore="TouchTargetSizeCheck"
102+
tools:visibility="visible" />
103+
104+
<ImageView
105+
android:id="@+id/grid_live_photo_indicator"
106+
android:layout_width="@dimen/grid_layout_item_size"
107+
android:layout_height="@dimen/grid_layout_item_size"
108+
android:layout_marginEnd="@dimen/grid_layout_margin_end"
109+
android:clickable="true"
110+
android:contentDescription="@string/grid_file_features_live_photo_content_description"
111+
android:focusable="true"
112+
android:src="@drawable/ic_live_photo"
113+
android:visibility="gone"
114+
app:tint="@color/grid_file_features_icon_color"
115+
tools:ignore="TouchTargetSizeCheck"
116+
tools:visibility="visible" />
117+
118+
<ImageView
119+
android:id="@+id/localFileIndicator"
120+
android:layout_width="@dimen/grid_layout_item_size"
121+
android:layout_height="@dimen/grid_layout_item_size"
122+
android:layout_marginEnd="@dimen/grid_layout_margin_end"
123+
android:contentDescription="@string/synced_icon"
124+
android:src="@drawable/ic_synced"
125+
tools:visibility="visible" />
126+
127+
</LinearLayout>
128+
129+
<ImageView
130+
android:id="@+id/custom_checkbox"
131+
android:layout_width="@dimen/grid_checkbox_size"
132+
android:layout_height="@dimen/grid_checkbox_size"
133+
android:layout_marginStart="@dimen/grid_checkbox_margin"
134+
android:layout_marginTop="@dimen/grid_checkbox_margin"
135+
android:contentDescription="@string/checkbox"
136+
android:src="@android:drawable/checkbox_off_background"
137+
app:layout_constraintStart_toStartOf="parent"
138+
app:layout_constraintTop_toTopOf="parent"
139+
tools:visibility="visible" />
140+
141+
<LinearLayout
142+
android:layout_width="match_parent"
143+
android:layout_height="wrap_content"
144+
app:layout_constraintEnd_toEndOf="parent"
145+
app:layout_constraintBottom_toBottomOf="parent"
146+
app:layout_constraintStart_toStartOf="parent"
147+
app:layout_constraintTop_toBottomOf="@+id/thumbnail"
148+
android:orientation="vertical">
149+
150+
<LinearLayout
151+
android:layout_width="match_parent"
152+
android:gravity="center"
153+
android:layout_height="wrap_content"
154+
android:orientation="horizontal">
155+
156+
<TextView
157+
android:id="@+id/filename"
158+
android:layout_width="wrap_content"
159+
android:layout_height="match_parent"
160+
android:maxWidth="@dimen/grid_container_default_max_file_name"
161+
android:layout_gravity="center"
162+
android:ellipsize="end"
163+
android:gravity="center"
164+
android:singleLine="true"
165+
android:text="@string/placeholder_filename"
166+
android:textSize="@dimen/grid_item_text_size" />
167+
168+
<TextView
169+
android:id="@+id/extension"
170+
android:layout_width="wrap_content"
171+
android:layout_height="match_parent"
172+
android:ellipsize="start"
173+
android:gravity="center"
174+
android:maxLines="1"
175+
android:text="@string/placeholder_extension"
176+
android:textSize="@dimen/grid_item_text_size" />
177+
178+
</LinearLayout>
179+
180+
<TextView
181+
android:id="@+id/reason"
182+
android:layout_width="wrap_content"
183+
android:layout_height="match_parent"
184+
android:layout_gravity="center"
185+
android:gravity="center"
186+
android:singleLine="true"
187+
android:text="@string/placeholder_reason"
188+
android:textSize="@dimen/grid_recommended_item_reason_text_size" />
189+
190+
</LinearLayout>
191+
192+
<ImageButton
193+
android:id="@+id/more"
194+
android:layout_width="@dimen/grid_bottom_view_height"
195+
android:layout_height="@dimen/grid_bottom_view_height"
196+
android:layout_gravity="end"
197+
android:background="@color/transparent"
198+
android:contentDescription="@string/overflow_menu"
199+
app:layout_constraintBottom_toBottomOf="parent"
200+
app:layout_constraintEnd_toEndOf="parent"
201+
app:srcCompat="@drawable/ic_dots_vertical"
202+
tools:ignore="TouchTargetSizeCheck" />
203+
204+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/res/values/dims.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<dimen name="bottom_navigation_view_margin">100dp</dimen>
3838
<dimen name="standard_quarter_margin">4dp</dimen>
3939

40+
<dimen name="grid_recommended_item_reason_text_size">12sp</dimen>
4041
<dimen name="grid_bottom_view_height">20dp</dimen>
4142
<dimen name="grid_layout_item_size">10dp</dimen>
4243
<dimen name="grid_layout_margin_end">2dp</dimen>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@
450450

451451
<string name="placeholder_sentence">This is a placeholder</string>
452452
<string name="placeholder_filename">placeholder</string>
453+
<string name="placeholder_reason">Recently edited</string>
453454
<string name="placeholder_extension">.txt</string>
454455
<string name="placeholder_fileSize">389 KB</string>
455456
<string name="placeholder_timestamp">2012/05/18 12:23 PM</string>

0 commit comments

Comments
 (0)