Skip to content
Merged
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 @@ -11,7 +11,6 @@ import android.graphics.Bitmap
import android.widget.ImageView
import androidx.core.content.ContextCompat
import com.nextcloud.client.account.User
import com.nextcloud.utils.OCFileUtils
import com.nextcloud.utils.allocationKilobyte
import com.nextcloud.utils.extensions.isPNG
import com.owncloud.android.MainApp
Expand All @@ -22,10 +21,13 @@ import com.owncloud.android.datamodel.ThumbnailsCacheManager
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.utils.MimeTypeUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
import java.util.WeakHashMap

class GalleryImageGenerationJob(private val user: User, private val storageManager: FileDataStorageManager) {
companion object {
Expand All @@ -36,26 +38,44 @@ class GalleryImageGenerationJob(private val user: User, private val storageManag
Runtime.getRuntime().availableProcessors() / 2
)
)
private val activeJobs = WeakHashMap<ImageView, Job>()

fun removeActiveJob(imageView: ImageView, job: CoroutineScope) {
if (isActiveJob(imageView, job)) {
removeJob(imageView)
}
}

fun isActiveJob(imageView: ImageView, job: CoroutineScope): Boolean = activeJobs[imageView] === job

fun storeJob(job: Job, imageView: ImageView) {
activeJobs[imageView] = job
}

fun cancelPreviousJob(imageView: ImageView) {
activeJobs[imageView]?.cancel()
removeJob(imageView)
}

fun removeJob(imageView: ImageView) {
activeJobs.remove(imageView)
}
}

@Suppress("TooGenericExceptionCaught")
suspend fun run(
file: OCFile,
imageView: ImageView,
imageDimension: Pair<Int, Int>,
listener: GalleryImageGenerationListener
) {
suspend fun run(file: OCFile, imageView: ImageView, listener: GalleryImageGenerationListener) {
try {
var newImage = false

if (file.remoteId == null && !file.isPreviewAvailable) {
Log_OC.e(TAG, "file has no remoteId and no preview")
withContext(Dispatchers.Main) {
listener.onError()
}
return
}

val bitmap: Bitmap? = getBitmap(imageView, file, imageDimension, onThumbnailGeneration = {
val bitmap: Bitmap? = getBitmap(file, onThumbnailGeneration = {
newImage = true
})

Expand All @@ -75,40 +95,25 @@ class GalleryImageGenerationJob(private val user: User, private val storageManag
}
}

private suspend fun getBitmap(
imageView: ImageView,
file: OCFile,
imageDimension: Pair<Int, Int>,
onThumbnailGeneration: () -> Unit
): Bitmap? = withContext(Dispatchers.IO) {
if (file.remoteId == null && !file.isPreviewAvailable) {
Log_OC.w(TAG, "file has no remoteId and no preview")
return@withContext null
}

val key = file.remoteId
val cachedThumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + file.remoteId
)
if (cachedThumbnail != null && !file.isUpdateThumbnailNeeded) {
Log_OC.d(TAG, "cached thumbnail is used for: ${file.fileName}")
return@withContext getThumbnailFromCache(file, cachedThumbnail, key)
}
private suspend fun getBitmap(file: OCFile, onThumbnailGeneration: () -> Unit): Bitmap? =
withContext(Dispatchers.IO) {
val key = file.remoteId
val cachedThumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(
ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + file.remoteId
)
if (cachedThumbnail != null && !file.isUpdateThumbnailNeeded) {
Log_OC.d(TAG, "cached thumbnail is used for: ${file.fileName}")
return@withContext getThumbnailFromCache(file, cachedThumbnail, key)
}

Log_OC.d(TAG, "generating new thumbnail for: ${file.fileName}")
Log_OC.d(TAG, "generating new thumbnail for: ${file.fileName}")

// only add placeholder if new thumbnail will be generated because cached image will appear so quickly
withContext(Dispatchers.Main) {
val placeholderDrawable = OCFileUtils.getMediaPlaceholder(file, imageDimension)
imageView.setImageDrawable(placeholderDrawable)
onThumbnailGeneration()
semaphore.withPermit {
return@withContext getThumbnailFromServerAndAddToCache(file, cachedThumbnail)
}
}

onThumbnailGeneration()
semaphore.withPermit {
return@withContext getThumbnailFromServerAndAddToCache(file, cachedThumbnail)
}
}

private suspend fun setThumbnail(
bitmap: Bitmap,
file: OCFile,
Expand All @@ -117,24 +122,25 @@ class GalleryImageGenerationJob(private val user: User, private val storageManag
listener: GalleryImageGenerationListener
) = withContext(Dispatchers.Main) {
val tagId = file.fileId.toString()
if (imageView.tag?.toString() != tagId) return@withContext

if (file.isPNG()) {
imageView.setBackgroundColor(
ContextCompat.getColor(
MainApp.getAppContext(),
R.color.bg_default
if (imageView.tag.toString() == tagId) {
if (file.isPNG()) {
imageView.setBackgroundColor(
ContextCompat.getColor(
MainApp.getAppContext(),
R.color.bg_default
)
)
)
}
}

if (newImage) {
listener.onNewGalleryImage()
}
if (newImage) {
listener.onNewGalleryImage()
}

if (imageView.isAttachedToWindow) {
imageView.setImageBitmap(bitmap)
imageView.invalidate()
if (imageView.isAttachedToWindow) {
imageView.setImageBitmap(bitmap)
imageView.invalidate()
}
}

listener.onSuccess()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.nextcloud.client.jobs.gallery.GalleryImageGenerationJob
import com.nextcloud.client.jobs.gallery.GalleryImageGenerationListener
import com.nextcloud.client.jobs.upload.FileUploadHelper
import com.nextcloud.client.preferences.AppPreferences
import com.nextcloud.utils.OCFileUtils
import com.nextcloud.utils.extensions.getSubfiles
import com.nextcloud.utils.extensions.makeRounded
import com.nextcloud.utils.extensions.setVisibleIf
Expand Down Expand Up @@ -106,33 +107,57 @@ class OCFileListDelegate(
galleryRowHolder: GalleryRowHolder,
imageDimension: Pair<Int, Int>
) {
// Cancel previous job for this ImageView
GalleryImageGenerationJob.cancelPreviousJob(imageView)

imageView.tag = file.fileId

ioScope.launch {
galleryImageGenerationJob.run(
file,
imageView,
imageDimension,
object : GalleryImageGenerationListener {
override fun onSuccess() {
galleryRowHolder.binding.rowLayout.invalidate()
Log_OC.d(tag, "setGalleryImage.onSuccess()")
DisplayUtils.stopShimmer(shimmer, imageView)
}
// set placeholder before async job
val cached = ThumbnailsCacheManager.getBitmapFromDiskCache(
ThumbnailsCacheManager.PREFIX_RESIZED_IMAGE + file.remoteId
)
if (cached != null) {
imageView.setImageBitmap(cached)
} else {
imageView.setImageDrawable(OCFileUtils.getMediaPlaceholder(file, imageDimension))
}

override fun onNewGalleryImage() {
Log_OC.d(tag, "setGalleryImage.updateRowVisuals()")
galleryRowHolder.updateRowVisuals()
}
val job = ioScope.launch {
try {
galleryImageGenerationJob.run(
file,
imageView,
object : GalleryImageGenerationListener {
override fun onSuccess() {
if (imageView.tag == file.fileId) {
Log_OC.d(tag, "setGalleryImage.onSuccess()")
galleryRowHolder.binding.rowLayout.invalidate()
DisplayUtils.stopShimmer(shimmer, imageView)
}
}

override fun onError() {
Log_OC.d(tag, "setGalleryImage.onError()")
DisplayUtils.stopShimmer(shimmer, imageView)
override fun onNewGalleryImage() {
if (imageView.tag == file.fileId) {
Log_OC.d(tag, "setGalleryImage.updateRowVisuals()")
galleryRowHolder.updateRowVisuals()
}
}

override fun onError() {
if (imageView.tag == file.fileId) {
Log_OC.d(tag, "setGalleryImage.onError()")
DisplayUtils.stopShimmer(shimmer, imageView)
}
}
}
}
)
)
} finally {
GalleryImageGenerationJob.removeActiveJob(imageView, this)
}
}

GalleryImageGenerationJob.storeJob(job, imageView)

imageView.setOnClickListener {
ocFileListFragmentInterface.onItemClicked(file)
GalleryFragment.setLastMediaItemPosition(galleryRowHolder.absoluteAdapterPosition)
Expand Down
Loading