Refactor file metadata types

This commit is contained in:
MM20
2023-10-29 22:50:50 +01:00
parent 97fca2d014
commit 0a508a37ba
11 changed files with 166 additions and 81 deletions
@@ -11,9 +11,12 @@ import de.mm20.launcher2.files.providers.OwncloudFile
import de.mm20.launcher2.ktx.jsonObjectOf
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.search.FileMetaType
import de.mm20.launcher2.search.SavableSearchable
import de.mm20.launcher2.search.SearchableDeserializer
import de.mm20.launcher2.search.SearchableSerializer
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap
import org.json.JSONObject
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
@@ -93,8 +96,8 @@ internal class GDriveFileSerializer : SearchableSerializer {
for ((k, v) in searchable.metaData) {
put(
when (k) {
R.string.file_meta_owner -> "owner"
R.string.file_meta_dimensions -> "dimensions"
FileMetaType.Owner -> "owner"
FileMetaType.Dimensions -> "dimensions"
else -> "other"
}, v
)
@@ -119,10 +122,10 @@ internal class GDriveFileDeserializer : SearchableDeserializer {
val uri = json.getString("uri")
val owner = json.optString("owner")
val dimensions = json.optString("dimensions")
val metaData = mutableListOf<Pair<Int, String>>()
owner.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_owner to it) }
val metaData = mutableMapOf<FileMetaType, String>()
owner.takeIf { it.isNotEmpty() }?.let { metaData[FileMetaType.Owner] = it }
dimensions.takeIf { it.isNotEmpty() }
?.let { metaData.add(R.string.file_meta_dimensions to it) }
?.let { metaData[FileMetaType.Dimensions] = it }
return GDriveFile(
fileId = id,
label = label,
@@ -132,7 +135,7 @@ internal class GDriveFileDeserializer : SearchableDeserializer {
directoryColor = color,
isDirectory = directory,
viewUri = uri,
metaData = metaData
metaData = metaData.toImmutableMap()
)
}
}
@@ -151,8 +154,8 @@ internal class OneDriveFileSerializer : SearchableSerializer {
for ((k, v) in searchable.metaData) {
put(
when (k) {
R.string.file_meta_owner -> "owner"
R.string.file_meta_dimensions -> "dimensions"
FileMetaType.Owner -> "owner"
FileMetaType.Dimensions -> "dimensions"
else -> "other"
}, v
)
@@ -175,10 +178,10 @@ internal class OneDriveFileDeserializer : SearchableDeserializer {
val webUrl = json.getString("webUrl")
val owner = json.optString("owner")
val dimensions = json.optString("dimensions")
val metaData = mutableListOf<Pair<Int, String>>()
owner.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_owner to it) }
val metaData = mutableMapOf<FileMetaType, String>()
owner.takeIf { it.isNotEmpty() }?.let { metaData[FileMetaType.Owner] = it }
dimensions.takeIf { it.isNotEmpty() }
?.let { metaData.add(R.string.file_meta_dimensions to it) }
?.let { metaData[FileMetaType.Dimensions] = it }
return OneDriveFile(
fileId = fileId,
label = label,
@@ -186,7 +189,7 @@ internal class OneDriveFileDeserializer : SearchableDeserializer {
mimeType = mimeType,
size = size,
isDirectory = isDirectory,
metaData = metaData,
metaData = metaData.toImmutableMap(),
webUrl = webUrl
)
}
@@ -207,7 +210,7 @@ internal class NextcloudFileSerializer : SearchableSerializer {
for ((k, v) in searchable.metaData) {
put(
when (k) {
R.string.file_meta_owner -> "owner"
FileMetaType.Owner -> "owner"
else -> "other"
}, v
)
@@ -239,7 +242,7 @@ internal class NextcloudFileDeserializer : SearchableDeserializer {
size = size,
isDirectory = isDirectory,
server = server,
metaData = owner?.let { listOf(R.string.file_meta_owner to it) } ?: emptyList()
metaData = owner?.let { persistentMapOf(FileMetaType.Owner to it) } ?: persistentMapOf()
)
}
@@ -260,7 +263,7 @@ internal class OwncloudFileSerializer : SearchableSerializer {
for ((k, v) in searchable.metaData) {
put(
when (k) {
R.string.file_meta_owner -> "owner"
FileMetaType.Owner -> "owner"
else -> "other"
}, v
)
@@ -292,7 +295,7 @@ internal class OwncloudFileDeserializer : SearchableDeserializer {
size = size,
isDirectory = isDirectory,
server = server,
metaData = owner?.let { listOf(R.string.file_meta_owner to it) } ?: emptyList()
metaData = owner?.let { persistentMapOf(FileMetaType.Owner to it) } ?: persistentMapOf()
)
}
@@ -8,7 +8,9 @@ import de.mm20.launcher2.files.GDriveFileSerializer
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import de.mm20.launcher2.search.SearchableSerializer
import kotlinx.collections.immutable.ImmutableMap
internal data class GDriveFile(
val fileId: String,
@@ -17,7 +19,7 @@ internal data class GDriveFile(
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
override val metaData: List<Pair<Int, String>>,
override val metaData: ImmutableMap<FileMetaType, String>,
val directoryColor: String?,
val viewUri: String,
override val labelOverride: String? = null,
@@ -5,6 +5,9 @@ import de.mm20.launcher2.files.R
import de.mm20.launcher2.gservices.DriveFileMeta
import de.mm20.launcher2.gservices.GoogleApiHelper
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.toImmutableMap
internal class GDriveFileProvider(
private val context: Context
@@ -27,13 +30,13 @@ internal class GDriveFileProvider(
}
}
private fun getMetadata(file: DriveFileMeta): List<Pair<Int, String>> {
val metaData = mutableListOf<Pair<Int, String>>()
private fun getMetadata(file: DriveFileMeta): ImmutableMap<FileMetaType, String> {
val metaData = mutableMapOf<FileMetaType, String>()
val owners = file.owners
metaData.add(R.string.file_meta_owner to owners.joinToString(separator = ", "))
val width = file.width ?: file.width
val height = file.height ?: file.height
if (width != null && height != null) metaData.add(R.string.file_meta_dimensions to "${width}x$height")
return metaData
metaData[FileMetaType.Owner] = owners.joinToString(separator = ", ")
val width = file.width
val height = file.height
if (width != null && height != null) metaData[FileMetaType.Dimensions] = "${width}x${height}"
return metaData.toImmutableMap()
}
}
@@ -17,13 +17,19 @@ import androidx.core.content.FileProvider
import androidx.exifinterface.media.ExifInterface
import de.mm20.launcher2.crashreporter.CrashReporter
import de.mm20.launcher2.files.LocalFileSerializer
import de.mm20.launcher2.files.R
import de.mm20.launcher2.icons.*
import de.mm20.launcher2.icons.ColorLayer
import de.mm20.launcher2.icons.LauncherIcon
import de.mm20.launcher2.icons.StaticIconLayer
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.icons.TransparentLayer
import de.mm20.launcher2.ktx.formatToString
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.media.ThumbnailUtilsCompat
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import de.mm20.launcher2.search.SearchableSerializer
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
@@ -36,7 +42,7 @@ internal data class LocalFile(
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
override val metaData: List<Pair<Int, String>>,
override val metaData: ImmutableMap<FileMetaType, String>,
override val labelOverride: String? = null
) : File {
@@ -75,6 +81,7 @@ internal data class LocalFile(
backgroundLayer = ColorLayer()
)
}
mimeType.startsWith("video/") -> {
val thumbnail = withContext(Dispatchers.IO) {
ThumbnailUtilsCompat.createVideoThumbnail(
@@ -91,6 +98,7 @@ internal data class LocalFile(
backgroundLayer = ColorLayer()
)
}
mimeType.startsWith("audio/") -> {
val thumbnail = withContext(Dispatchers.IO) {
val mediaMetadataRetriever = MediaMetadataRetriever()
@@ -123,6 +131,7 @@ internal data class LocalFile(
)
}
mimeType == "application/vnd.android.package-archive" -> {
val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0)
val icon = withContext(Dispatchers.IO) {
@@ -145,6 +154,7 @@ internal data class LocalFile(
} ?: TransparentLayer,
)
}
else -> {
return StaticLauncherIcon(
foregroundLayer = StaticIconLayer(
@@ -238,48 +248,55 @@ internal data class LocalFile(
context: Context,
mimeType: String,
path: String
): List<Pair<Int, String>> {
val metaData = mutableListOf<Pair<Int, String>>()
): ImmutableMap<FileMetaType, String> {
val metaData = mutableMapOf<FileMetaType, String>()
when {
mimeType.startsWith("audio/") -> {
val retriever = MediaMetadataRetriever()
try {
retriever.setDataSource(path)
arrayOf(
R.string.file_meta_title to MediaMetadataRetriever.METADATA_KEY_TITLE,
R.string.file_meta_artist to MediaMetadataRetriever.METADATA_KEY_ARTIST,
R.string.file_meta_album to MediaMetadataRetriever.METADATA_KEY_ALBUM,
R.string.file_meta_year to MediaMetadataRetriever.METADATA_KEY_YEAR
).forEach {
retriever.extractMetadata(it.second)
?.let { m -> metaData.add(it.first to m) }
}
val duration =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
?.toLong() ?: 0
val d = DateUtils.formatElapsedTime((duration) / 1000)
metaData.add(3, R.string.file_meta_duration to d)
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
?.let { metaData[FileMetaType.Title] = it }
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
?.let { metaData[FileMetaType.Artist] = it }
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM)
?.let { metaData[FileMetaType.Album] = it }
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR)
?.let { metaData[FileMetaType.Year] = it }
retriever
.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
?.toLongOrNull()
?.let {
metaData[FileMetaType.Duration] =
DateUtils.formatElapsedTime((it) / 1000)
}
retriever.release()
} catch (e: RuntimeException) {
retriever.release()
}
}
mimeType.startsWith("video/") -> {
val retriever = MediaMetadataRetriever()
try {
retriever.setDataSource(path)
val width =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
?.toLong() ?: 0
?.toLongOrNull()
val height =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
?.toLong() ?: 0
metaData.add(R.string.file_meta_dimensions to "${width}x$height")
?.toLongOrNull()
if (width != null && height != null) {
metaData[FileMetaType.Dimensions] = "${width}x$height"
}
val duration =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
?.toLong() ?: 0
val d = DateUtils.formatElapsedTime(duration / 1000)
metaData.add(R.string.file_meta_duration to d)
?.toLongOrNull()
?.let {
metaData[FileMetaType.Duration] =
DateUtils.formatElapsedTime((it) / 1000)
}
val loc =
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION)
if (Geocoder.isPresent() && loc != null) {
@@ -290,9 +307,15 @@ internal data class LocalFile(
loc.lastIndexOfAny(charArrayOf('+', '-')),
loc.indexOf('/')
).toDouble()
val list = Geocoder(context).getFromLocation(lon, lat, 1)
val list = try {
Geocoder(context).getFromLocation(lon, lat, 1)
} catch (e: IOException) {
null
} catch (e: IllegalArgumentException) {
null
}
if (list != null && list.size > 0) {
metaData.add(R.string.file_meta_location to list[0].formatToString())
metaData[FileMetaType.Location] = list[0].formatToString()
}
}
retriever.release()
@@ -304,40 +327,48 @@ internal data class LocalFile(
retriever.release()
}
}
mimeType.startsWith("image/") -> {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, options)
val width = options.outWidth
val height = options.outHeight
metaData.add(R.string.file_meta_dimensions to "${width}x$height")
if (height >= 0 && width >= 0) {
metaData[FileMetaType.Dimensions] = "${width}x$height"
}
try {
val exif = ExifInterface(path)
val loc = exif.latLong
if (loc != null && Geocoder.isPresent()) {
val list = Geocoder(context).getFromLocation(loc[0], loc[1], 1)
val list = try {
Geocoder(context).getFromLocation(loc[0], loc[1], 1)
} catch (e: IllegalArgumentException) {
null
} catch (e: IOException) {
null
}
if (list != null && list.size > 0) {
metaData.add(R.string.file_meta_location to list[0].formatToString())
metaData[FileMetaType.Location] = list[0].formatToString()
}
}
} catch (_: IOException) {
}
}
mimeType == "application/vnd.android.package-archive" -> {
val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0)
?: return metaData
metaData.add(
R.string.file_meta_app_name to pkgInfo.applicationInfo.loadLabel(
context.packageManager
).toString()
)
metaData.add(R.string.file_meta_app_pkgname to pkgInfo.packageName)
metaData.add(R.string.file_meta_app_version to pkgInfo.versionName)
metaData.add(R.string.file_meta_app_min_sdk to pkgInfo.applicationInfo.minSdkVersion.toString())
?: return metaData.toImmutableMap()
metaData[FileMetaType.AppName] =
pkgInfo.applicationInfo.loadLabel(context.packageManager).toString()
metaData[FileMetaType.AppPackageName] = pkgInfo.packageName
metaData[FileMetaType.AppVersion] = pkgInfo.versionName
metaData[FileMetaType.AppMinSdk] =
pkgInfo.applicationInfo.minSdkVersion.toString()
}
}
return metaData
return metaData.toImmutableMap()
}
}
@@ -9,7 +9,9 @@ import de.mm20.launcher2.files.NextcloudFileSerializer
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import de.mm20.launcher2.search.SearchableSerializer
import kotlinx.collections.immutable.ImmutableMap
internal data class NextcloudFile(
val fileId: Long,
@@ -19,7 +21,7 @@ internal data class NextcloudFile(
override val size: Long,
override val isDirectory: Boolean,
val server: String,
override val metaData: List<Pair<Int, String>>,
override val metaData: ImmutableMap<FileMetaType, String>,
override val labelOverride: String? = null,
) : File {
@@ -3,6 +3,8 @@ package de.mm20.launcher2.files.providers
import de.mm20.launcher2.files.R
import de.mm20.launcher2.nextcloud.NextcloudApiHelper
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlin.math.min
@@ -23,8 +25,8 @@ internal class NextcloudFileProvider(
size = it.size,
isDirectory = it.isDirectory,
server = server,
metaData = it.owner?.let { listOf(R.string.file_meta_owner to it) }
?: emptyList()
metaData = it.owner?.let { persistentMapOf(FileMetaType.Owner to it) }
?: persistentMapOf()
)
}
}
@@ -8,18 +8,20 @@ import de.mm20.launcher2.files.OneDriveFileSerializer
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import de.mm20.launcher2.search.SearchableSerializer
import kotlinx.collections.immutable.ImmutableMap
internal data class OneDriveFile(
val fileId: String,
override val label: String,
override val path: String,
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
override val metaData: List<Pair<Int, String>>,
val webUrl: String,
override val labelOverride: String? = null,
val fileId: String,
override val label: String,
override val path: String,
override val mimeType: String,
override val size: Long,
override val isDirectory: Boolean,
override val metaData: ImmutableMap<FileMetaType, String>,
val webUrl: String,
override val labelOverride: String? = null,
) : File {
override fun overrideLabel(label: String): OneDriveFile {
@@ -8,7 +8,9 @@ import de.mm20.launcher2.files.OwncloudFileSerializer
import de.mm20.launcher2.files.R
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import de.mm20.launcher2.search.SearchableSerializer
import kotlinx.collections.immutable.ImmutableMap
internal data class OwncloudFile(
val fileId: Long,
@@ -18,7 +20,7 @@ internal data class OwncloudFile(
override val size: Long,
override val isDirectory: Boolean,
val server: String,
override val metaData: List<Pair<Int, String>>,
override val metaData: ImmutableMap<FileMetaType, String>,
override val labelOverride: String? = null,
) : File {
@@ -3,6 +3,8 @@ package de.mm20.launcher2.files.providers
import de.mm20.launcher2.files.R
import de.mm20.launcher2.owncloud.OwncloudClient
import de.mm20.launcher2.search.File
import de.mm20.launcher2.search.FileMetaType
import kotlinx.collections.immutable.persistentMapOf
internal class OwncloudFileProvider(
private val owncloudClient: OwncloudClient
@@ -19,7 +21,8 @@ internal class OwncloudFileProvider(
size = it.size,
isDirectory = it.isDirectory,
server = server,
metaData = it.owner?.let { listOf(R.string.file_meta_owner to it) } ?: emptyList()
metaData = it.owner?.let { persistentMapOf(FileMetaType.Owner to it) }
?: persistentMapOf()
)
}
}