From 8273f9b7edc3ba4af8c6ac1bfa1ca84705b6b2bb Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 9 Oct 2026 12:28:37 +0200 Subject: [PATCH 1/3] feat(gallery): indicate unsupported media Signed-off-by: alperozturk96 --- .../utils/extensions/ImageViewExtensions.kt | 17 ++++--- .../android/ui/adapter/GalleryRowHolder.kt | 33 +++++++------ .../android/ui/adapter/OCFileListDelegate.kt | 25 +++++++--- .../res/layout/gallery_unsupported_cell.xml | 48 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 5 files changed, 97 insertions(+), 27 deletions(-) create mode 100644 app/src/main/res/layout/gallery_unsupported_cell.xml diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt index 5a8a0306f119..781f39ac186d 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt @@ -19,7 +19,6 @@ import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat import com.elyeproj.loaderviewlibrary.LoaderImageView -import com.nextcloud.utils.OCFileUtils import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.MimeTypeUtil @@ -38,13 +37,19 @@ fun ImageView.stopShimmer(shimmer: LoaderImageView?) { setVisibility(View.VISIBLE) } -fun ImageView.setMediaPlaceholder(file: OCFile, iconInset: Int) { - scaleType = ImageView.ScaleType.FIT_CENTER - setPadding(iconInset, iconInset, iconInset, iconInset) - setBackgroundResource(R.color.media_placeholder_background) +fun ImageView.setMediaLoading(shimmer: LoaderImageView?) { + scaleType = ImageView.ScaleType.CENTER_CROP + setPadding(0, 0, 0, 0) + background = null foreground = null - setImageDrawable(OCFileUtils.getMediaPlaceholder(file)) + setImageDrawable(null) setTag(R.id.media_thumbnail_file_id, null) + + shimmer?.run { + setImageResource(R.drawable.background) + resetLoader() + visibility = View.VISIBLE + } } fun ImageView.setMediaThumbnail(file: OCFile, bitmap: Bitmap) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt index 76f7416b597d..f5801d256638 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt @@ -9,6 +9,7 @@ package com.owncloud.android.ui.adapter import android.view.Gravity +import android.view.LayoutInflater import android.view.View import android.view.ViewOutlineProvider import android.widget.FrameLayout @@ -22,20 +23,21 @@ import com.nextcloud.utils.extensions.createRoundedOutline import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R import com.owncloud.android.databinding.GalleryRowBinding +import com.owncloud.android.databinding.GalleryUnsupportedCellBinding import com.owncloud.android.datamodel.GalleryCellSize import com.owncloud.android.datamodel.GalleryRow import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.theme.ViewThemeUtils private const val CHECKED_SCALE = 0.8f -private const val PLACEHOLDER_ICON_INSET_RATIO = 0.32f private const val UNCHECKED_SCALE = 1.0f private const val SELECTION_ANIMATION_DURATION_MS = 150L private const val SELECTION_BACKGROUND_INDEX = 0 private const val SHIMMER_INDEX = 1 private const val THUMBNAIL_INDEX = 2 -private const val CHECKBOX_INDEX = 3 +private const val UNSUPPORTED_INDEX = 3 +private const val CHECKBOX_INDEX = 4 class GalleryRowHolder( val binding: GalleryRowBinding, @@ -127,6 +129,7 @@ class GalleryRowHolder( addView(selectionBackground) addView(shimmer) addView(thumbnail) + addView(GalleryUnsupportedCellBinding.inflate(LayoutInflater.from(context), this, false).root) addView(checkbox) } } @@ -136,24 +139,25 @@ class GalleryRowHolder( val selectionBackground = frameLayout[SELECTION_BACKGROUND_INDEX] val shimmer = frameLayout[SHIMMER_INDEX] as LoaderImageView val thumbnail = frameLayout[THUMBNAIL_INDEX] as ImageView + val unsupported = GalleryUnsupportedCellBinding.bind(frameLayout[UNSUPPORTED_INDEX]) val checkbox = frameLayout[CHECKBOX_INDEX] as ImageView val endMargin = if (isLast) zero else smallMargin applyCellSize(shimmer, size, endMargin = zero, bottomMargin = zero) applyCellSize(thumbnail, size, endMargin = endMargin, bottomMargin = smallMargin) + applyCellSize(unsupported.root, size, endMargin = endMargin, bottomMargin = smallMargin) applyCellSize(selectionBackground, size, endMargin = endMargin, bottomMargin = smallMargin) val isChecked = ocFileListDelegate.isCheckedFile(file) + val isSameFileRebound = thumbnail.tag == file.fileId selectionBackground.setVisibleIf(isChecked) - applySelection(thumbnail, file, isChecked) + applySelection(thumbnail, isChecked, isSameFileRebound) + applySelection(unsupported.root, isChecked, isSameFileRebound) applyCheckBox(checkbox, isChecked) - ocFileListDelegate.bindGalleryRow(shimmer, thumbnail, file, this, placeholderInset(size)) + ocFileListDelegate.bindGalleryRow(shimmer, thumbnail, unsupported, file, this) } - private fun placeholderInset(size: GalleryCellSize): Int = - (minOf(size.width, size.height) * PLACEHOLDER_ICON_INSET_RATIO).toInt() - private fun applyCellSize(view: View, size: GalleryCellSize, endMargin: Int, bottomMargin: Int) { val params = view.layoutParams as FrameLayout.LayoutParams @@ -172,21 +176,20 @@ class GalleryRowHolder( view.layoutParams = params } - private fun applySelection(thumbnail: ImageView, file: OCFile, isChecked: Boolean) { - thumbnail.outlineProvider = if (isChecked) selectedOutline else ViewOutlineProvider.BACKGROUND - thumbnail.clipToOutline = isChecked + private fun applySelection(view: View, isChecked: Boolean, isSameFileRebound: Boolean) { + view.outlineProvider = if (isChecked) selectedOutline else ViewOutlineProvider.BACKGROUND + view.clipToOutline = isChecked val scale = if (isChecked) CHECKED_SCALE else UNCHECKED_SCALE - thumbnail.animate().cancel() + view.animate().cancel() - val isSameFileRebound = thumbnail.tag == file.fileId if (!isSameFileRebound) { - thumbnail.scaleX = scale - thumbnail.scaleY = scale + view.scaleX = scale + view.scaleY = scale return } - thumbnail.animate() + view.animate() .scaleX(scale) .scaleY(scale) .setDuration(SELECTION_ANIMATION_DURATION_MS) 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 b1e713211dd0..a3f5f9a51f8b 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 @@ -19,8 +19,9 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper import com.nextcloud.client.jobs.gallery.GalleryImageGenerationJob import com.nextcloud.client.jobs.gallery.GalleryImageGenerationListener import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.utils.OCFileUtils import com.nextcloud.utils.extensions.makeRounded -import com.nextcloud.utils.extensions.setMediaPlaceholder +import com.nextcloud.utils.extensions.setMediaLoading import com.nextcloud.utils.extensions.setVisibleIf import com.nextcloud.utils.extensions.showsMediaThumbnailOf import com.nextcloud.utils.extensions.stopShimmer @@ -28,6 +29,7 @@ import com.nextcloud.utils.mdm.MDMConfig import com.nextcloud.utils.thumbnail.ThumbnailArguments import com.nextcloud.utils.thumbnail.ThumbnailGenerator import com.owncloud.android.R +import com.owncloud.android.databinding.GalleryUnsupportedCellBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.SyncedFolderProvider @@ -105,14 +107,15 @@ class OCFileListDelegate( fun bindGalleryRow( shimmer: LoaderImageView?, imageView: ImageView, + unsupported: GalleryUnsupportedCellBinding, file: OCFile, - galleryRowHolder: GalleryRowHolder, - placeholderInset: Int + galleryRowHolder: GalleryRowHolder ) { bindGalleryRowListeners(imageView, file, galleryRowHolder) if (imageView.showsMediaThumbnailOf(file) && !file.isUpdateThumbnailNeeded) { imageView.tag = file.fileId + unsupported.root.setVisibleIf(false) imageView.stopShimmer(shimmer) return } @@ -122,7 +125,8 @@ class OCFileListDelegate( imageView.tag = file.fileId ViewCompat.setTransitionName(imageView, NavigationAnimator.sharedElementName(file)) - imageView.setMediaPlaceholder(file, placeholderInset) + unsupported.root.setVisibleIf(false) + imageView.setMediaLoading(shimmer) val job = ioScope.launch(start = CoroutineStart.LAZY) { try { @@ -137,9 +141,12 @@ class OCFileListDelegate( } override fun onError() { - if (imageView.tag == file.fileId) { - imageView.stopShimmer(shimmer) + if (imageView.tag != file.fileId || file.isPreviewAvailable) { + return } + + imageView.stopShimmer(shimmer) + showUnsupported(unsupported, file) } } ) @@ -153,6 +160,12 @@ class OCFileListDelegate( job.start() } + private fun showUnsupported(unsupported: GalleryUnsupportedCellBinding, file: OCFile) { + unsupported.icon.setImageDrawable(OCFileUtils.getMediaPlaceholder(file)) + unsupported.fileName.text = file.fileName + unsupported.root.setVisibleIf(true) + } + fun cancelGalleryRow(imageView: ImageView) { GalleryImageGenerationJob.cancelPreviousJob(imageView) } diff --git a/app/src/main/res/layout/gallery_unsupported_cell.xml b/app/src/main/res/layout/gallery_unsupported_cell.xml new file mode 100644 index 000000000000..58bc6abfff0c --- /dev/null +++ b/app/src/main/res/layout/gallery_unsupported_cell.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c18bcf8427e0..87eb97e7e6ef 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,6 +53,7 @@ Add to album File added successfully Unsupported media + Unsupported Select images from account This name is already in use. Already exists. From 6a2f6e62a983e66842df39e8dca8822f5d2d390f Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 9 Oct 2026 12:46:57 +0200 Subject: [PATCH 2/3] use gallery cell instead of introducing another index Signed-off-by: alperozturk96 --- .../utils/extensions/ImageViewExtensions.kt | 17 ++-- .../android/ui/adapter/GalleryRowHolder.kt | 98 ++++++------------- .../android/ui/adapter/OCFileListDelegate.kt | 15 ++- app/src/main/res/layout/gallery_cell.xml | 45 +++++++++ 4 files changed, 92 insertions(+), 83 deletions(-) create mode 100644 app/src/main/res/layout/gallery_cell.xml diff --git a/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt index 781f39ac186d..5a8a0306f119 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/ImageViewExtensions.kt @@ -19,6 +19,7 @@ import androidx.annotation.ColorInt import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat import com.elyeproj.loaderviewlibrary.LoaderImageView +import com.nextcloud.utils.OCFileUtils import com.owncloud.android.R import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.MimeTypeUtil @@ -37,19 +38,13 @@ fun ImageView.stopShimmer(shimmer: LoaderImageView?) { setVisibility(View.VISIBLE) } -fun ImageView.setMediaLoading(shimmer: LoaderImageView?) { - scaleType = ImageView.ScaleType.CENTER_CROP - setPadding(0, 0, 0, 0) - background = null +fun ImageView.setMediaPlaceholder(file: OCFile, iconInset: Int) { + scaleType = ImageView.ScaleType.FIT_CENTER + setPadding(iconInset, iconInset, iconInset, iconInset) + setBackgroundResource(R.color.media_placeholder_background) foreground = null - setImageDrawable(null) + setImageDrawable(OCFileUtils.getMediaPlaceholder(file)) setTag(R.id.media_thumbnail_file_id, null) - - shimmer?.run { - setImageResource(R.drawable.background) - resetLoader() - visibility = View.VISIBLE - } } fun ImageView.setMediaThumbnail(file: OCFile, bitmap: Bitmap) { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt index f5801d256638..ec6878893642 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt @@ -8,37 +8,29 @@ */ package com.owncloud.android.ui.adapter -import android.view.Gravity import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.view.ViewOutlineProvider -import android.widget.FrameLayout import android.widget.ImageView import androidx.core.content.ContextCompat -import androidx.core.view.get import com.afollestad.sectionedrecyclerview.SectionedViewHolder -import com.elyeproj.loaderviewlibrary.LoaderImageView import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.utils.extensions.createRoundedOutline import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R +import com.owncloud.android.databinding.GalleryCellBinding import com.owncloud.android.databinding.GalleryRowBinding -import com.owncloud.android.databinding.GalleryUnsupportedCellBinding import com.owncloud.android.datamodel.GalleryCellSize import com.owncloud.android.datamodel.GalleryRow import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.theme.ViewThemeUtils private const val CHECKED_SCALE = 0.8f +private const val PLACEHOLDER_ICON_INSET_RATIO = 0.32f private const val UNCHECKED_SCALE = 1.0f private const val SELECTION_ANIMATION_DURATION_MS = 150L -private const val SELECTION_BACKGROUND_INDEX = 0 -private const val SHIMMER_INDEX = 1 -private const val THUMBNAIL_INDEX = 2 -private const val UNSUPPORTED_INDEX = 3 -private const val CHECKBOX_INDEX = 4 - class GalleryRowHolder( val binding: GalleryRowBinding, private val ocFileListDelegate: OCFileListDelegate, @@ -47,9 +39,10 @@ class GalleryRowHolder( ) : SectionedViewHolder(binding.root) { val context = galleryAdapter.context + private val cells = mutableListOf() + private val zero by lazy { context.resources.getInteger(R.integer.zero) } private val smallMargin by lazy { context.resources.getInteger(R.integer.small_margin) } - private val checkBoxMargin by lazy { context.resources.getDimensionPixelSize(R.dimen.standard_half_margin) } private val selectedOutline by lazy { val resources = context.resources @@ -76,72 +69,34 @@ class GalleryRowHolder( row.files.forEachIndexed { index, file -> val size = row.cellSizes.getOrNull(index) ?: return@forEachIndexed - bindCell(index, file, size, isLast = index == row.files.lastIndex) + bindCell(cells[index], file, size, isLast = index == row.files.lastIndex) } } fun recycle() { - for (index in 0 until binding.rowLayout.childCount) { - ocFileListDelegate.cancelGalleryRow(thumbnailAt(index)) - } + cells.forEach { ocFileListDelegate.cancelGalleryRow(it.thumbnail) } } private fun ensureCellCount(count: Int) { - if (binding.rowLayout.childCount == count) { + if (cells.size == count) { return } binding.rowLayout.removeAllViews() - repeat(count) { binding.rowLayout.addView(createCell()) } - } - - private fun createCell(): FrameLayout { - val selectionBackground = View(context).apply { - visibility = View.GONE - layoutParams = FrameLayout.LayoutParams(0, 0) - viewThemeUtils.platform.colorViewBackground(this, ColorRole.SURFACE_CONTAINER_HIGHEST) - } - - val shimmer = LoaderImageView(context).apply { - setImageResource(R.drawable.background) - resetLoader() - layoutParams = FrameLayout.LayoutParams(0, 0) - } - - val thumbnail = ImageView(context).apply { - scaleType = ImageView.ScaleType.CENTER_CROP - layoutParams = FrameLayout.LayoutParams(0, 0) - } - - val checkbox = ImageView(context).apply { - visibility = View.GONE - layoutParams = FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - FrameLayout.LayoutParams.WRAP_CONTENT - ).apply { - gravity = Gravity.TOP or Gravity.START - marginStart = checkBoxMargin - topMargin = checkBoxMargin - } - } - - return FrameLayout(context).apply { - addView(selectionBackground) - addView(shimmer) - addView(thumbnail) - addView(GalleryUnsupportedCellBinding.inflate(LayoutInflater.from(context), this, false).root) - addView(checkbox) + cells.clear() + repeat(count) { + val cell = createCell() + cells.add(cell) + binding.rowLayout.addView(cell.root) } } - private fun bindCell(index: Int, file: OCFile, size: GalleryCellSize, isLast: Boolean) { - val frameLayout = binding.rowLayout[index] as FrameLayout - val selectionBackground = frameLayout[SELECTION_BACKGROUND_INDEX] - val shimmer = frameLayout[SHIMMER_INDEX] as LoaderImageView - val thumbnail = frameLayout[THUMBNAIL_INDEX] as ImageView - val unsupported = GalleryUnsupportedCellBinding.bind(frameLayout[UNSUPPORTED_INDEX]) - val checkbox = frameLayout[CHECKBOX_INDEX] as ImageView + private fun createCell(): GalleryCellBinding = + GalleryCellBinding.inflate(LayoutInflater.from(context), binding.rowLayout, false).apply { + viewThemeUtils.platform.colorViewBackground(selectionBackground, ColorRole.SURFACE_CONTAINER_HIGHEST) + } + private fun bindCell(cell: GalleryCellBinding, file: OCFile, size: GalleryCellSize, isLast: Boolean) = with(cell) { val endMargin = if (isLast) zero else smallMargin applyCellSize(shimmer, size, endMargin = zero, bottomMargin = zero) applyCellSize(thumbnail, size, endMargin = endMargin, bottomMargin = smallMargin) @@ -155,11 +110,21 @@ class GalleryRowHolder( applySelection(unsupported.root, isChecked, isSameFileRebound) applyCheckBox(checkbox, isChecked) - ocFileListDelegate.bindGalleryRow(shimmer, thumbnail, unsupported, file, this) + ocFileListDelegate.bindGalleryRow( + shimmer, + thumbnail, + unsupported, + file, + this@GalleryRowHolder, + placeholderInset(size) + ) } + private fun placeholderInset(size: GalleryCellSize): Int = + (minOf(size.width, size.height) * PLACEHOLDER_ICON_INSET_RATIO).toInt() + private fun applyCellSize(view: View, size: GalleryCellSize, endMargin: Int, bottomMargin: Int) { - val params = view.layoutParams as FrameLayout.LayoutParams + val params = view.layoutParams as ViewGroup.MarginLayoutParams val unchanged = params.width == size.width && params.height == size.height && @@ -209,7 +174,4 @@ class GalleryRowHolder( imageView.background = if (isChecked) checkedBackground else null } } - - private fun thumbnailAt(index: Int): ImageView = - (binding.rowLayout[index] as FrameLayout)[THUMBNAIL_INDEX] as ImageView } 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 a3f5f9a51f8b..81803f826993 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 @@ -12,6 +12,7 @@ import android.view.View import android.widget.ImageView import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat +import androidx.core.view.isVisible import com.elyeproj.loaderviewlibrary.LoaderImageView import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.User @@ -21,7 +22,7 @@ import com.nextcloud.client.jobs.gallery.GalleryImageGenerationListener import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.utils.OCFileUtils import com.nextcloud.utils.extensions.makeRounded -import com.nextcloud.utils.extensions.setMediaLoading +import com.nextcloud.utils.extensions.setMediaPlaceholder import com.nextcloud.utils.extensions.setVisibleIf import com.nextcloud.utils.extensions.showsMediaThumbnailOf import com.nextcloud.utils.extensions.stopShimmer @@ -109,7 +110,8 @@ class OCFileListDelegate( imageView: ImageView, unsupported: GalleryUnsupportedCellBinding, file: OCFile, - galleryRowHolder: GalleryRowHolder + galleryRowHolder: GalleryRowHolder, + placeholderInset: Int ) { bindGalleryRowListeners(imageView, file, galleryRowHolder) @@ -120,13 +122,18 @@ class OCFileListDelegate( return } + val showsUnsupportedOfFile = unsupported.root.isVisible && imageView.tag == file.fileId + if (showsUnsupportedOfFile && !file.isUpdateThumbnailNeeded) { + return + } + GalleryImageGenerationJob.cancelPreviousJob(imageView) imageView.tag = file.fileId ViewCompat.setTransitionName(imageView, NavigationAnimator.sharedElementName(file)) unsupported.root.setVisibleIf(false) - imageView.setMediaLoading(shimmer) + imageView.setMediaPlaceholder(file, placeholderInset) val job = ioScope.launch(start = CoroutineStart.LAZY) { try { @@ -141,7 +148,7 @@ class OCFileListDelegate( } override fun onError() { - if (imageView.tag != file.fileId || file.isPreviewAvailable) { + if (imageView.tag != file.fileId) { return } diff --git a/app/src/main/res/layout/gallery_cell.xml b/app/src/main/res/layout/gallery_cell.xml new file mode 100644 index 000000000000..aca3ae5d665c --- /dev/null +++ b/app/src/main/res/layout/gallery_cell.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + From 62e3d96850c6b71057b023347f6bbb6f66406de4 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 9 Oct 2026 13:09:33 +0200 Subject: [PATCH 3/3] move view logic to the xmls Signed-off-by: alperozturk96 --- .../android/ui/adapter/GalleryAdapter.kt | 2 +- .../android/ui/adapter/GalleryRowHolder.kt | 70 ++++++------------- .../res/animator/gallery_cell_selection.xml | 37 ++++++++++ .../res/drawable/gallery_cell_spacing.xml | 12 ++++ .../gallery_selection_checkbox_background.xml | 10 +++ app/src/main/res/layout/gallery_cell.xml | 18 ++--- app/src/main/res/layout/gallery_row.xml | 5 +- .../res/layout/gallery_unsupported_cell.xml | 1 + app/src/main/res/values/dims.xml | 4 +- 9 files changed, 98 insertions(+), 61 deletions(-) create mode 100644 app/src/main/res/animator/gallery_cell_selection.xml create mode 100644 app/src/main/res/drawable/gallery_cell_spacing.xml create mode 100644 app/src/main/res/drawable/gallery_selection_checkbox_background.xml diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt index ee06009657f0..3aa9d143bb1c 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryAdapter.kt @@ -350,7 +350,7 @@ class GalleryAdapter( fun rowLayout(): GalleryRowLayout = GalleryRowLayout( columns, context.resources.displayMetrics.widthPixels, - context.resources.getInteger(R.integer.small_margin), + context.resources.getDimensionPixelSize(R.dimen.gallery_cell_spacing), defaultThumbnailSize ) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt index ec6878893642..83cbaebfa148 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt @@ -10,10 +10,10 @@ package com.owncloud.android.ui.adapter import android.view.LayoutInflater import android.view.View -import android.view.ViewGroup import android.view.ViewOutlineProvider import android.widget.ImageView import androidx.core.content.ContextCompat +import androidx.core.view.updateLayoutParams import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.utils.extensions.createRoundedOutline @@ -26,10 +26,7 @@ import com.owncloud.android.datamodel.GalleryRow import com.owncloud.android.datamodel.OCFile import com.owncloud.android.utils.theme.ViewThemeUtils -private const val CHECKED_SCALE = 0.8f private const val PLACEHOLDER_ICON_INSET_RATIO = 0.32f -private const val UNCHECKED_SCALE = 1.0f -private const val SELECTION_ANIMATION_DURATION_MS = 150L class GalleryRowHolder( val binding: GalleryRowBinding, @@ -41,9 +38,6 @@ class GalleryRowHolder( private val cells = mutableListOf() - private val zero by lazy { context.resources.getInteger(R.integer.zero) } - private val smallMargin by lazy { context.resources.getInteger(R.integer.small_margin) } - private val selectedOutline by lazy { val resources = context.resources val radiusDp = resources.getDimension(R.dimen.activity_icon_radius) / resources.displayMetrics.density @@ -56,10 +50,6 @@ class GalleryRowHolder( } } - private val checkedBackground by lazy { - ContextCompat.getDrawable(context, R.drawable.gallery_selection_checked_background) - } - private val uncheckedDrawable by lazy { ContextCompat.getDrawable(context, R.drawable.gallery_selection_unchecked) } @@ -69,7 +59,7 @@ class GalleryRowHolder( row.files.forEachIndexed { index, file -> val size = row.cellSizes.getOrNull(index) ?: return@forEachIndexed - bindCell(cells[index], file, size, isLast = index == row.files.lastIndex) + bindCell(cells[index], file, size) } } @@ -96,18 +86,12 @@ class GalleryRowHolder( viewThemeUtils.platform.colorViewBackground(selectionBackground, ColorRole.SURFACE_CONTAINER_HIGHEST) } - private fun bindCell(cell: GalleryCellBinding, file: OCFile, size: GalleryCellSize, isLast: Boolean) = with(cell) { - val endMargin = if (isLast) zero else smallMargin - applyCellSize(shimmer, size, endMargin = zero, bottomMargin = zero) - applyCellSize(thumbnail, size, endMargin = endMargin, bottomMargin = smallMargin) - applyCellSize(unsupported.root, size, endMargin = endMargin, bottomMargin = smallMargin) - applyCellSize(selectionBackground, size, endMargin = endMargin, bottomMargin = smallMargin) + private fun bindCell(cell: GalleryCellBinding, file: OCFile, size: GalleryCellSize) = with(cell) { + root.applySize(size) val isChecked = ocFileListDelegate.isCheckedFile(file) val isSameFileRebound = thumbnail.tag == file.fileId - selectionBackground.setVisibleIf(isChecked) - applySelection(thumbnail, isChecked, isSameFileRebound) - applySelection(unsupported.root, isChecked, isSameFileRebound) + applySelection(cell, isChecked, isSameFileRebound) applyCheckBox(checkbox, isChecked) ocFileListDelegate.bindGalleryRow( @@ -123,42 +107,31 @@ class GalleryRowHolder( private fun placeholderInset(size: GalleryCellSize): Int = (minOf(size.width, size.height) * PLACEHOLDER_ICON_INSET_RATIO).toInt() - private fun applyCellSize(view: View, size: GalleryCellSize, endMargin: Int, bottomMargin: Int) { - val params = view.layoutParams as ViewGroup.MarginLayoutParams - - val unchanged = params.width == size.width && - params.height == size.height && - params.rightMargin == endMargin && - params.bottomMargin == bottomMargin - - if (unchanged) { + private fun View.applySize(size: GalleryCellSize) { + if (layoutParams.width == size.width && layoutParams.height == size.height) { return } - params.width = size.width - params.height = size.height - params.setMargins(0, 0, endMargin, bottomMargin) - view.layoutParams = params + updateLayoutParams { + width = size.width + height = size.height + } } - private fun applySelection(view: View, isChecked: Boolean, isSameFileRebound: Boolean) { - view.outlineProvider = if (isChecked) selectedOutline else ViewOutlineProvider.BACKGROUND - view.clipToOutline = isChecked - - val scale = if (isChecked) CHECKED_SCALE else UNCHECKED_SCALE - view.animate().cancel() + private fun applySelection(cell: GalleryCellBinding, isChecked: Boolean, isSameFileRebound: Boolean) { + cell.selectionBackground.setVisibleIf(isChecked) + cell.thumbnail.applySelectedOutline(isChecked) + cell.unsupported.root.applySelectedOutline(isChecked) + cell.root.isSelected = isChecked if (!isSameFileRebound) { - view.scaleX = scale - view.scaleY = scale - return + cell.root.jumpDrawablesToCurrentState() } + } - view.animate() - .scaleX(scale) - .scaleY(scale) - .setDuration(SELECTION_ANIMATION_DURATION_MS) - .start() + private fun View.applySelectedOutline(isChecked: Boolean) { + outlineProvider = if (isChecked) selectedOutline else ViewOutlineProvider.BACKGROUND + clipToOutline = isChecked } private fun applyCheckBox(imageView: ImageView, isChecked: Boolean) { @@ -171,7 +144,6 @@ class GalleryRowHolder( val checkboxDrawable = if (isChecked) checkedDrawable else uncheckedDrawable if (imageView.drawable !== checkboxDrawable) { imageView.setImageDrawable(checkboxDrawable) - imageView.background = if (isChecked) checkedBackground else null } } } diff --git a/app/src/main/res/animator/gallery_cell_selection.xml b/app/src/main/res/animator/gallery_cell_selection.xml new file mode 100644 index 000000000000..77c1d35f8be7 --- /dev/null +++ b/app/src/main/res/animator/gallery_cell_selection.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/gallery_cell_spacing.xml b/app/src/main/res/drawable/gallery_cell_spacing.xml new file mode 100644 index 000000000000..11a6b7dfcd85 --- /dev/null +++ b/app/src/main/res/drawable/gallery_cell_spacing.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/main/res/drawable/gallery_selection_checkbox_background.xml b/app/src/main/res/drawable/gallery_selection_checkbox_background.xml new file mode 100644 index 000000000000..9af2aa5bc29d --- /dev/null +++ b/app/src/main/res/drawable/gallery_selection_checkbox_background.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/layout/gallery_cell.xml b/app/src/main/res/layout/gallery_cell.xml index aca3ae5d665c..200f64c06ed1 100644 --- a/app/src/main/res/layout/gallery_cell.xml +++ b/app/src/main/res/layout/gallery_cell.xml @@ -2,7 +2,7 @@ + android:scaleType="centerCrop" + android:stateListAnimator="@animator/gallery_cell_selection" /> + android:divider="@drawable/gallery_cell_spacing" + android:orientation="horizontal" + android:paddingBottom="@dimen/gallery_cell_spacing" + android:showDividers="middle" /> diff --git a/app/src/main/res/layout/gallery_unsupported_cell.xml b/app/src/main/res/layout/gallery_unsupported_cell.xml index 58bc6abfff0c..d0304b72e510 100644 --- a/app/src/main/res/layout/gallery_unsupported_cell.xml +++ b/app/src/main/res/layout/gallery_unsupported_cell.xml @@ -13,6 +13,7 @@ android:gravity="center" android:orientation="vertical" android:padding="@dimen/standard_half_padding" + android:stateListAnimator="@animator/gallery_cell_selection" android:visibility="gone"> 24dp 180dp 20sp - 5 - 0 + 2dp + 150 16sp 108dp