Refactor file metadata types
This commit is contained in:
parent
97fca2d014
commit
0a508a37ba
@ -48,6 +48,7 @@ import androidx.compose.ui.unit.roundToIntRect
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
import de.mm20.launcher2.ui.R
|
import de.mm20.launcher2.ui.R
|
||||||
import de.mm20.launcher2.ui.animation.animateTextStyleAsState
|
import de.mm20.launcher2.ui.animation.animateTextStyleAsState
|
||||||
import de.mm20.launcher2.ui.component.DefaultToolbarAction
|
import de.mm20.launcher2.ui.component.DefaultToolbarAction
|
||||||
@ -136,7 +137,7 @@ fun FileItem(
|
|||||||
)
|
)
|
||||||
for ((k, v) in file.metaData) {
|
for ((k, v) in file.metaData) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(k, v),
|
text = stringResource(k.labelRes, v),
|
||||||
style = MaterialTheme.typography.bodySmall
|
style = MaterialTheme.typography.bodySmall
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -365,4 +366,22 @@ private fun formatFileSize(size: Long): String {
|
|||||||
size < 1000000000000L -> "${DecimalFormat("#,##0.#").format(size / 1000000000.0)} GB"
|
size < 1000000000000L -> "${DecimalFormat("#,##0.#").format(size / 1000000000.0)} GB"
|
||||||
else -> "${DecimalFormat("#,##0.#").format(size / 1000000000000.0)} TB"
|
else -> "${DecimalFormat("#,##0.#").format(size / 1000000000000.0)} TB"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val FileMetaType.labelRes: Int
|
||||||
|
get() {
|
||||||
|
return when (this) {
|
||||||
|
FileMetaType.Title -> R.string.file_meta_title
|
||||||
|
FileMetaType.Artist -> R.string.file_meta_artist
|
||||||
|
FileMetaType.Album -> R.string.file_meta_album
|
||||||
|
FileMetaType.Duration -> R.string.file_meta_duration
|
||||||
|
FileMetaType.Year -> R.string.file_meta_year
|
||||||
|
FileMetaType.Dimensions -> R.string.file_meta_dimensions
|
||||||
|
FileMetaType.Location -> R.string.file_meta_location
|
||||||
|
FileMetaType.AppName -> R.string.file_meta_app_name
|
||||||
|
FileMetaType.AppVersion -> R.string.file_meta_app_version
|
||||||
|
FileMetaType.AppMinSdk -> R.string.file_meta_app_min_sdk
|
||||||
|
FileMetaType.AppPackageName -> R.string.file_meta_app_pkgname
|
||||||
|
FileMetaType.Owner -> R.string.file_meta_owner
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ import de.mm20.launcher2.base.R
|
|||||||
import de.mm20.launcher2.icons.ColorLayer
|
import de.mm20.launcher2.icons.ColorLayer
|
||||||
import de.mm20.launcher2.icons.StaticLauncherIcon
|
import de.mm20.launcher2.icons.StaticLauncherIcon
|
||||||
import de.mm20.launcher2.icons.TintedIconLayer
|
import de.mm20.launcher2.icons.TintedIconLayer
|
||||||
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
interface File : SavableSearchable {
|
interface File : SavableSearchable {
|
||||||
@ -13,7 +14,7 @@ interface File : SavableSearchable {
|
|||||||
val mimeType: String
|
val mimeType: String
|
||||||
val size: Long
|
val size: Long
|
||||||
val isDirectory: Boolean
|
val isDirectory: Boolean
|
||||||
val metaData: List<Pair<Int, String>>
|
val metaData: ImmutableMap<FileMetaType, String>
|
||||||
|
|
||||||
val isStoredInCloud: Boolean
|
val isStoredInCloud: Boolean
|
||||||
|
|
||||||
@ -139,4 +140,19 @@ interface File : SavableSearchable {
|
|||||||
fun share(context: Context) {}
|
fun share(context: Context) {}
|
||||||
suspend fun delete(context: Context) {}
|
suspend fun delete(context: Context) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class FileMetaType {
|
||||||
|
Title,
|
||||||
|
Artist,
|
||||||
|
Album,
|
||||||
|
Duration,
|
||||||
|
Year,
|
||||||
|
Dimensions,
|
||||||
|
Location,
|
||||||
|
AppName,
|
||||||
|
AppVersion,
|
||||||
|
AppMinSdk,
|
||||||
|
AppPackageName,
|
||||||
|
Owner,
|
||||||
}
|
}
|
||||||
@ -11,9 +11,12 @@ import de.mm20.launcher2.files.providers.OwncloudFile
|
|||||||
import de.mm20.launcher2.ktx.jsonObjectOf
|
import de.mm20.launcher2.ktx.jsonObjectOf
|
||||||
import de.mm20.launcher2.permissions.PermissionGroup
|
import de.mm20.launcher2.permissions.PermissionGroup
|
||||||
import de.mm20.launcher2.permissions.PermissionsManager
|
import de.mm20.launcher2.permissions.PermissionsManager
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
import de.mm20.launcher2.search.SavableSearchable
|
import de.mm20.launcher2.search.SavableSearchable
|
||||||
import de.mm20.launcher2.search.SearchableDeserializer
|
import de.mm20.launcher2.search.SearchableDeserializer
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
@ -93,8 +96,8 @@ internal class GDriveFileSerializer : SearchableSerializer {
|
|||||||
for ((k, v) in searchable.metaData) {
|
for ((k, v) in searchable.metaData) {
|
||||||
put(
|
put(
|
||||||
when (k) {
|
when (k) {
|
||||||
R.string.file_meta_owner -> "owner"
|
FileMetaType.Owner -> "owner"
|
||||||
R.string.file_meta_dimensions -> "dimensions"
|
FileMetaType.Dimensions -> "dimensions"
|
||||||
else -> "other"
|
else -> "other"
|
||||||
}, v
|
}, v
|
||||||
)
|
)
|
||||||
@ -119,10 +122,10 @@ internal class GDriveFileDeserializer : SearchableDeserializer {
|
|||||||
val uri = json.getString("uri")
|
val uri = json.getString("uri")
|
||||||
val owner = json.optString("owner")
|
val owner = json.optString("owner")
|
||||||
val dimensions = json.optString("dimensions")
|
val dimensions = json.optString("dimensions")
|
||||||
val metaData = mutableListOf<Pair<Int, String>>()
|
val metaData = mutableMapOf<FileMetaType, String>()
|
||||||
owner.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_owner to it) }
|
owner.takeIf { it.isNotEmpty() }?.let { metaData[FileMetaType.Owner] = it }
|
||||||
dimensions.takeIf { it.isNotEmpty() }
|
dimensions.takeIf { it.isNotEmpty() }
|
||||||
?.let { metaData.add(R.string.file_meta_dimensions to it) }
|
?.let { metaData[FileMetaType.Dimensions] = it }
|
||||||
return GDriveFile(
|
return GDriveFile(
|
||||||
fileId = id,
|
fileId = id,
|
||||||
label = label,
|
label = label,
|
||||||
@ -132,7 +135,7 @@ internal class GDriveFileDeserializer : SearchableDeserializer {
|
|||||||
directoryColor = color,
|
directoryColor = color,
|
||||||
isDirectory = directory,
|
isDirectory = directory,
|
||||||
viewUri = uri,
|
viewUri = uri,
|
||||||
metaData = metaData
|
metaData = metaData.toImmutableMap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,8 +154,8 @@ internal class OneDriveFileSerializer : SearchableSerializer {
|
|||||||
for ((k, v) in searchable.metaData) {
|
for ((k, v) in searchable.metaData) {
|
||||||
put(
|
put(
|
||||||
when (k) {
|
when (k) {
|
||||||
R.string.file_meta_owner -> "owner"
|
FileMetaType.Owner -> "owner"
|
||||||
R.string.file_meta_dimensions -> "dimensions"
|
FileMetaType.Dimensions -> "dimensions"
|
||||||
else -> "other"
|
else -> "other"
|
||||||
}, v
|
}, v
|
||||||
)
|
)
|
||||||
@ -175,10 +178,10 @@ internal class OneDriveFileDeserializer : SearchableDeserializer {
|
|||||||
val webUrl = json.getString("webUrl")
|
val webUrl = json.getString("webUrl")
|
||||||
val owner = json.optString("owner")
|
val owner = json.optString("owner")
|
||||||
val dimensions = json.optString("dimensions")
|
val dimensions = json.optString("dimensions")
|
||||||
val metaData = mutableListOf<Pair<Int, String>>()
|
val metaData = mutableMapOf<FileMetaType, String>()
|
||||||
owner.takeIf { it.isNotEmpty() }?.let { metaData.add(R.string.file_meta_owner to it) }
|
owner.takeIf { it.isNotEmpty() }?.let { metaData[FileMetaType.Owner] = it }
|
||||||
dimensions.takeIf { it.isNotEmpty() }
|
dimensions.takeIf { it.isNotEmpty() }
|
||||||
?.let { metaData.add(R.string.file_meta_dimensions to it) }
|
?.let { metaData[FileMetaType.Dimensions] = it }
|
||||||
return OneDriveFile(
|
return OneDriveFile(
|
||||||
fileId = fileId,
|
fileId = fileId,
|
||||||
label = label,
|
label = label,
|
||||||
@ -186,7 +189,7 @@ internal class OneDriveFileDeserializer : SearchableDeserializer {
|
|||||||
mimeType = mimeType,
|
mimeType = mimeType,
|
||||||
size = size,
|
size = size,
|
||||||
isDirectory = isDirectory,
|
isDirectory = isDirectory,
|
||||||
metaData = metaData,
|
metaData = metaData.toImmutableMap(),
|
||||||
webUrl = webUrl
|
webUrl = webUrl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -207,7 +210,7 @@ internal class NextcloudFileSerializer : SearchableSerializer {
|
|||||||
for ((k, v) in searchable.metaData) {
|
for ((k, v) in searchable.metaData) {
|
||||||
put(
|
put(
|
||||||
when (k) {
|
when (k) {
|
||||||
R.string.file_meta_owner -> "owner"
|
FileMetaType.Owner -> "owner"
|
||||||
else -> "other"
|
else -> "other"
|
||||||
}, v
|
}, v
|
||||||
)
|
)
|
||||||
@ -239,7 +242,7 @@ internal class NextcloudFileDeserializer : SearchableDeserializer {
|
|||||||
size = size,
|
size = size,
|
||||||
isDirectory = isDirectory,
|
isDirectory = isDirectory,
|
||||||
server = server,
|
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) {
|
for ((k, v) in searchable.metaData) {
|
||||||
put(
|
put(
|
||||||
when (k) {
|
when (k) {
|
||||||
R.string.file_meta_owner -> "owner"
|
FileMetaType.Owner -> "owner"
|
||||||
else -> "other"
|
else -> "other"
|
||||||
}, v
|
}, v
|
||||||
)
|
)
|
||||||
@ -292,7 +295,7 @@ internal class OwncloudFileDeserializer : SearchableDeserializer {
|
|||||||
size = size,
|
size = size,
|
||||||
isDirectory = isDirectory,
|
isDirectory = isDirectory,
|
||||||
server = server,
|
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.files.R
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
|
|
||||||
internal data class GDriveFile(
|
internal data class GDriveFile(
|
||||||
val fileId: String,
|
val fileId: String,
|
||||||
@ -17,7 +19,7 @@ internal data class GDriveFile(
|
|||||||
override val mimeType: String,
|
override val mimeType: String,
|
||||||
override val size: Long,
|
override val size: Long,
|
||||||
override val isDirectory: Boolean,
|
override val isDirectory: Boolean,
|
||||||
override val metaData: List<Pair<Int, String>>,
|
override val metaData: ImmutableMap<FileMetaType, String>,
|
||||||
val directoryColor: String?,
|
val directoryColor: String?,
|
||||||
val viewUri: String,
|
val viewUri: String,
|
||||||
override val labelOverride: String? = null,
|
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.DriveFileMeta
|
||||||
import de.mm20.launcher2.gservices.GoogleApiHelper
|
import de.mm20.launcher2.gservices.GoogleApiHelper
|
||||||
import de.mm20.launcher2.search.File
|
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(
|
internal class GDriveFileProvider(
|
||||||
private val context: Context
|
private val context: Context
|
||||||
@ -27,13 +30,13 @@ internal class GDriveFileProvider(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getMetadata(file: DriveFileMeta): List<Pair<Int, String>> {
|
private fun getMetadata(file: DriveFileMeta): ImmutableMap<FileMetaType, String> {
|
||||||
val metaData = mutableListOf<Pair<Int, String>>()
|
val metaData = mutableMapOf<FileMetaType, String>()
|
||||||
val owners = file.owners
|
val owners = file.owners
|
||||||
metaData.add(R.string.file_meta_owner to owners.joinToString(separator = ", "))
|
metaData[FileMetaType.Owner] = owners.joinToString(separator = ", ")
|
||||||
val width = file.width ?: file.width
|
val width = file.width
|
||||||
val height = file.height ?: file.height
|
val height = file.height
|
||||||
if (width != null && height != null) metaData.add(R.string.file_meta_dimensions to "${width}x$height")
|
if (width != null && height != null) metaData[FileMetaType.Dimensions] = "${width}x${height}"
|
||||||
return metaData
|
return metaData.toImmutableMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,13 +17,19 @@ import androidx.core.content.FileProvider
|
|||||||
import androidx.exifinterface.media.ExifInterface
|
import androidx.exifinterface.media.ExifInterface
|
||||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||||
import de.mm20.launcher2.files.LocalFileSerializer
|
import de.mm20.launcher2.files.LocalFileSerializer
|
||||||
import de.mm20.launcher2.files.R
|
import de.mm20.launcher2.icons.ColorLayer
|
||||||
import de.mm20.launcher2.icons.*
|
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.formatToString
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.media.ThumbnailUtilsCompat
|
import de.mm20.launcher2.media.ThumbnailUtilsCompat
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -36,7 +42,7 @@ internal data class LocalFile(
|
|||||||
override val mimeType: String,
|
override val mimeType: String,
|
||||||
override val size: Long,
|
override val size: Long,
|
||||||
override val isDirectory: Boolean,
|
override val isDirectory: Boolean,
|
||||||
override val metaData: List<Pair<Int, String>>,
|
override val metaData: ImmutableMap<FileMetaType, String>,
|
||||||
override val labelOverride: String? = null
|
override val labelOverride: String? = null
|
||||||
) : File {
|
) : File {
|
||||||
|
|
||||||
@ -75,6 +81,7 @@ internal data class LocalFile(
|
|||||||
backgroundLayer = ColorLayer()
|
backgroundLayer = ColorLayer()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType.startsWith("video/") -> {
|
mimeType.startsWith("video/") -> {
|
||||||
val thumbnail = withContext(Dispatchers.IO) {
|
val thumbnail = withContext(Dispatchers.IO) {
|
||||||
ThumbnailUtilsCompat.createVideoThumbnail(
|
ThumbnailUtilsCompat.createVideoThumbnail(
|
||||||
@ -91,6 +98,7 @@ internal data class LocalFile(
|
|||||||
backgroundLayer = ColorLayer()
|
backgroundLayer = ColorLayer()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType.startsWith("audio/") -> {
|
mimeType.startsWith("audio/") -> {
|
||||||
val thumbnail = withContext(Dispatchers.IO) {
|
val thumbnail = withContext(Dispatchers.IO) {
|
||||||
val mediaMetadataRetriever = MediaMetadataRetriever()
|
val mediaMetadataRetriever = MediaMetadataRetriever()
|
||||||
@ -123,6 +131,7 @@ internal data class LocalFile(
|
|||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType == "application/vnd.android.package-archive" -> {
|
mimeType == "application/vnd.android.package-archive" -> {
|
||||||
val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0)
|
val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0)
|
||||||
val icon = withContext(Dispatchers.IO) {
|
val icon = withContext(Dispatchers.IO) {
|
||||||
@ -145,6 +154,7 @@ internal data class LocalFile(
|
|||||||
} ?: TransparentLayer,
|
} ?: TransparentLayer,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
return StaticLauncherIcon(
|
return StaticLauncherIcon(
|
||||||
foregroundLayer = StaticIconLayer(
|
foregroundLayer = StaticIconLayer(
|
||||||
@ -238,48 +248,55 @@ internal data class LocalFile(
|
|||||||
context: Context,
|
context: Context,
|
||||||
mimeType: String,
|
mimeType: String,
|
||||||
path: String
|
path: String
|
||||||
): List<Pair<Int, String>> {
|
): ImmutableMap<FileMetaType, String> {
|
||||||
val metaData = mutableListOf<Pair<Int, String>>()
|
val metaData = mutableMapOf<FileMetaType, String>()
|
||||||
when {
|
when {
|
||||||
mimeType.startsWith("audio/") -> {
|
mimeType.startsWith("audio/") -> {
|
||||||
val retriever = MediaMetadataRetriever()
|
val retriever = MediaMetadataRetriever()
|
||||||
try {
|
try {
|
||||||
retriever.setDataSource(path)
|
retriever.setDataSource(path)
|
||||||
arrayOf(
|
|
||||||
R.string.file_meta_title to MediaMetadataRetriever.METADATA_KEY_TITLE,
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
|
||||||
R.string.file_meta_artist to MediaMetadataRetriever.METADATA_KEY_ARTIST,
|
?.let { metaData[FileMetaType.Title] = it }
|
||||||
R.string.file_meta_album to MediaMetadataRetriever.METADATA_KEY_ALBUM,
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
|
||||||
R.string.file_meta_year to MediaMetadataRetriever.METADATA_KEY_YEAR
|
?.let { metaData[FileMetaType.Artist] = it }
|
||||||
).forEach {
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM)
|
||||||
retriever.extractMetadata(it.second)
|
?.let { metaData[FileMetaType.Album] = it }
|
||||||
?.let { m -> metaData.add(it.first to m) }
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR)
|
||||||
}
|
?.let { metaData[FileMetaType.Year] = it }
|
||||||
val duration =
|
retriever
|
||||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
|
.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
|
||||||
?.toLong() ?: 0
|
?.toLongOrNull()
|
||||||
val d = DateUtils.formatElapsedTime((duration) / 1000)
|
?.let {
|
||||||
metaData.add(3, R.string.file_meta_duration to d)
|
metaData[FileMetaType.Duration] =
|
||||||
|
DateUtils.formatElapsedTime((it) / 1000)
|
||||||
|
}
|
||||||
retriever.release()
|
retriever.release()
|
||||||
} catch (e: RuntimeException) {
|
} catch (e: RuntimeException) {
|
||||||
retriever.release()
|
retriever.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType.startsWith("video/") -> {
|
mimeType.startsWith("video/") -> {
|
||||||
val retriever = MediaMetadataRetriever()
|
val retriever = MediaMetadataRetriever()
|
||||||
try {
|
try {
|
||||||
retriever.setDataSource(path)
|
retriever.setDataSource(path)
|
||||||
val width =
|
val width =
|
||||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
|
||||||
?.toLong() ?: 0
|
?.toLongOrNull()
|
||||||
val height =
|
val height =
|
||||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
|
||||||
?.toLong() ?: 0
|
?.toLongOrNull()
|
||||||
metaData.add(R.string.file_meta_dimensions to "${width}x$height")
|
if (width != null && height != null) {
|
||||||
|
metaData[FileMetaType.Dimensions] = "${width}x$height"
|
||||||
|
}
|
||||||
val duration =
|
val duration =
|
||||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
|
||||||
?.toLong() ?: 0
|
?.toLongOrNull()
|
||||||
val d = DateUtils.formatElapsedTime(duration / 1000)
|
?.let {
|
||||||
metaData.add(R.string.file_meta_duration to d)
|
metaData[FileMetaType.Duration] =
|
||||||
|
DateUtils.formatElapsedTime((it) / 1000)
|
||||||
|
}
|
||||||
val loc =
|
val loc =
|
||||||
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION)
|
retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION)
|
||||||
if (Geocoder.isPresent() && loc != null) {
|
if (Geocoder.isPresent() && loc != null) {
|
||||||
@ -290,9 +307,15 @@ internal data class LocalFile(
|
|||||||
loc.lastIndexOfAny(charArrayOf('+', '-')),
|
loc.lastIndexOfAny(charArrayOf('+', '-')),
|
||||||
loc.indexOf('/')
|
loc.indexOf('/')
|
||||||
).toDouble()
|
).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) {
|
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()
|
retriever.release()
|
||||||
@ -304,40 +327,48 @@ internal data class LocalFile(
|
|||||||
retriever.release()
|
retriever.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType.startsWith("image/") -> {
|
mimeType.startsWith("image/") -> {
|
||||||
val options = BitmapFactory.Options()
|
val options = BitmapFactory.Options()
|
||||||
options.inJustDecodeBounds = true
|
options.inJustDecodeBounds = true
|
||||||
BitmapFactory.decodeFile(path, options)
|
BitmapFactory.decodeFile(path, options)
|
||||||
val width = options.outWidth
|
val width = options.outWidth
|
||||||
val height = options.outHeight
|
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 {
|
try {
|
||||||
val exif = ExifInterface(path)
|
val exif = ExifInterface(path)
|
||||||
val loc = exif.latLong
|
val loc = exif.latLong
|
||||||
if (loc != null && Geocoder.isPresent()) {
|
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) {
|
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) {
|
} catch (_: IOException) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mimeType == "application/vnd.android.package-archive" -> {
|
mimeType == "application/vnd.android.package-archive" -> {
|
||||||
val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0)
|
val pkgInfo = context.packageManager.getPackageArchiveInfo(path, 0)
|
||||||
?: return metaData
|
?: return metaData.toImmutableMap()
|
||||||
metaData.add(
|
metaData[FileMetaType.AppName] =
|
||||||
R.string.file_meta_app_name to pkgInfo.applicationInfo.loadLabel(
|
pkgInfo.applicationInfo.loadLabel(context.packageManager).toString()
|
||||||
context.packageManager
|
metaData[FileMetaType.AppPackageName] = pkgInfo.packageName
|
||||||
).toString()
|
metaData[FileMetaType.AppVersion] = pkgInfo.versionName
|
||||||
)
|
metaData[FileMetaType.AppMinSdk] =
|
||||||
metaData.add(R.string.file_meta_app_pkgname to pkgInfo.packageName)
|
pkgInfo.applicationInfo.minSdkVersion.toString()
|
||||||
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
|
return metaData.toImmutableMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,9 @@ import de.mm20.launcher2.files.NextcloudFileSerializer
|
|||||||
import de.mm20.launcher2.files.R
|
import de.mm20.launcher2.files.R
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
|
|
||||||
internal data class NextcloudFile(
|
internal data class NextcloudFile(
|
||||||
val fileId: Long,
|
val fileId: Long,
|
||||||
@ -19,7 +21,7 @@ internal data class NextcloudFile(
|
|||||||
override val size: Long,
|
override val size: Long,
|
||||||
override val isDirectory: Boolean,
|
override val isDirectory: Boolean,
|
||||||
val server: String,
|
val server: String,
|
||||||
override val metaData: List<Pair<Int, String>>,
|
override val metaData: ImmutableMap<FileMetaType, String>,
|
||||||
override val labelOverride: String? = null,
|
override val labelOverride: String? = null,
|
||||||
) : File {
|
) : File {
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package de.mm20.launcher2.files.providers
|
|||||||
import de.mm20.launcher2.files.R
|
import de.mm20.launcher2.files.R
|
||||||
import de.mm20.launcher2.nextcloud.NextcloudApiHelper
|
import de.mm20.launcher2.nextcloud.NextcloudApiHelper
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@ -23,8 +25,8 @@ internal class NextcloudFileProvider(
|
|||||||
size = it.size,
|
size = it.size,
|
||||||
isDirectory = it.isDirectory,
|
isDirectory = it.isDirectory,
|
||||||
server = server,
|
server = server,
|
||||||
metaData = it.owner?.let { listOf(R.string.file_meta_owner to it) }
|
metaData = it.owner?.let { persistentMapOf(FileMetaType.Owner to it) }
|
||||||
?: emptyList()
|
?: persistentMapOf()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,18 +8,20 @@ import de.mm20.launcher2.files.OneDriveFileSerializer
|
|||||||
import de.mm20.launcher2.files.R
|
import de.mm20.launcher2.files.R
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
|
|
||||||
internal data class OneDriveFile(
|
internal data class OneDriveFile(
|
||||||
val fileId: String,
|
val fileId: String,
|
||||||
override val label: String,
|
override val label: String,
|
||||||
override val path: String,
|
override val path: String,
|
||||||
override val mimeType: String,
|
override val mimeType: String,
|
||||||
override val size: Long,
|
override val size: Long,
|
||||||
override val isDirectory: Boolean,
|
override val isDirectory: Boolean,
|
||||||
override val metaData: List<Pair<Int, String>>,
|
override val metaData: ImmutableMap<FileMetaType, String>,
|
||||||
val webUrl: String,
|
val webUrl: String,
|
||||||
override val labelOverride: String? = null,
|
override val labelOverride: String? = null,
|
||||||
) : File {
|
) : File {
|
||||||
|
|
||||||
override fun overrideLabel(label: String): OneDriveFile {
|
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.files.R
|
||||||
import de.mm20.launcher2.ktx.tryStartActivity
|
import de.mm20.launcher2.ktx.tryStartActivity
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
import de.mm20.launcher2.search.SearchableSerializer
|
import de.mm20.launcher2.search.SearchableSerializer
|
||||||
|
import kotlinx.collections.immutable.ImmutableMap
|
||||||
|
|
||||||
internal data class OwncloudFile(
|
internal data class OwncloudFile(
|
||||||
val fileId: Long,
|
val fileId: Long,
|
||||||
@ -18,7 +20,7 @@ internal data class OwncloudFile(
|
|||||||
override val size: Long,
|
override val size: Long,
|
||||||
override val isDirectory: Boolean,
|
override val isDirectory: Boolean,
|
||||||
val server: String,
|
val server: String,
|
||||||
override val metaData: List<Pair<Int, String>>,
|
override val metaData: ImmutableMap<FileMetaType, String>,
|
||||||
override val labelOverride: String? = null,
|
override val labelOverride: String? = null,
|
||||||
) : File {
|
) : File {
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package de.mm20.launcher2.files.providers
|
|||||||
import de.mm20.launcher2.files.R
|
import de.mm20.launcher2.files.R
|
||||||
import de.mm20.launcher2.owncloud.OwncloudClient
|
import de.mm20.launcher2.owncloud.OwncloudClient
|
||||||
import de.mm20.launcher2.search.File
|
import de.mm20.launcher2.search.File
|
||||||
|
import de.mm20.launcher2.search.FileMetaType
|
||||||
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
|
|
||||||
internal class OwncloudFileProvider(
|
internal class OwncloudFileProvider(
|
||||||
private val owncloudClient: OwncloudClient
|
private val owncloudClient: OwncloudClient
|
||||||
@ -19,7 +21,8 @@ internal class OwncloudFileProvider(
|
|||||||
size = it.size,
|
size = it.size,
|
||||||
isDirectory = it.isDirectory,
|
isDirectory = it.isDirectory,
|
||||||
server = server,
|
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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user