Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import android.util.Size
import android.view.WindowManager
import android.widget.ImageView
import com.nextcloud.client.account.User
import com.nextcloud.utils.extensions.getBigThumbnail
import com.nextcloud.utils.extensions.getBigThumbnailKey
import com.nextcloud.utils.extensions.getSmallThumbnail
import com.nextcloud.utils.extensions.isPNG
Expand Down Expand Up @@ -111,7 +110,7 @@ class GalleryImageGenerationJob(private val user: User, private val storageManag
}

private suspend fun getBitmap(file: OCFile): Bitmap? = withContext(Dispatchers.IO) {
val cached = file.getBigThumbnail()
val cached = ThumbnailsCacheManager.getBitmapFromDiskCacheWithoutLock(file.getBigThumbnailKey())
if (cached != null && !file.isUpdateThumbnailNeeded) {
return@withContext cached
}
Expand Down Expand Up @@ -200,7 +199,7 @@ class GalleryImageGenerationJob(private val user: User, private val storageManag
) = withContext(Dispatchers.Main) {
val tagId = file.fileId.toString()

if (imageView.tag.toString() == tagId && imageView.isAttachedToWindow) {
if (imageView.tag.toString() == tagId) {
imageView.setMediaThumbnail(file, bitmap)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,15 @@ public static Bitmap getScaledBitmapFromDiskCache(String key, int width, int hei
return null;
}

public static Bitmap getBitmapFromDiskCacheWithoutLock(String key) {
DiskLruImageCache cache = mThumbnailCache;
if (cache == null) {
return null;
}

return cache.getBitmap(key);
}

public static Bitmap getBitmapFromDiskCache(String key) {
synchronized (mThumbnailsDiskCacheLock) {
// Wait while disk cache is started from background thread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ 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.extensions.getBigThumbnail
import com.nextcloud.utils.extensions.makeRounded
import com.nextcloud.utils.extensions.setMediaPlaceholder
import com.nextcloud.utils.extensions.setMediaThumbnail
import com.nextcloud.utils.extensions.setVisibleIf
import com.nextcloud.utils.extensions.showsMediaThumbnailOf
import com.nextcloud.utils.extensions.stopShimmer
Expand Down Expand Up @@ -121,13 +119,6 @@ class OCFileListDelegate(

imageView.tag = file.fileId

val cached = file.takeUnless { it.isUpdateThumbnailNeeded }?.getBigThumbnail()
if (cached != null) {
imageView.setMediaThumbnail(file, cached)
imageView.stopShimmer(shimmer)
return
}

imageView.setMediaPlaceholder(file, placeholderInset)

val job = ioScope.launch(start = CoroutineStart.LAZY) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
Expand Down Expand Up @@ -55,12 +57,13 @@ import com.owncloud.android.ui.asynctasks.GallerySearchTask
import com.owncloud.android.ui.events.ChangeMenuEvent
import com.owncloud.android.ui.fragment.GalleryFragmentBottomSheetDialog.MediaState
import com.owncloud.android.ui.fragment.helper.ColumnCount
import com.owncloud.android.ui.fragment.helper.GalleryPinchListener
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

@Suppress("ForbiddenComment", "ReturnCount", "MagicNumber", "MaxLineLength")
@Suppress("ForbiddenComment", "ReturnCount", "MagicNumber", "MaxLineLength", "TooManyFunctions")
class GalleryFragment :
OCFileListFragment(),
GalleryFragmentBottomSheetActions {
Expand All @@ -70,6 +73,7 @@ class GalleryFragment :
private val limit = 150
private var loadedItemCount = INITIAL_GALLERY_WINDOW
private var restoreScrollPending = false
private var scrollAnchorFile: OCFile? = null
private var adapter: GalleryAdapter? = null

private var bottomSheet: GalleryFragmentBottomSheetDialog? = null
Expand Down Expand Up @@ -98,7 +102,7 @@ class GalleryFragment :
isFromAlbum = it.getBoolean(AlbumsPickerActivity.EXTRA_FROM_ALBUM, false)
}
bottomSheet = GalleryFragmentBottomSheetDialog()
columnsCount = ColumnCount.Wide.get(resources.isLandscape())
columnsCount = defaultColumnsCount(resources.isLandscape())
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -182,6 +186,8 @@ class GalleryFragment :
}
})

setupPinchToChangeColumns()

Log_OC.i(this, "onCreateView() in GalleryFragment end")
return v
}
Expand Down Expand Up @@ -240,7 +246,7 @@ class GalleryFragment :

override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
columnsCount = ColumnCount.Wide.get(newConfig.isLandscape())
columnsCount = defaultColumnsCount(newConfig.isLandscape())
adapter?.changeColumn(columnsCount)
showAllGalleryItems()
}
Expand Down Expand Up @@ -459,6 +465,7 @@ class GalleryFragment :
}
adapter?.updateList(galleryItems)
updateSubtitle(mediaState)
scrollToAnchorFile()

if (restoreScrollPending && galleryItems.isNotEmpty()) {
restoreScrollPending = false
Expand Down Expand Up @@ -512,12 +519,66 @@ class GalleryFragment :

override fun setGridViewColumns(scaleFactor: Float) = Unit

private fun setupPinchToChangeColumns() {
val detector = ScaleGestureDetector(requireContext(), GalleryPinchListener(::changeColumnsBy))
recyclerView?.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
detector.onTouchEvent(e)
return detector.isInProgress
}

override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
detector.onTouchEvent(e)
}
})
}

private fun defaultColumnsCount(isLandscape: Boolean): Int =
if (isLandscape) ColumnCount.Wide.landscape else ColumnCount.Normal.portrait

private fun changeColumnsBy(delta: Int) {
val maxColumns = ColumnCount.Wide.get(resources.isLandscape())
val updated = (columnsCount + delta).coerceIn(MIN_COLUMNS, maxColumns)
if (updated == columnsCount) {
return
}

columnsCount = updated
adapter?.changeColumn(columnsCount)
scrollAnchorFile = scrollAnchorFile ?: firstVisibleFile()
showAllGalleryItems()
}

private fun firstVisibleFile(): OCFile? {
val layoutManager = recyclerView?.layoutManager as? GridLayoutManager ?: return null
val first = layoutManager.findFirstVisibleItemPosition()
val last = layoutManager.findLastVisibleItemPosition()
if (first == RecyclerView.NO_POSITION) {
return null
}

return (first..last).firstNotNullOfOrNull { adapter?.getItem(it) }
}

private fun scrollToAnchorFile() {
val file = scrollAnchorFile ?: return
scrollAnchorFile = null

val position = adapter?.getItemPosition(file) ?: return
if (position < 0) {
return
}

(recyclerView?.layoutManager as? GridLayoutManager)?.scrollToPositionWithOffset(position, 0)
}

fun markAsFavorite(remotePath: String, favorite: Boolean) {
adapter?.markAsFavorite(remotePath, favorite)
}

companion object {
private const val MAX_ITEMS_PER_ROW = 10
private const val MIN_COLUMNS = 1
private const val FRAGMENT_TAG_BOTTOM_SHEET = "data"
private const val ITEM_VIEW_CACHE_SIZE = 8
private const val PAGINATION_LOADER_DELAY_IN_MS = 500L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ package com.owncloud.android.ui.fragment.helper

private const val WIDE_LANDSCAPE_COLUMNS = 5
private const val NORMAL_LANDSCAPE_COLUMNS = 4
private const val PORTRAIT_COLUMNS = 2
private const val WIDE_PORTRAIT_COLUMNS = 3
private const val NORMAL_PORTRAIT_COLUMNS = 2

enum class ColumnCount(val landscape: Int, val portrait: Int) {
Wide(WIDE_LANDSCAPE_COLUMNS, PORTRAIT_COLUMNS),
Normal(NORMAL_LANDSCAPE_COLUMNS, PORTRAIT_COLUMNS);
Wide(WIDE_LANDSCAPE_COLUMNS, WIDE_PORTRAIT_COLUMNS),
Normal(NORMAL_LANDSCAPE_COLUMNS, NORMAL_PORTRAIT_COLUMNS);

fun get(isLandscape: Boolean): Int = if (isLandscape) landscape else portrait
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.fragment.helper

import android.view.ScaleGestureDetector

private const val ZOOM_IN_THRESHOLD = 1.25f
private const val ZOOM_OUT_THRESHOLD = 0.8f
private const val NEUTRAL_SCALE = 1f

class GalleryPinchListener(private val onColumnStep: (Int) -> Unit) :
ScaleGestureDetector.SimpleOnScaleGestureListener() {

private var accumulatedScale = NEUTRAL_SCALE

override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
accumulatedScale = NEUTRAL_SCALE
return true
}

override fun onScale(detector: ScaleGestureDetector): Boolean {
accumulatedScale *= detector.scaleFactor

if (accumulatedScale >= ZOOM_IN_THRESHOLD) {
accumulatedScale = NEUTRAL_SCALE
onColumnStep(-1)
} else if (accumulatedScale <= ZOOM_OUT_THRESHOLD) {
accumulatedScale = NEUTRAL_SCALE
onColumnStep(1)
}

return true
}
}
Loading