From a94b8ad2d5abe5fc6734fb91820dd00ae0be42f1 Mon Sep 17 00:00:00 2001 From: Vincent TE Date: Thu, 13 Feb 2025 10:22:15 +0100 Subject: [PATCH] chore: Use Thumbnails module --- Core | 2 +- app/build.gradle | 1 + .../infomaniak/drive/utils/FileItemUtils.kt | 111 +----------------- .../java/com/infomaniak/drive/utils/Utils.kt | 44 ------- settings.gradle | 2 +- 5 files changed, 6 insertions(+), 154 deletions(-) diff --git a/Core b/Core index 7b1f0c72c7..d66155fc72 160000 --- a/Core +++ b/Core @@ -1 +1 @@ -Subproject commit 7b1f0c72c7031206b8161edd59a01e38c4edaac6 +Subproject commit d66155fc721f8b208f5de35da9f42bc84c2f2fc8 diff --git a/app/build.gradle b/app/build.gradle index 500d829b21..fd78acd4c8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -132,6 +132,7 @@ dependencies { implementation project(path: ':Core:Legacy') implementation project(path: ':Core:Legacy:AppLock') implementation project(path: ':Core:Legacy:Stores') + implementation project(path: ':Core:Thumbnails') def work_version = '2.9.1' // Keep the same version as the one in Core implementation "androidx.work:work-runtime-ktx:$work_version" diff --git a/app/src/main/java/com/infomaniak/drive/utils/FileItemUtils.kt b/app/src/main/java/com/infomaniak/drive/utils/FileItemUtils.kt index f6ed61cb9b..1cf338aa00 100644 --- a/app/src/main/java/com/infomaniak/drive/utils/FileItemUtils.kt +++ b/app/src/main/java/com/infomaniak/drive/utils/FileItemUtils.kt @@ -17,22 +17,12 @@ */ package com.infomaniak.drive.utils -import android.content.ContentResolver -import android.content.ContentUris import android.content.Context -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.graphics.drawable.Drawable -import android.media.ThumbnailUtils -import android.net.Uri -import android.os.Build -import android.provider.MediaStore -import android.util.Size import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.graphics.toColorInt -import androidx.core.net.toFile import androidx.core.net.toUri import androidx.core.view.forEachIndexed import androidx.core.view.isGone @@ -41,6 +31,7 @@ import androidx.core.view.isVisible import androidx.viewbinding.ViewBinding import coil.load import com.google.android.material.progressindicator.CircularProgressIndicator +import com.infomaniak.core.thumbnails.ThumbnailsUtils.getLocalThumbnail import com.infomaniak.drive.R import com.infomaniak.drive.data.api.ApiRoutes import com.infomaniak.drive.data.cache.DriveInfosController @@ -56,7 +47,6 @@ import com.infomaniak.drive.views.CategoryIconView import com.infomaniak.drive.views.ProgressLayoutView import com.infomaniak.lib.core.utils.context import com.infomaniak.lib.core.utils.format -import io.sentry.Sentry import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -143,7 +133,8 @@ private fun ImageView.displayFileIcon( file.isFromUploads && isGraphic -> { scaleType = ImageView.ScaleType.CENTER_CROP CoroutineScope(Dispatchers.IO).launch { - val bitmap = context.getLocalThumbnail(file) + val isVideo = file.getMimeType().contains("video") + val bitmap = context.getLocalThumbnail(file.path.toUri(), isVideo, 100) withContext(Dispatchers.Main) { if (isVisible && context != null) loadAny(bitmap, fileType.icon) } @@ -216,102 +207,6 @@ fun File.getFolderIcon(): Pair { } } -suspend fun Context.getLocalThumbnail(file: File): Bitmap? = withContext(Dispatchers.IO) { - val fileUri = file.path.toUri() - val thumbnailSize = 100 - return@withContext if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - getThumbnailAfterAndroidPie(file, fileUri, thumbnailSize) - } else { - getThumbnailUntilAndroidPie(file, fileUri, thumbnailSize) - } -} - -private fun Context.getThumbnailAfterAndroidPie(file: File, fileUri: Uri, thumbnailSize: Int): Bitmap? { - return if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - val size = Size(thumbnailSize, thumbnailSize) - try { - if (fileUri.scheme.equals(ContentResolver.SCHEME_FILE)) { - if (file.getMimeType().contains("video")) { - ThumbnailUtils.createVideoThumbnail(fileUri.toFile(), size, null) - } else { - ThumbnailUtils.createImageThumbnail(fileUri.toFile(), size, null) - } - } else { - contentResolver.loadThumbnail(fileUri, size, null) - } - } catch (e: Exception) { - null - } - } else { - null - } -} - -private fun Context.getThumbnailUntilAndroidPie(file: File, fileUri: Uri, thumbnailSize: Int): Bitmap? { - val isSchemeFile = fileUri.scheme.equals(ContentResolver.SCHEME_FILE) - val localFile = fileUri.lastPathSegment?.split(":")?.let { list -> - list.getOrNull(1)?.let { path -> IOFile(path) } - } - val externalRealPath = getExternalRealPath(fileUri, isSchemeFile, localFile) - - return if (isSchemeFile || externalRealPath.isNotBlank()) { - getBitmapFromPath(file, fileUri, thumbnailSize, externalRealPath) - } else { - getBitmapFromFileId(fileUri, thumbnailSize) - } -} - -private fun Context.getExternalRealPath(fileUri: Uri, isSchemeFile: Boolean, localFile: IOFile?): String { - return when { - !isSchemeFile && localFile?.exists() == true -> { - Sentry.captureMessage("Uri contains absolute path") - localFile.absolutePath - } - fileUri.authority == "com.android.externalstorage.documents" -> { - Utils.getRealPathFromExternalStorage(this, fileUri) - } - else -> "" - } -} - -private fun getBitmapFromPath(file: File, fileUri: Uri, thumbnailSize: Int, externalRealPath: String): Bitmap? { - val path = externalRealPath.ifBlank { fileUri.path ?: return null } - - return if (file.getMimeType().contains("video")) { - ThumbnailUtils.createVideoThumbnail(path, MediaStore.Video.Thumbnails.MICRO_KIND) - } else { - Utils.extractThumbnail(path, thumbnailSize, thumbnailSize) - } -} - -private fun Context.getBitmapFromFileId(fileUri: Uri, thumbnailSize: Int): Bitmap? { - return try { - ContentUris.parseId(fileUri) - } catch (e: Exception) { - fileUri.lastPathSegment?.split(":")?.let { it.getOrNull(1)?.toLongOrNull() } - }?.let { fileId -> - val options = BitmapFactory.Options().apply { - outWidth = thumbnailSize - outHeight = thumbnailSize - } - if (contentResolver.getType(fileUri)?.contains("video") == true) { - MediaStore.Video.Thumbnails.getThumbnail( - contentResolver, - fileId, - MediaStore.Video.Thumbnails.MICRO_KIND, - options, - ) - } else { - MediaStore.Images.Thumbnails.getThumbnail( - contentResolver, - fileId, - MediaStore.Images.Thumbnails.MICRO_KIND, - options, - ) - } - } -} - fun ProgressLayoutView.setupFileProgress(file: File, containsProgress: Boolean = false) { when { !containsProgress && file.isMarkedAsOffline -> { diff --git a/app/src/main/java/com/infomaniak/drive/utils/Utils.kt b/app/src/main/java/com/infomaniak/drive/utils/Utils.kt index 5439636409..7bebbbdf0c 100644 --- a/app/src/main/java/com/infomaniak/drive/utils/Utils.kt +++ b/app/src/main/java/com/infomaniak/drive/utils/Utils.kt @@ -22,12 +22,8 @@ import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.content.Intent -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.net.Uri import android.os.Build -import android.os.Environment -import android.provider.DocumentsContract import android.view.LayoutInflater import androidx.activity.result.ActivityResultLauncher import androidx.annotation.DrawableRes @@ -69,7 +65,6 @@ import com.infomaniak.lib.core.utils.DownloadManagerUtils import com.infomaniak.lib.core.utils.showKeyboard import com.infomaniak.lib.core.utils.showToast import java.util.Date -import kotlin.math.min import kotlin.math.pow object Utils { @@ -415,45 +410,6 @@ object Utils { return outputFile.toUri() } - /** - * From file path - */ - @Deprecated(message = "Only for API 28 and below, otherwise use ThumbnailUtils.createImageThumbnail()") - fun extractThumbnail(filePath: String, width: Int, height: Int): Bitmap? { - val bitmapOptions = BitmapFactory.Options() - bitmapOptions.inJustDecodeBounds = true - BitmapFactory.decodeFile(filePath, bitmapOptions) - - val widthScale = bitmapOptions.outWidth.toFloat() / width - val heightScale = bitmapOptions.outHeight.toFloat() / height - val scale = min(widthScale, heightScale) - var sampleSize = 1 - while (sampleSize < scale) { - sampleSize *= 2 - } - bitmapOptions.inSampleSize = sampleSize - bitmapOptions.inJustDecodeBounds = false - - return BitmapFactory.decodeFile(filePath, bitmapOptions) - } - - fun getRealPathFromExternalStorage(context: Context, uri: Uri): String { - // ExternalStorageProvider - val docId = DocumentsContract.getDocumentId(uri) - val split = docId.split(":").dropLastWhile { it.isEmpty() }.toTypedArray() - val type = split.first() - val relativePath = split.getOrNull(1) ?: return "" - val external = context.externalMediaDirs - return when { - "primary".equals(type, true) -> Environment.getExternalStorageDirectory().toString() + "/" + relativePath - external.size > 1 -> { - val filePath = external[1].absolutePath - filePath.substring(0, filePath.indexOf("Android")) + relativePath - } - else -> "" - } - } - fun createProgressDialog(context: Context, title: Int): AlertDialog { return MaterialAlertDialogBuilder(context, R.style.DialogStyle).apply { setTitle(title) diff --git a/settings.gradle b/settings.gradle index 2084089217..f0413ec4c9 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,4 +21,4 @@ dependencyResolutionManagement { } rootProject.name = "kDrive" -include ':app', ':Core:Legacy', ':Core:Legacy:AppLock', ':Core:Legacy:Stores' +include ':app', ':Core:Legacy', ':Core:Legacy:AppLock', ':Core:Legacy:Stores', ':Core:Thumbnails'