From b0e656f5638a287dac0e4bfefb4bb21a5ea341b1 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Dec 2025 14:11:18 +0100 Subject: [PATCH 1/6] fix: gallery image scaling Signed-off-by: alperozturk --- .../java/com/nextcloud/utils/OCFileUtils.kt | 91 ++++++++---- .../android/ui/adapter/GalleryRowHolder.kt | 133 +++++++----------- 2 files changed, 113 insertions(+), 111 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt index 142c6edfa009..98160b8af1c9 100644 --- a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt @@ -22,48 +22,81 @@ import com.owncloud.android.utils.MimeTypeUtil object OCFileUtils { private const val TAG = "OCFileUtils" - @Suppress("ReturnCount", "NestedBlockDepth") fun getImageSize(ocFile: OCFile, defaultThumbnailSize: Float): Pair { + val fallback = defaultThumbnailSize.toInt().coerceAtLeast(1) + val fallbackPair = fallback to fallback + try { Log_OC.d(TAG, "Getting image size for: ${ocFile.fileName}") - val widthFromDimension = ocFile.imageDimension?.width - val heightFromDimension = ocFile.imageDimension?.height - if (widthFromDimension != null && heightFromDimension != null) { - val width = widthFromDimension.toInt() - val height = heightFromDimension.toInt() - Log_OC.d(TAG, "Image dimensions are used, width: $width, height: $height") + // Use server-provided imageDimension if available + ocFile.imageDimension?.let { dim -> + val width = dim.width.toInt().coerceAtLeast(1) + val height = dim.height.toInt().coerceAtLeast(1) + Log_OC.d(TAG, "Using server-provided imageDimension: $width x $height") return width to height } - return if (ocFile.exists()) { - val exif = ExifInterface(ocFile.storagePath) - val width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) - val height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) + // Fallback to local file if it exists + val path = ocFile.storagePath + if (!path.isNullOrEmpty() && ocFile.exists()) { - if (width > 0 && height > 0) { - Log_OC.d(TAG, "Exif used width: $width and height: $height") - width to height + // Try EXIF first + val exifSize = getExifSize(path) + if (exifSize != null) { + Log_OC.d(TAG, "EXIF size used: ${exifSize.first} x ${exifSize.second}") + return exifSize } - val (bitmapWidth, bitmapHeight) = BitmapUtils.getImageResolution(ocFile.storagePath) - .let { it[0] to it[1] } - - if (bitmapWidth > 0 && bitmapHeight > 0) { - Log_OC.d(TAG, "BitmapUtils.getImageResolution used width: $bitmapWidth and height: $bitmapHeight") - bitmapWidth to bitmapHeight + // Then try BitmapUtils + val bitmapSize = getBitmapSize(path) + if (bitmapSize != null) { + Log_OC.d(TAG, "BitmapUtils resolution used: ${bitmapSize.first} x ${bitmapSize.second}") + return bitmapSize } + } + + // Fallback to defaultThumbnailSize + Log_OC.d(TAG, "All sources failed, using default size: $fallback x $fallback") + return fallbackPair - val fallback = defaultThumbnailSize.toInt().coerceAtLeast(1) - Log_OC.d(TAG, "Default size used width: $fallback and height: $fallback") - fallback to fallback - } else { - Log_OC.d(TAG, "Default size is used: $defaultThumbnailSize") - val size = defaultThumbnailSize.toInt().coerceAtLeast(1) - size to size + } catch (e: Exception) { + Log_OC.e(TAG, "Error getting image size for ${ocFile.fileName}", e) + } + + return fallbackPair + } + + private fun getExifSize(path: String): Pair? { + return try { + val exif = ExifInterface(path) + var width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) + var height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) + + val orientation = exif.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL + ) + if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || + orientation == ExifInterface.ORIENTATION_ROTATE_270 + ) { + val tmp = width + width = height + height = tmp } - } finally { - Log_OC.d(TAG, "-----------------------------") + + if (width > 0 && height > 0) width to height else null + } catch (_: Exception) { + null + } + } + + private fun getBitmapSize(path: String): Pair? { + return try { + val (w, h) = BitmapUtils.getImageResolution(path).takeIf { it.size == 2 } ?: return null + if (w > 0 && h > 0) w to h else null + } catch (_: Exception) { + null } } 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 877ed965f7e0..84b7a94844f5 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,7 +10,6 @@ package com.owncloud.android.ui.adapter import android.view.Gravity import android.view.View -import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView import androidx.core.content.ContextCompat @@ -27,8 +26,6 @@ import com.owncloud.android.databinding.GalleryRowBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.GalleryRow import com.owncloud.android.datamodel.OCFile -import com.owncloud.android.lib.resources.files.model.ImageDimension -import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils @Suppress("LongParameterList") @@ -72,16 +69,15 @@ class GalleryRowHolder( if (lastFileCount != requiredCount) { binding.rowLayout.removeAllViews() for (file in row.files) { - val rowLayout = getRowLayout(file) - binding.rowLayout.addView(rowLayout) + binding.rowLayout.addView(getRowLayout(file)) } lastFileCount = requiredCount } - val shrinkRatio = computeShrinkRatio(row) + val dimensions = getDimensions(row) for (i in row.files.indices) { - adjustFile(i, row.files[i], shrinkRatio, row) + adjustFile(i, row.files[i], dimensions[i], row) } } @@ -115,7 +111,7 @@ class GalleryRowHolder( val rowCellImageView = ImageView(context).apply { setImageDrawable(drawable) adjustViewBounds = true - scaleType = ImageView.ScaleType.FIT_XY + scaleType = ImageView.ScaleType.CENTER_CROP layoutParams = FrameLayout.LayoutParams(width, height) } @@ -126,101 +122,74 @@ class GalleryRowHolder( } } - @SuppressWarnings("MagicNumber") - private fun computeShrinkRatio(row: GalleryRow): Float { - val screenWidth = DisplayUtils.convertDpToPixel( - context.resources.configuration.screenWidthDp.toFloat(), - context - ).toFloat() - - return if (row.files.size > 1) { - computeMultiFileShrinkRatio(row, screenWidth) - } else { - computeSingleFileShrinkRatio(row, screenWidth) - } - } + private fun adjustFile( + index: Int, + file: OCFile, + dims: Pair, + row: GalleryRow + ) { + val (width, height) = dims - private fun computeMultiFileShrinkRatio(row: GalleryRow, screenWidth: Float): Float { - val targetHeight = row.getMaxHeight() - var totalUnscaledWidth = 0f + val frameLayout = binding.rowLayout[index] as FrameLayout + val shimmer = frameLayout[0] as LoaderImageView + val thumbnail = frameLayout[1] as ImageView + val checkbox = frameLayout[2] as ImageView - for (file in row.files) { - val (originalWidth, originalHeight) = OCFileUtils.getImageSize(file, defaultThumbnailSize) + val isChecked = ocFileListDelegate.isCheckedFile(file) - val scaledWidth = targetHeight * (originalWidth.toFloat() / originalHeight) - file.imageDimension = ImageDimension(scaledWidth, targetHeight) + adjustRowCell(thumbnail, isChecked) + adjustCheckBox(checkbox, isChecked) - totalUnscaledWidth += scaledWidth + ocFileListDelegate.bindGalleryRow(shimmer, thumbnail, file, this, dims) + + val endMargin = if (index < row.files.size - 1) smallMargin else zero + + thumbnail.layoutParams = FrameLayout.LayoutParams(width, height).apply { + setMargins(0, 0, endMargin, smallMargin) } + shimmer.layoutParams = FrameLayout.LayoutParams(width, height) - val totalAvailableWidth = screenWidth - ((row.files.size - 1) * smallMargin) - return totalAvailableWidth / totalUnscaledWidth + frameLayout.requestLayout() } - private fun computeSingleFileShrinkRatio(row: GalleryRow, screenWidth: Float): Float { - val width = OCFileUtils.getImageSize(row.files[0], defaultThumbnailSize).first - return (screenWidth / galleryAdapter.columns) / width - } + private fun getDimensions(row: GalleryRow): List> { + val screenWidthPx = context.resources.displayMetrics.widthPixels.toFloat() + val marginPx = smallMargin.toFloat() - private fun adjustFile(index: Int, file: OCFile, shrinkRatio: Float, row: GalleryRow) { - val width = file.imageDimension?.width?.times(shrinkRatio)?.toInt() ?: 0 - val height = file.imageDimension?.height?.times(shrinkRatio)?.toInt() ?: 0 + val aspectRatios = row.files.map { file -> + val (w, h) = OCFileUtils.getImageSize(file, defaultThumbnailSize) + if (w > 0 && h > 0) w.toFloat() / h.toFloat() else 1f + } - val frameLayout = binding.rowLayout[index] as FrameLayout - val shimmer = frameLayout[0] as LoaderImageView - val thumbnail = frameLayout[1] as ImageView - val checkBoxImageView = frameLayout[2] as ImageView + // Start with max height based on largest image + val targetHeight = row.getMaxHeight().coerceAtLeast(1f) - val isChecked = ocFileListDelegate.isCheckedFile(file) + val unscaledWidths = aspectRatios.map { ar -> targetHeight * ar } - adjustRowCell(thumbnail, isChecked) - adjustCheckBox(checkBoxImageView, isChecked) - - ocFileListDelegate.bindGalleryRow( - shimmer, - thumbnail, - file, - this, - width to height - ) - - // Update layout params only if they differ - val thumbLp = thumbnail.layoutParams - if (thumbLp.width != width || thumbLp.height != height) { - thumbnail.layoutParams = thumbLp.getFrameLayout(width, height).apply { - val endMargin = if (index < row.files.size - 1) smallMargin else zero - this.setMargins(zero, zero, endMargin, smallMargin) - } - } + // margins + val totalUnscaled = unscaledWidths.sum() + val totalMargins = marginPx * (row.files.size - 1) + val available = screenWidthPx - totalMargins - val shimmerLp = shimmer.layoutParams - if (shimmerLp.width != width || shimmerLp.height != height) { - shimmer.layoutParams = shimmerLp.getFrameLayout(width, height) - } + // calculate shrink ratio + val shrink = available / totalUnscaled - // Force layout update - frameLayout.requestLayout() - } + // prepare scaled dimensions + val dims = unscaledWidths.map { w -> + val finalW = (w * shrink).toInt() + val finalH = (targetHeight * shrink).toInt() + finalW to finalH + } - private fun ViewGroup.LayoutParams?.getFrameLayout(width: Int, height: Int): FrameLayout.LayoutParams = ( - this as? FrameLayout.LayoutParams - ?: FrameLayout.LayoutParams(width, height) - ).apply { - this.width = width - this.height = height + return dims } @Suppress("MagicNumber") private fun adjustRowCell(imageView: ImageView, isChecked: Boolean) { val scale = if (isChecked) 0.8f else 1.0f val radius = if (isChecked) iconRadius else 0f - - // Only update if values changed - if (imageView.scaleX != scale) { - imageView.scaleX = scale - imageView.scaleY = scale - } - + imageView.scaleX = scale + imageView.scaleY = scale imageView.makeRounded(context, radius) } From 51b322f9ed959ac8c9fb8dbf3a3646e8eefdd828 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Dec 2025 14:12:09 +0100 Subject: [PATCH 2/6] fix: gallery image scaling Signed-off-by: alperozturk --- .../java/com/nextcloud/utils/OCFileUtils.kt | 41 +++++++++---------- .../android/ui/adapter/GalleryRowHolder.kt | 7 +--- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt index 98160b8af1c9..2ad15a7c5d2b 100644 --- a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt @@ -19,6 +19,7 @@ import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.utils.BitmapUtils import com.owncloud.android.utils.MimeTypeUtil +@Suppress("TooGenericExceptionCaught", "ReturnCount") object OCFileUtils { private const val TAG = "OCFileUtils" @@ -40,7 +41,6 @@ object OCFileUtils { // Fallback to local file if it exists val path = ocFile.storagePath if (!path.isNullOrEmpty() && ocFile.exists()) { - // Try EXIF first val exifSize = getExifSize(path) if (exifSize != null) { @@ -59,7 +59,6 @@ object OCFileUtils { // Fallback to defaultThumbnailSize Log_OC.d(TAG, "All sources failed, using default size: $fallback x $fallback") return fallbackPair - } catch (e: Exception) { Log_OC.e(TAG, "Error getting image size for ${ocFile.fileName}", e) } @@ -67,28 +66,26 @@ object OCFileUtils { return fallbackPair } - private fun getExifSize(path: String): Pair? { - return try { - val exif = ExifInterface(path) - var width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) - var height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) - - val orientation = exif.getAttributeInt( - ExifInterface.TAG_ORIENTATION, - ExifInterface.ORIENTATION_NORMAL - ) - if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || - orientation == ExifInterface.ORIENTATION_ROTATE_270 - ) { - val tmp = width - width = height - height = tmp - } + private fun getExifSize(path: String): Pair? = try { + val exif = ExifInterface(path) + var width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) + var height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) - if (width > 0 && height > 0) width to height else null - } catch (_: Exception) { - null + val orientation = exif.getAttributeInt( + ExifInterface.TAG_ORIENTATION, + ExifInterface.ORIENTATION_NORMAL + ) + if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || + orientation == ExifInterface.ORIENTATION_ROTATE_270 + ) { + val tmp = width + width = height + height = tmp } + + if (width > 0 && height > 0) width to height else null + } catch (_: Exception) { + null } private fun getBitmapSize(path: String): Pair? { 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 84b7a94844f5..aae51085d8ed 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 @@ -122,12 +122,7 @@ class GalleryRowHolder( } } - private fun adjustFile( - index: Int, - file: OCFile, - dims: Pair, - row: GalleryRow - ) { + private fun adjustFile(index: Int, file: OCFile, dims: Pair, row: GalleryRow) { val (width, height) = dims val frameLayout = binding.rowLayout[index] as FrameLayout From bd465ee07b4375f21f8dd4b3f72522face89e0d4 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Dec 2025 14:34:14 +0100 Subject: [PATCH 3/6] fix: gallery image scaling Signed-off-by: alperozturk --- .../java/com/nextcloud/utils/OCFileUtils.kt | 58 ++++++------- .../android/ui/adapter/GalleryRowHolder.kt | 83 +++++++++---------- 2 files changed, 62 insertions(+), 79 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt index 2ad15a7c5d2b..9cfe0f9f9e73 100644 --- a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt @@ -30,34 +30,23 @@ object OCFileUtils { try { Log_OC.d(TAG, "Getting image size for: ${ocFile.fileName}") - // Use server-provided imageDimension if available + // Server-provided ocFile.imageDimension?.let { dim -> - val width = dim.width.toInt().coerceAtLeast(1) - val height = dim.height.toInt().coerceAtLeast(1) - Log_OC.d(TAG, "Using server-provided imageDimension: $width x $height") - return width to height + val w = dim.width.toInt().coerceAtLeast(1) + val h = dim.height.toInt().coerceAtLeast(1) + Log_OC.d(TAG, "Using server-provided imageDimension: $w x $h") + return w to h } - // Fallback to local file if it exists + // Local file val path = ocFile.storagePath if (!path.isNullOrEmpty() && ocFile.exists()) { - // Try EXIF first - val exifSize = getExifSize(path) - if (exifSize != null) { - Log_OC.d(TAG, "EXIF size used: ${exifSize.first} x ${exifSize.second}") - return exifSize - } - - // Then try BitmapUtils - val bitmapSize = getBitmapSize(path) - if (bitmapSize != null) { - Log_OC.d(TAG, "BitmapUtils resolution used: ${bitmapSize.first} x ${bitmapSize.second}") - return bitmapSize - } + getExifSize(path)?.let { return it } + getBitmapSize(path)?.let { return it } } - // Fallback to defaultThumbnailSize - Log_OC.d(TAG, "All sources failed, using default size: $fallback x $fallback") + // 3 Fallback + Log_OC.d(TAG, "Fallback to default size: $fallback x $fallback") return fallbackPair } catch (e: Exception) { Log_OC.e(TAG, "Error getting image size for ${ocFile.fileName}", e) @@ -68,8 +57,8 @@ object OCFileUtils { private fun getExifSize(path: String): Pair? = try { val exif = ExifInterface(path) - var width = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) - var height = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) + var w = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) + var h = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) val orientation = exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, @@ -78,23 +67,24 @@ object OCFileUtils { if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270 ) { - val tmp = width - width = height - height = tmp + val tmp = w + w = h + h = tmp } - if (width > 0 && height > 0) width to height else null + if (w > 0 && h > 0) w to h else null } catch (_: Exception) { null } - private fun getBitmapSize(path: String): Pair? { - return try { - val (w, h) = BitmapUtils.getImageResolution(path).takeIf { it.size == 2 } ?: return null - if (w > 0 && h > 0) w to h else null - } catch (_: Exception) { - null - } + private fun getBitmapSize(path: String): Pair? = try { + val options = android.graphics.BitmapFactory.Options().apply { inJustDecodeBounds = true } + android.graphics.BitmapFactory.decodeFile(path, options) + val w = options.outWidth + val h = options.outHeight + if (w > 0 && h > 0) w to h else null + } catch (_: Exception) { + null } fun getMediaPlaceholder(file: OCFile, imageDimension: Pair): BitmapDrawable { 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 aae51085d8ed..01ed35d40aed 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 @@ -19,7 +19,6 @@ import com.elyeproj.loaderviewlibrary.LoaderImageView import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.utils.OCFileUtils import com.nextcloud.utils.extensions.makeRounded -import com.nextcloud.utils.extensions.mediaSize import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R import com.owncloud.android.databinding.GalleryRowBinding @@ -68,7 +67,7 @@ class GalleryRowHolder( // Only rebuild if file count changed if (lastFileCount != requiredCount) { binding.rowLayout.removeAllViews() - for (file in row.files) { + row.files.forEach { file -> binding.rowLayout.addView(getRowLayout(file)) } lastFileCount = requiredCount @@ -81,11 +80,11 @@ class GalleryRowHolder( } } - fun updateRowVisuals() { - bind(currentRow) - } + fun updateRowVisuals() = bind(currentRow) private fun getRowLayout(file: OCFile): FrameLayout { + val (width, height) = OCFileUtils.getImageSize(file, defaultThumbnailSize) + val checkbox = ImageView(context).apply { visibility = View.GONE layoutParams = FrameLayout.LayoutParams( @@ -98,16 +97,13 @@ class GalleryRowHolder( } } - val mediaSize = file.mediaSize(defaultThumbnailSize) - val (width, height) = mediaSize - val shimmer = LoaderImageView(context).apply { setImageResource(R.drawable.background) resetLoader() layoutParams = FrameLayout.LayoutParams(width, height) } - val drawable = OCFileUtils.getMediaPlaceholder(file, mediaSize) + val drawable = OCFileUtils.getMediaPlaceholder(file, width to height) val rowCellImageView = ImageView(context).apply { setImageDrawable(drawable) adjustViewBounds = true @@ -122,63 +118,60 @@ class GalleryRowHolder( } } + private fun getDimensions(row: GalleryRow): List> { + val screenWidthPx = context.resources.displayMetrics.widthPixels.toFloat() + val marginPx = smallMargin.toFloat() + val totalMargins = marginPx * (row.files.size - 1) + val availableWidth = screenWidthPx - totalMargins + + val aspectRatios = row.files.map { file -> + val (w, h) = OCFileUtils.getImageSize(file, defaultThumbnailSize) + if (h > 0) w.toFloat() / h else 1.0f + } + + val sumAspectRatios = aspectRatios.sum() + + // calculate row height based on aspect ratios + val rowHeightFloat = if (sumAspectRatios > 0) availableWidth / sumAspectRatios else defaultThumbnailSize + val finalHeight = rowHeightFloat.toInt() + + // for each aspect ratio calculate widths + val finalWidths = aspectRatios.map { ratio -> (rowHeightFloat * ratio).toInt() }.toMutableList() + val usedWidth = finalWidths.sum() + + // based on screen width get remaining pixels + val remainingPixels = (availableWidth - usedWidth).toInt() + + // add to remaining pixels to last image + if (remainingPixels > 0 && finalWidths.isNotEmpty()) { + val lastIndex = finalWidths.lastIndex + finalWidths[lastIndex] = finalWidths[lastIndex] + remainingPixels + } + + return finalWidths.map { w -> w to finalHeight } + } + private fun adjustFile(index: Int, file: OCFile, dims: Pair, row: GalleryRow) { val (width, height) = dims - val frameLayout = binding.rowLayout[index] as FrameLayout val shimmer = frameLayout[0] as LoaderImageView val thumbnail = frameLayout[1] as ImageView val checkbox = frameLayout[2] as ImageView val isChecked = ocFileListDelegate.isCheckedFile(file) - adjustRowCell(thumbnail, isChecked) adjustCheckBox(checkbox, isChecked) ocFileListDelegate.bindGalleryRow(shimmer, thumbnail, file, this, dims) val endMargin = if (index < row.files.size - 1) smallMargin else zero - thumbnail.layoutParams = FrameLayout.LayoutParams(width, height).apply { setMargins(0, 0, endMargin, smallMargin) } shimmer.layoutParams = FrameLayout.LayoutParams(width, height) - frameLayout.requestLayout() } - private fun getDimensions(row: GalleryRow): List> { - val screenWidthPx = context.resources.displayMetrics.widthPixels.toFloat() - val marginPx = smallMargin.toFloat() - - val aspectRatios = row.files.map { file -> - val (w, h) = OCFileUtils.getImageSize(file, defaultThumbnailSize) - if (w > 0 && h > 0) w.toFloat() / h.toFloat() else 1f - } - - // Start with max height based on largest image - val targetHeight = row.getMaxHeight().coerceAtLeast(1f) - - val unscaledWidths = aspectRatios.map { ar -> targetHeight * ar } - - // margins - val totalUnscaled = unscaledWidths.sum() - val totalMargins = marginPx * (row.files.size - 1) - val available = screenWidthPx - totalMargins - - // calculate shrink ratio - val shrink = available / totalUnscaled - - // prepare scaled dimensions - val dims = unscaledWidths.map { w -> - val finalW = (w * shrink).toInt() - val finalH = (targetHeight * shrink).toInt() - finalW to finalH - } - - return dims - } - @Suppress("MagicNumber") private fun adjustRowCell(imageView: ImageView, isChecked: Boolean) { val scale = if (isChecked) 0.8f else 1.0f From c6885451b71d8ac6302d36845e5970a7890ccd9b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Wed, 10 Dec 2025 14:34:26 +0100 Subject: [PATCH 4/6] fix: gallery image scaling Signed-off-by: alperozturk --- .../java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 01ed35d40aed..da4b72e9a11d 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 @@ -33,7 +33,7 @@ class GalleryRowHolder( private val defaultThumbnailSize: Float, private val ocFileListDelegate: OCFileListDelegate, val storageManager: FileDataStorageManager, - private val galleryAdapter: GalleryAdapter, + galleryAdapter: GalleryAdapter, private val viewThemeUtils: ViewThemeUtils ) : SectionedViewHolder(binding.root) { val context = galleryAdapter.context From d5223059a628ae3dca7279f0625ecc62d2ab2bd2 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Dec 2025 10:11:57 +0100 Subject: [PATCH 5/6] check nullability of the dim just in case Signed-off-by: alperozturk --- .../java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 da4b72e9a11d..fead84f34822 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 @@ -76,7 +76,8 @@ class GalleryRowHolder( val dimensions = getDimensions(row) for (i in row.files.indices) { - adjustFile(i, row.files[i], dimensions[i], row) + val dim = dimensions.getOrNull(i) ?: (defaultThumbnailSize.toInt() to defaultThumbnailSize.toInt()) + adjustFile(i, row.files[i], dim, row) } } From 7fe17f21f7f9fee8898a6a7af941a73760e02524 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Fri, 12 Dec 2025 10:17:39 +0100 Subject: [PATCH 6/6] add logs Signed-off-by: alperozturk --- app/src/main/java/com/nextcloud/utils/OCFileUtils.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt index 9cfe0f9f9e73..cdcc3506e018 100644 --- a/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt +++ b/app/src/main/java/com/nextcloud/utils/OCFileUtils.kt @@ -72,6 +72,7 @@ object OCFileUtils { h = tmp } + Log_OC.d(TAG, "Using exif imageDimension: $w x $h") if (w > 0 && h > 0) w to h else null } catch (_: Exception) { null @@ -82,6 +83,8 @@ object OCFileUtils { android.graphics.BitmapFactory.decodeFile(path, options) val w = options.outWidth val h = options.outHeight + + Log_OC.d(TAG, "Using bitmap factory imageDimension: $w x $h") if (w > 0 && h > 0) w to h else null } catch (_: Exception) { null