Skip to content

Commit 251ef24

Browse files
Merge pull request #12278 from nextcloud/feature/new_grid-layout
New Grid Layout
2 parents 2df8096 + d3ab706 commit 251ef24

15 files changed

+514
-273
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Nextcloud Android client application
3+
*
4+
* @author Alper Ozturk
5+
* Copyright (C) 2023 Alper Ozturk
6+
* Copyright (C) 2023 Nextcloud GmbH
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*/
21+
22+
package com.nextcloud.utils.extensions
23+
24+
import android.content.Context
25+
import android.graphics.Outline
26+
import android.util.TypedValue
27+
import android.view.View
28+
import android.view.ViewOutlineProvider
29+
30+
fun createRoundedOutline(context: Context, cornerRadiusValue: Float): ViewOutlineProvider {
31+
return object : ViewOutlineProvider() {
32+
override fun getOutline(view: View, outline: Outline) {
33+
val left = 0
34+
val top = 0
35+
val right = view.width
36+
val bottom = view.height
37+
val cornerRadius = TypedValue.applyDimension(
38+
TypedValue.COMPLEX_UNIT_DIP,
39+
cornerRadiusValue,
40+
context.resources.displayMetrics
41+
).toInt()
42+
43+
outline.setRoundRect(left, top, right, bottom, cornerRadius.toFloat())
44+
}
45+
}
46+
}

app/src/main/java/com/owncloud/android/MainApp.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@
129129
* Contains methods to build the "static" strings. These strings were before constants in different classes
130130
*/
131131
public class MainApp extends MultiDexApplication implements HasAndroidInjector {
132-
133132
public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_23;
134133
public static final OwnCloudVersion MINIMUM_SUPPORTED_SERVER_VERSION = OwnCloudVersion.nextcloud_16;
135134

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
package com.owncloud.android.ui.adapter
2323

2424
import android.view.View
25+
import android.widget.ImageButton
2526
import android.widget.ImageView
27+
import android.widget.LinearLayout
2628
import android.widget.TextView
2729
import com.elyeproj.loaderviewlibrary.LoaderImageView
2830

@@ -32,12 +34,14 @@ interface ListGridImageViewHolder {
3234
val shimmerThumbnail: LoaderImageView
3335
val favorite: ImageView
3436
val localFileIndicator: ImageView
37+
val imageFileName: TextView?
3538
val shared: ImageView
3639
val checkbox: ImageView
3740
val itemLayout: View
3841
val unreadComments: ImageView
39-
40-
val gridLivePhotoIndicator: TextView?
42+
val more: ImageButton?
43+
val fileFeaturesLayout: LinearLayout?
44+
val gridLivePhotoIndicator: ImageView?
4145
val livePhotoIndicator: TextView?
4246
val livePhotoIndicatorSeparator: TextView?
4347
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import android.widget.LinearLayout;
3333
import android.widget.TextView;
3434

35+
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
3536
import com.nextcloud.client.preferences.AppPreferences;
3637
import com.owncloud.android.R;
3738
import com.owncloud.android.datamodel.ThumbnailsCacheManager;
@@ -54,6 +55,7 @@
5455

5556
import androidx.annotation.NonNull;
5657
import androidx.annotation.VisibleForTesting;
58+
import androidx.core.content.ContextCompat;
5759
import androidx.recyclerview.widget.RecyclerView;
5860

5961
/**
@@ -186,11 +188,10 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
186188
} else {
187189
gridViewHolder.checkbox.setVisibility(View.VISIBLE);
188190
if (isCheckedFile(file)) {
189-
gridViewHolder.itemLayout.setBackgroundColor(mContext.getResources()
190-
.getColor(R.color.selected_item_background));
191+
gridViewHolder.itemLayout.setBackgroundColor(ContextCompat.getColor(mContext, R.color.selected_item_background));
191192

192193
gridViewHolder.checkbox.setImageDrawable(
193-
viewThemeUtils.platform.tintPrimaryDrawable(mContext, R.drawable.ic_checkbox_marked));
194+
viewThemeUtils.platform.tintDrawable(mContext, R.drawable.ic_checkbox_marked, ColorRole.PRIMARY));
194195
} else {
195196
gridViewHolder.itemLayout.setBackgroundColor(mContext.getResources().getColor(R.color.bg_default));
196197
gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline);

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

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
import android.view.LayoutInflater;
4040
import android.view.View;
4141
import android.view.ViewGroup;
42+
import android.widget.ImageButton;
4243
import android.widget.ImageView;
44+
import android.widget.LinearLayout;
4345

4446
import com.elyeproj.loaderviewlibrary.LoaderImageView;
4547
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
@@ -300,6 +302,7 @@ public long getItemId(int position) {
300302
return headerId;
301303
}
302304

305+
303306
// skip header
304307
position--;
305308
}
@@ -365,8 +368,7 @@ public boolean isEmpty() {
365368
@Override
366369
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
367370
switch (viewType) {
368-
default:
369-
case VIEWTYPE_ITEM:
371+
default -> {
370372
if (gridView) {
371373
return new OCFileListGridItemViewHolder(
372374
GridItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
@@ -376,8 +378,8 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
376378
ListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
377379
);
378380
}
379-
380-
case VIEWTYPE_IMAGE:
381+
}
382+
case VIEWTYPE_IMAGE -> {
381383
if (gridView) {
382384
return new OCFileListGridImageViewHolder(
383385
GridImageBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
@@ -387,23 +389,22 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int
387389
ListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
388390
);
389391
}
390-
391-
case VIEWTYPE_FOOTER:
392+
}
393+
case VIEWTYPE_FOOTER -> {
392394
return new OCFileListFooterViewHolder(
393395
ListFooterBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)
394396
);
395-
396-
case VIEWTYPE_HEADER:
397+
}
398+
case VIEWTYPE_HEADER -> {
397399
ListHeaderBinding binding = ListHeaderBinding.inflate(
398400
LayoutInflater.from(parent.getContext()),
399401
parent,
400402
false);
401-
402403
ViewGroup.LayoutParams layoutParams = binding.headerView.getLayoutParams();
403404
layoutParams.height = (int) (parent.getHeight() * 0.3);
404405
binding.headerView.setLayoutParams(layoutParams);
405-
406406
return new OCFileListHeaderViewHolder(binding);
407+
}
407408
}
408409
}
409410

@@ -430,19 +431,54 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
430431
}
431432

432433
ocFileListDelegate.bindGridViewHolder(gridViewHolder, file, searchType);
434+
checkVisibilityOfMoreButtons(gridViewHolder);
435+
checkVisibilityOfFileFeaturesLayout(gridViewHolder);
433436

434437
if (holder instanceof ListItemViewHolder) {
435438
bindListItemViewHolder((ListItemViewHolder) gridViewHolder, file);
436439
}
437440

438441
if (holder instanceof ListGridItemViewHolder) {
439442
bindListGridItemViewHolder((ListGridItemViewHolder) holder, file);
443+
checkVisibilityOfMoreButtons((ListGridItemViewHolder) holder);
444+
checkVisibilityOfFileFeaturesLayout((ListGridItemViewHolder) holder);
440445
}
441446

442447
updateLivePhotoIndicators(gridViewHolder, file);
443448
}
444449
}
445450

451+
private void checkVisibilityOfFileFeaturesLayout(ListGridImageViewHolder holder) {
452+
int fileFeaturesVisibility = View.GONE;
453+
LinearLayout fileFeaturesLayout = holder.getFileFeaturesLayout();
454+
455+
if (fileFeaturesLayout == null) {
456+
return;
457+
}
458+
459+
for (int i = 0; i < fileFeaturesLayout.getChildCount(); i++) {
460+
View child = fileFeaturesLayout.getChildAt(i);
461+
if (child.getVisibility() == View.VISIBLE) {
462+
fileFeaturesVisibility = View.VISIBLE;
463+
}
464+
}
465+
466+
fileFeaturesLayout.setVisibility(fileFeaturesVisibility);
467+
}
468+
469+
private void checkVisibilityOfMoreButtons(ListGridImageViewHolder holder) {
470+
ImageButton moreButton = holder.getMore();
471+
if (moreButton == null) {
472+
return;
473+
}
474+
475+
if (isMultiSelect()) {
476+
moreButton.setVisibility(View.GONE);
477+
} else {
478+
moreButton.setVisibility(View.VISIBLE);
479+
}
480+
}
481+
446482
private void mergeOCFilesForLivePhoto() {
447483
List<OCFile> filesToRemove = new ArrayList<>();
448484

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

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@ import android.graphics.drawable.ColorDrawable
2727
import android.os.AsyncTask
2828
import android.view.View
2929
import android.widget.ImageView
30+
import androidx.core.content.ContextCompat
3031
import androidx.core.content.res.ResourcesCompat
3132
import com.elyeproj.loaderviewlibrary.LoaderImageView
33+
import com.nextcloud.android.common.ui.theme.utils.ColorRole
3234
import com.nextcloud.client.account.User
3335
import com.nextcloud.client.preferences.AppPreferences
36+
import com.nextcloud.utils.extensions.createRoundedOutline
3437
import com.owncloud.android.R
3538
import com.owncloud.android.datamodel.FileDataStorageManager
3639
import com.owncloud.android.datamodel.OCFile
@@ -142,7 +145,7 @@ class OCFileListDelegate(
142145
storageManager,
143146
asyncGalleryTasks,
144147
file.remoteId,
145-
context.resources.getColor(R.color.bg_default)
148+
ContextCompat.getColor(context, R.color.bg_default)
146149
)
147150
var drawable = MimeTypeUtil.getFileTypeIcon(
148151
file.mimeType,
@@ -204,6 +207,7 @@ class OCFileListDelegate(
204207
searchType: SearchType?
205208
) {
206209
// thumbnail
210+
gridViewHolder.imageFileName?.text = file.fileName
207211
gridViewHolder.thumbnail.tag = file.fileId
208212
DisplayUtils.setThumbnail(
209213
file,
@@ -218,6 +222,7 @@ class OCFileListDelegate(
218222
viewThemeUtils,
219223
syncFolderProvider
220224
)
225+
221226
// item layout + click listeners
222227
bindGridItemLayout(file, gridViewHolder)
223228

@@ -232,17 +237,20 @@ class OCFileListDelegate(
232237
}
233238

234239
// download state
235-
gridViewHolder.localFileIndicator.visibility = View.INVISIBLE // default first
240+
gridViewHolder.localFileIndicator.visibility = View.GONE // default first
236241

237242
// metadata (downloaded, favorite)
238243
bindGridMetadataViews(file, gridViewHolder)
239244

240245
// shares
241-
val shouldHideShare = gridView ||
246+
val shouldHideShare = (
242247
hideItemOptions ||
243-
!file.isFolder && file.isEncrypted ||
244-
file.isEncrypted && !EncryptionUtils.supportsSecureFiledrop(file, user) ||
245-
searchType == SearchType.FAVORITE_SEARCH
248+
!file.isFolder &&
249+
file.isEncrypted ||
250+
file.isEncrypted &&
251+
!EncryptionUtils.supportsSecureFiledrop(file, user) ||
252+
searchType == SearchType.FAVORITE_SEARCH
253+
)
246254
if (shouldHideShare) {
247255
gridViewHolder.shared.visibility = View.GONE
248256
} else {
@@ -263,34 +271,63 @@ class OCFileListDelegate(
263271
}
264272

265273
private fun bindGridItemLayout(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
266-
if (highlightedItem != null && file.fileId == highlightedItem!!.fileId) {
267-
gridViewHolder.itemLayout.setBackgroundColor(
268-
context.resources
269-
.getColor(R.color.selected_item_background)
270-
)
271-
} else if (isCheckedFile(file)) {
272-
gridViewHolder.itemLayout.setBackgroundColor(
273-
context.resources
274-
.getColor(R.color.selected_item_background)
275-
)
276-
gridViewHolder.checkbox.setImageDrawable(
277-
viewThemeUtils.platform.tintPrimaryDrawable(context, R.drawable.ic_checkbox_marked)
278-
)
279-
} else {
280-
gridViewHolder.itemLayout.setBackgroundColor(context.resources.getColor(R.color.bg_default))
281-
gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline)
274+
setItemLayoutBackgroundColor(file, gridViewHolder)
275+
setCheckBoxImage(file, gridViewHolder)
276+
setItemLayoutOnClickListeners(file, gridViewHolder)
277+
278+
gridViewHolder.more?.setOnClickListener {
279+
ocFileListFragmentInterface.onOverflowIconClicked(file, it)
282280
}
281+
}
282+
283+
private fun setItemLayoutOnClickListeners(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
283284
gridViewHolder.itemLayout.setOnClickListener { ocFileListFragmentInterface.onItemClicked(file) }
285+
284286
if (!hideItemOptions) {
285-
gridViewHolder.itemLayout.isLongClickable = true
286-
gridViewHolder.itemLayout.setOnLongClickListener {
287-
ocFileListFragmentInterface.onLongItemClicked(
288-
file
289-
)
287+
gridViewHolder.itemLayout.apply {
288+
isLongClickable = true
289+
setOnLongClickListener {
290+
ocFileListFragmentInterface.onLongItemClicked(
291+
file
292+
)
293+
}
290294
}
291295
}
292296
}
293297

298+
private fun setItemLayoutBackgroundColor(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
299+
val cornerRadius = context.resources.getDimension(R.dimen.selected_grid_container_radius)
300+
301+
val isDarkModeActive = (syncFolderProvider?.preferences?.isDarkModeEnabled == true)
302+
val selectedItemBackgroundColorId: Int = if (isDarkModeActive) {
303+
R.color.action_mode_background
304+
} else {
305+
R.color.selected_item_background
306+
}
307+
308+
val itemLayoutBackgroundColorId: Int = if (file.fileId == highlightedItem?.fileId || isCheckedFile(file)) {
309+
selectedItemBackgroundColorId
310+
} else {
311+
R.color.bg_default
312+
}
313+
314+
gridViewHolder.itemLayout.apply {
315+
outlineProvider = createRoundedOutline(context, cornerRadius)
316+
clipToOutline = true
317+
setBackgroundColor(ContextCompat.getColor(context, itemLayoutBackgroundColorId))
318+
}
319+
}
320+
321+
private fun setCheckBoxImage(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
322+
if (isCheckedFile(file)) {
323+
gridViewHolder.checkbox.setImageDrawable(
324+
viewThemeUtils.platform.tintDrawable(context, R.drawable.ic_checkbox_marked, ColorRole.PRIMARY)
325+
)
326+
} else {
327+
gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline)
328+
}
329+
}
330+
294331
private fun bindGridMetadataViews(file: OCFile, gridViewHolder: ListGridImageViewHolder) {
295332
if (showMetadata) {
296333
showLocalFileIndicator(file, gridViewHolder)

0 commit comments

Comments
 (0)