6.6.3 - Alpha5 - APK 文件类型信息及媒体文件类型信息优化对话框显示效率及信息展示逻辑
This commit is contained in:
@@ -43,6 +43,7 @@
|
|||||||
"已忽略更新记录及客户端模式连接地址记录支持清空操作",
|
"已忽略更新记录及客户端模式连接地址记录支持清空操作",
|
||||||
"版本更新信息支持多语言显示 (与当前显示语言同步)",
|
"版本更新信息支持多语言显示 (与当前显示语言同步)",
|
||||||
"使用异步加载方式一定程度提升文件管理器列表滑动流畅性",
|
"使用异步加载方式一定程度提升文件管理器列表滑动流畅性",
|
||||||
|
"APK 文件类型信息及媒体文件类型信息优化对话框显示效率及信息展示逻辑",
|
||||||
"Gradle 构建脚本提升版本自适应能力 _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
"Gradle 构建脚本提升版本自适应能力 _[`discussion #369`](http://discussions.autojs6.com/369)_"
|
||||||
],
|
],
|
||||||
"dependency": [
|
"dependency": [
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ package org.autojs.autojs.extension
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.autojs.autojs.theme.ThemeColorHelper
|
import org.autojs.autojs.theme.ThemeColorHelper
|
||||||
import org.autojs.autojs.util.ClipboardUtils
|
import org.autojs.autojs.util.ClipboardUtils
|
||||||
import org.autojs.autojs.util.IntentUtils
|
import org.autojs.autojs.util.IntentUtils
|
||||||
@@ -36,16 +40,23 @@ object MaterialDialogExtensions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MaterialDialog.setCopyableTextIfAbsent(textView: TextView, f: () -> String?) {
|
fun MaterialDialog.setCopyableTextIfAbsent(textView: TextView, textValue: String?) {
|
||||||
val textValue = f.invoke()
|
|
||||||
if (textView.text == context.getString(R.string.ellipsis_six)) {
|
if (textView.text == context.getString(R.string.ellipsis_six)) {
|
||||||
textView.text = textValue.takeUnless { it.isNullOrBlank() } ?: context.getString(R.string.text_unknown)
|
textView.text = textValue.takeUnless { it.isNullOrBlank() } ?: context.getString(R.string.text_unknown)
|
||||||
}
|
}
|
||||||
this.makeTextCopyable(textView, textValue)
|
this.makeTextCopyable(textView, textValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun MaterialDialog.setCopyableText(textView: TextView, f: () -> String?) {
|
fun MaterialDialog.setCopyableTextIfAbsent(textView: TextView, scope: CoroutineScope, f: () -> String?) {
|
||||||
val textValue = f.invoke()
|
scope.launch(Dispatchers.IO) {
|
||||||
|
val textValue = f.invoke()
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
setCopyableTextIfAbsent(textView, textValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun MaterialDialog.setCopyableText(textView: TextView, textValue: String?) {
|
||||||
textView.text = textValue.takeUnless { it.isNullOrBlank() } ?: context.getString(R.string.text_unknown)
|
textView.text = textValue.takeUnless { it.isNullOrBlank() } ?: context.getString(R.string.text_unknown)
|
||||||
this.makeTextCopyable(textView, textValue)
|
this.makeTextCopyable(textView, textValue)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ object ColorInfoDialogManager {
|
|||||||
|
|
||||||
private suspend fun TextView.bindWith(dialog: MaterialDialog, text: String) {
|
private suspend fun TextView.bindWith(dialog: MaterialDialog, text: String) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
dialog.setCopyableText(this@bindWith) { text }
|
dialog.setCopyableText(this@bindWith, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package org.autojs.autojs.ui.main.scripts
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageInfo
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.GET_META_DATA
|
import android.content.pm.PackageManager.GET_META_DATA
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@@ -14,7 +16,11 @@ import com.afollestad.materialdialogs.MaterialDialog
|
|||||||
import com.android.apksig.ApkVerifier
|
import com.android.apksig.ApkVerifier
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.selects.select
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.dongliu.apk.parser.ApkFile
|
import net.dongliu.apk.parser.ApkFile
|
||||||
import org.autojs.autojs.extension.MaterialDialogExtensions.makeSettingsLaunchable
|
import org.autojs.autojs.extension.MaterialDialogExtensions.makeSettingsLaunchable
|
||||||
@@ -38,6 +44,10 @@ object ApkInfoDialogManager {
|
|||||||
fun showApkInfoDialog(context: Context, apkFile: File) {
|
fun showApkInfoDialog(context: Context, apkFile: File) {
|
||||||
val binding = ApkFileInfoDialogListItemBinding.inflate(LayoutInflater.from(context))
|
val binding = ApkFileInfoDialogListItemBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
// Create an independent Scope for the Dialog, bind its lifecycle with the Dialog.
|
||||||
|
// zh-CN: 针对 Dialog 独立创建一个 Scope, 生命周期与 Dialog 绑定.
|
||||||
|
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||||
|
|
||||||
val apkFilePath = apkFile.absolutePath
|
val apkFilePath = apkFile.absolutePath
|
||||||
val packageManager = context.packageManager
|
val packageManager = context.packageManager
|
||||||
|
|
||||||
@@ -63,101 +73,118 @@ object ApkInfoDialogManager {
|
|||||||
.neutralColorRes(R.color.dialog_button_hint)
|
.neutralColorRes(R.color.dialog_button_hint)
|
||||||
.onNegative { materialDialog, _ -> materialDialog.dismiss() }
|
.onNegative { materialDialog, _ -> materialDialog.dismiss() }
|
||||||
.show()
|
.show()
|
||||||
|
.apply {
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
setOnDismissListener { scope.cancel() }
|
||||||
val packageInfo = withContext(Dispatchers.IO) {
|
makeTextCopyable { it.titleView }
|
||||||
runCatching { packageManager.getPackageArchiveInfo(apkFilePath, GET_META_DATA) }.getOrNull()
|
|
||||||
}
|
}
|
||||||
val applicationInfo = packageInfo?.applicationInfo
|
|
||||||
|
|
||||||
val packageName = packageInfo?.packageName
|
scope.launch {
|
||||||
val versionName = packageInfo?.versionName
|
val apkInfoDeferred = async(Dispatchers.IO) { getApkInfo(apkFile) }
|
||||||
val versionCode = packageInfo?.let {
|
val packageInfoDeferred = async(Dispatchers.IO) { getPackageInfo(packageManager, apkFilePath) }
|
||||||
when {
|
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> it.longVersionCode
|
val firstPackageName: String? = select {
|
||||||
else -> @Suppress("DEPRECATION") it.versionCode.toLong()
|
apkInfoDeferred.onAwait { it?.packageName }
|
||||||
|
packageInfoDeferred.onAwait { it?.packageName }
|
||||||
|
}
|
||||||
|
val packageName: String? = firstPackageName ?: when {
|
||||||
|
apkInfoDeferred.isCompleted -> packageInfoDeferred.await()?.packageName
|
||||||
|
else -> apkInfoDeferred.await()?.packageName
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Hint by SuperMonster003 on Nov 27, 2024.
|
||||||
|
// ! Prioritize handling "installed version" to determine whether to display its content view.
|
||||||
|
// ! This is the only content view that needs to be considered before displaying the dialog.
|
||||||
|
// ! It is now safe to display the dialog immediately as all content views have placeholders.
|
||||||
|
// ! zh-CN:
|
||||||
|
// ! 优先处理 "已安装版本", 决定是否显示其内容视图.
|
||||||
|
// ! 这是唯一一个在显示对话框之前需要考虑的内容视图.
|
||||||
|
// ! 此时可立即安全显示对话框, 因所有内容视图均已完成占位.
|
||||||
|
val installedPackageName: String? = packageName?.also { pkg ->
|
||||||
|
AppUtils.getInstalledVersionInfo(pkg)?.let { versionInfo ->
|
||||||
|
binding.installedVersionParent.isVisible = true
|
||||||
|
dialog.setCopyableTextIfAbsent(
|
||||||
|
binding.installedVersionValue,
|
||||||
|
context.getString(R.string.text_full_version_info, versionInfo.versionName, versionInfo.versionCode),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fileSize = withContext(Dispatchers.IO) { PFiles.getHumanReadableSize(apkFile.length()) }
|
restoreEssentialViews(binding, context)
|
||||||
|
updateGuidelines(binding)
|
||||||
|
|
||||||
val signatureScheme = withContext(Dispatchers.IO) { getApkSignatureInfo(apkFile) }
|
dialog.setCopyableTextIfAbsent(binding.packageNameValue, packageName)
|
||||||
|
dialog.setCopyableTextIfAbsent(binding.deviceSdkValue, "${Build.VERSION.SDK_INT}")
|
||||||
|
dialog.setCopyableTextIfAbsent(binding.fileSizeValue, this) { PFiles.getHumanReadableSize(apkFile.length()) }
|
||||||
|
dialog.setCopyableTextIfAbsent(binding.signatureSchemeValue, this) { getApkSignatureInfo(apkFile) }
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
launch(Dispatchers.IO) {
|
||||||
// @Hint by SuperMonster003 on Nov 27, 2024.
|
val apkInfo = apkInfoDeferred.await()
|
||||||
// ! Prioritize handling "installed version" to determine whether to display its content view.
|
|
||||||
// ! This is the only content view that needs to be considered before displaying the dialog.
|
withContext(Dispatchers.Main) {
|
||||||
// ! It is now safe to display the dialog immediately as all content views have placeholders.
|
dialog.setCopyableTextIfAbsent(binding.labelNameValue, apkInfo?.label)
|
||||||
// ! zh-CN:
|
dialog.setCopyableTextIfAbsent(binding.minSdkValue, apkInfo?.minSdkVersion?.toString())
|
||||||
// ! 优先处理 "已安装版本", 决定是否显示其内容视图.
|
dialog.setCopyableTextIfAbsent(binding.targetSdkValue, apkInfo?.targetSdkVersion?.toString())
|
||||||
// ! 这是唯一一个在显示对话框之前需要考虑的内容视图.
|
|
||||||
// ! 此时可立即安全显示对话框, 因所有内容视图均已完成占位.
|
if (apkInfo != null) dialog.getActionButton(DialogAction.NEUTRAL).let { neutralButton ->
|
||||||
val installedPackageName: String? = packageName?.let { pkg ->
|
neutralButton.isVisible = true
|
||||||
AppUtils.getInstalledVersionInfo(pkg)?.let { versionInfo ->
|
neutralButton.text = "Manifest"
|
||||||
binding.installedVersionParent.isVisible = true
|
neutralButton.setOnClickListener { DisplayManifestActivity.launch(context, apkInfo.manifestXml, apkInfo.usesPermissions) }
|
||||||
dialog.setCopyableTextIfAbsent(binding.installedVersionValue) { context.getString(R.string.text_full_version_info, versionInfo.versionName, versionInfo.versionCode) }
|
|
||||||
pkg /* returns as installedPackageName */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
restoreEssentialViews(binding, context)
|
launch(Dispatchers.IO) {
|
||||||
updateGuidelines(binding)
|
val packageInfo = packageInfoDeferred.await()
|
||||||
|
val applicationInfo = packageInfo?.applicationInfo
|
||||||
when {
|
val versionName = packageInfo?.versionName
|
||||||
versionName != null -> {
|
val versionCode = packageInfo?.let {
|
||||||
binding.versionPlaceholderLabel.text = context.getString(R.string.text_version)
|
when {
|
||||||
dialog.setCopyableTextIfAbsent(binding.versionPlaceholderValue) { context.getString(R.string.text_full_version_info, versionName, versionCode) }
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> it.longVersionCode
|
||||||
|
else -> @Suppress("DEPRECATION") it.versionCode.toLong()
|
||||||
}
|
}
|
||||||
versionCode != null -> {
|
|
||||||
binding.versionPlaceholderLabel.text = context.getString(R.string.text_version_code)
|
|
||||||
dialog.setCopyableTextIfAbsent(binding.versionPlaceholderValue) { "$versionCode" }
|
|
||||||
}
|
|
||||||
else -> dialog.setCopyableTextIfAbsent(binding.versionPlaceholderValue) { null }
|
|
||||||
}
|
}
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
when {
|
||||||
|
versionName != null -> {
|
||||||
|
binding.versionPlaceholderLabel.text = context.getString(R.string.text_version)
|
||||||
|
dialog.setCopyableTextIfAbsent(
|
||||||
|
binding.versionPlaceholderValue,
|
||||||
|
context.getString(R.string.text_full_version_info, versionName, versionCode),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
versionCode != null -> {
|
||||||
|
binding.versionPlaceholderLabel.text = context.getString(R.string.text_version_code)
|
||||||
|
dialog.setCopyableTextIfAbsent(binding.versionPlaceholderValue, "$versionCode")
|
||||||
|
}
|
||||||
|
else -> dialog.setCopyableTextIfAbsent(binding.versionPlaceholderValue, null)
|
||||||
|
}
|
||||||
|
|
||||||
dialog.setCopyableTextIfAbsent(binding.packageNameValue) { packageName }
|
dialog.setIcon(applicationInfo?.apply {
|
||||||
dialog.setCopyableTextIfAbsent(binding.deviceSdkValue) { "${Build.VERSION.SDK_INT}" }
|
sourceDir = apkFilePath
|
||||||
dialog.setCopyableTextIfAbsent(binding.fileSizeValue) { fileSize }
|
publicSourceDir = apkFilePath
|
||||||
dialog.setCopyableTextIfAbsent(binding.signatureSchemeValue) { signatureScheme }
|
}?.loadIcon(packageManager) ?: AppCompatResources.getDrawable(context, R.drawable.ic_packaging))
|
||||||
|
dialog.makeSettingsLaunchable({ it.iconView }, installedPackageName)
|
||||||
dialog.setIcon(applicationInfo?.apply {
|
|
||||||
sourceDir = apkFilePath
|
|
||||||
publicSourceDir = apkFilePath
|
|
||||||
}?.loadIcon(packageManager) ?: AppCompatResources.getDrawable(context, R.drawable.ic_packaging))
|
|
||||||
|
|
||||||
dialog.makeSettingsLaunchable({ it.iconView }, installedPackageName)
|
|
||||||
dialog.makeTextCopyable { it.titleView }
|
|
||||||
|
|
||||||
val apkInfo = getApkInfo(apkFile)
|
|
||||||
|
|
||||||
dialog.setCopyableTextIfAbsent(binding.labelNameValue) { apkInfo?.label }
|
|
||||||
dialog.setCopyableTextIfAbsent(binding.packageNameValue) { apkInfo?.packageName }
|
|
||||||
dialog.setCopyableTextIfAbsent(binding.minSdkValue) { apkInfo?.minSdkVersion?.toString() }
|
|
||||||
dialog.setCopyableTextIfAbsent(binding.targetSdkValue) { apkInfo?.targetSdkVersion?.toString() }
|
|
||||||
|
|
||||||
if (apkInfo != null) dialog.getActionButton(DialogAction.NEUTRAL).let { neutralButton ->
|
|
||||||
neutralButton.isVisible = true
|
|
||||||
neutralButton.text = "Manifest"
|
|
||||||
neutralButton.setOnClickListener { DisplayManifestActivity.launch(context, apkInfo.manifestXml, apkInfo.usesPermissions) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getApkInfo(apkFile: File): ApkInfo? = withContext(Dispatchers.IO) {
|
private fun getApkInfo(apkFile: File): ApkInfo? = runCatching {
|
||||||
runCatching {
|
ApkFile(apkFile).use { parser ->
|
||||||
ApkFile(apkFile).use { parser ->
|
val meta = runCatching { parser.apkMeta }.getOrNull()
|
||||||
val meta = runCatching { parser.apkMeta }.getOrNull()
|
val label = meta?.label
|
||||||
val label = meta?.label
|
val packageName = meta?.packageName
|
||||||
val packageName = meta?.packageName
|
val minSdkVersion = meta?.minSdkVersion?.toIntOrNull()
|
||||||
val minSdkVersion = meta?.minSdkVersion?.toIntOrNull()
|
val targetSdkVersion = meta?.targetSdkVersion?.toIntOrNull()
|
||||||
val targetSdkVersion = meta?.targetSdkVersion?.toIntOrNull()
|
val usesPermissions = meta?.usesPermissions ?: emptyList()
|
||||||
val usesPermissions = meta?.usesPermissions ?: emptyList()
|
val manifestXml = parser.manifestXml
|
||||||
val manifestXml = parser.manifestXml
|
ApkInfo(label, packageName, minSdkVersion, targetSdkVersion, usesPermissions, manifestXml)
|
||||||
ApkInfo(label, packageName, minSdkVersion, targetSdkVersion, usesPermissions, manifestXml)
|
}
|
||||||
}
|
}.getOrNull()
|
||||||
}.getOrNull()
|
|
||||||
}
|
private fun getPackageInfo(packageManager: PackageManager, apkFilePath: String): PackageInfo? = runCatching {
|
||||||
|
packageManager.getPackageArchiveInfo(apkFilePath, GET_META_DATA)
|
||||||
|
}.getOrNull()
|
||||||
|
|
||||||
private fun restoreEssentialViews(binding: ApkFileInfoDialogListItemBinding, context: Context) {
|
private fun restoreEssentialViews(binding: ApkFileInfoDialogListItemBinding, context: Context) {
|
||||||
listOf(
|
listOf(
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ abstract class BaseDisplayContentActivity : BaseActivity() {
|
|||||||
// ! zh-CN: 事实上, 此处依然存疑.
|
// ! zh-CN: 事实上, 此处依然存疑.
|
||||||
// ! Reference: https://stackoverflow.com/questions/3866499/two-directional-scroll-view
|
// ! Reference: https://stackoverflow.com/questions/3866499/two-directional-scroll-view
|
||||||
// !
|
// !
|
||||||
|
|
||||||
super.onTouchEvent(event) or view.onTouchEvent(event)
|
super.onTouchEvent(event) or view.onTouchEvent(event)
|
||||||
}
|
}
|
||||||
else -> super.onTouchEvent(event)
|
else -> super.onTouchEvent(event)
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import com.afollestad.materialdialogs.DialogAction
|
|||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.autojs.autojs.extension.MaterialDialogExtensions.makeTextCopyable
|
import org.autojs.autojs.extension.MaterialDialogExtensions.makeTextCopyable
|
||||||
@@ -35,6 +36,10 @@ object MediaInfoDialogManager {
|
|||||||
fun showMediaInfoDialog(context: Context, explorerItem: ExplorerItem) {
|
fun showMediaInfoDialog(context: Context, explorerItem: ExplorerItem) {
|
||||||
val binding = MediaFileInfoDialogListItemBinding.inflate(LayoutInflater.from(context))
|
val binding = MediaFileInfoDialogListItemBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
// Create an independent Scope for the Dialog, bind its lifecycle with the Dialog.
|
||||||
|
// zh-CN: 针对 Dialog 独立创建一个 Scope, 生命周期与 Dialog 绑定.
|
||||||
|
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
|
||||||
|
|
||||||
val dialog = MaterialDialog.Builder(context)
|
val dialog = MaterialDialog.Builder(context)
|
||||||
.title(explorerItem.name)
|
.title(explorerItem.name)
|
||||||
.customView(binding.root, false)
|
.customView(binding.root, false)
|
||||||
@@ -48,60 +53,42 @@ object MediaInfoDialogManager {
|
|||||||
.neutralText(R.string.ellipsis_six)
|
.neutralText(R.string.ellipsis_six)
|
||||||
.neutralColorRes(R.color.dialog_button_unavailable)
|
.neutralColorRes(R.color.dialog_button_unavailable)
|
||||||
.show()
|
.show()
|
||||||
|
.apply { setOnDismissListener { scope.cancel() } }
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
scope.launch {
|
||||||
val mediaInfo = MediaInfo()
|
val mediaInfo = MediaInfo()
|
||||||
val filePath = explorerItem.path
|
val filePath = explorerItem.path
|
||||||
|
|
||||||
val deferredVideoFormat = async(Dispatchers.IO) { mediaInfo(filePath, VIDEO, "Format") }
|
val videoFormatDeferred = async(Dispatchers.IO) { mediaInfo(filePath, VIDEO, "Format") }
|
||||||
val deferredAudioFormat = async(Dispatchers.IO) { mediaInfo(filePath, AUDIO, "Format") }
|
val audioFormatDeferred = async(Dispatchers.IO) { mediaInfo(filePath, AUDIO, "Format") }
|
||||||
val deferredContainerFormat = async(Dispatchers.IO) { mediaInfo(filePath, GENERAL, "Format") }
|
val containerFormatDeferred = async(Dispatchers.IO) { mediaInfo(filePath, GENERAL, "Format") }
|
||||||
val deferredMediaInfoTrimmed = async(Dispatchers.IO) { mediaInfo.getMediaInfoTrimmed(explorerItem.path) }
|
val mediaInfoTrimmedDeferred = async(Dispatchers.IO) { mediaInfo.getMediaInfoTrimmed(explorerItem.path) }
|
||||||
|
|
||||||
launch(Dispatchers.IO) {
|
launch(Dispatchers.IO) {
|
||||||
mediaInfo(filePath, GENERAL, "FileSize/String").let {
|
mediaInfo(filePath, GENERAL, "FileSize/String").let { binding.fileSizeValue.bindWith(dialog, it) }
|
||||||
binding.fileSizeValue.bindWith(dialog, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
mediaInfo(filePath, GENERAL, "Duration/String").let { binding.durationValue.bindWith(dialog, it) }
|
mediaInfo(filePath, GENERAL, "Duration/String").let { binding.durationValue.bindWith(dialog, it) }
|
||||||
}
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
mediaInfo(filePath, GENERAL, "Album").let { binding.albumValue.bindWith(dialog, it) }
|
|
||||||
}
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
mediaInfo(filePath, GENERAL, "Track").let { binding.trackNameValue.bindWith(dialog, it) }
|
|
||||||
}
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
mediaInfo(filePath, GENERAL, "Performer").let { binding.performerValue.bindWith(dialog, it) }
|
|
||||||
}
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
mediaInfo(filePath, AUDIO, "BitRate/String").let { binding.bitRateForAudioValue.bindWith(dialog, it) }
|
mediaInfo(filePath, AUDIO, "BitRate/String").let { binding.bitRateForAudioValue.bindWith(dialog, it) }
|
||||||
}
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
mediaInfo(filePath, VIDEO, "BitRate/String").let { binding.bitRateForVideoValue.bindWith(dialog, it) }
|
mediaInfo(filePath, VIDEO, "BitRate/String").let { binding.bitRateForVideoValue.bindWith(dialog, it) }
|
||||||
}
|
mediaInfo(filePath, GENERAL, "Track").let { binding.trackNameValue.bindWith(dialog, it) }
|
||||||
launch(Dispatchers.IO) {
|
mediaInfo(filePath, GENERAL, "Performer").let { binding.performerValue.bindWith(dialog, it) }
|
||||||
|
mediaInfo(filePath, GENERAL, "Album").let { binding.albumValue.bindWith(dialog, it) }
|
||||||
|
run {
|
||||||
|
val width = mediaInfo(filePath, VIDEO, "Width")
|
||||||
|
val height = mediaInfo(filePath, VIDEO, "Height")
|
||||||
|
binding.resolutionValue.bindWith(dialog, if (width.isNotEmpty() && height.isNotEmpty()) "$width × $height" else "")
|
||||||
|
}
|
||||||
mediaInfo(filePath, VIDEO, "DisplayAspectRatio/String").let { binding.aspectRatioValue.bindWith(dialog, it) }
|
mediaInfo(filePath, VIDEO, "DisplayAspectRatio/String").let { binding.aspectRatioValue.bindWith(dialog, it) }
|
||||||
}
|
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
mediaInfo(filePath, VIDEO, "FrameRate").let { binding.frameRateValue.bindWith(dialog, it) }
|
mediaInfo(filePath, VIDEO, "FrameRate").let { binding.frameRateValue.bindWith(dialog, it) }
|
||||||
}
|
}
|
||||||
launch(Dispatchers.IO) {
|
|
||||||
val deferredVideoWidth = async(Dispatchers.IO) { mediaInfo(filePath, VIDEO, "Width") }
|
|
||||||
val deferredVideoHeight = async(Dispatchers.IO) { mediaInfo(filePath, VIDEO, "Height") }
|
|
||||||
val (width, height) = awaitAll(deferredVideoWidth, deferredVideoHeight)
|
|
||||||
binding.resolutionValue.bindWith(dialog, if (width.isNotEmpty() && height.isNotEmpty()) "$width × $height" else "")
|
|
||||||
}
|
|
||||||
|
|
||||||
when {
|
when {
|
||||||
deferredVideoFormat.await().isNotEmpty() -> {
|
videoFormatDeferred.await().isNotEmpty() -> {
|
||||||
setViewsAsVideoPlaceholder(binding)
|
setViewsAsVideoPlaceholder(binding)
|
||||||
dialog.setIcon(R.drawable.ic_movie)
|
dialog.setIcon(R.drawable.ic_movie)
|
||||||
dialog.getActionButton(DialogAction.POSITIVE)
|
dialog.getActionButton(DialogAction.POSITIVE)
|
||||||
setDialogPlayable(dialog, context, explorerItem)
|
setDialogPlayable(dialog, context, explorerItem)
|
||||||
}
|
}
|
||||||
deferredAudioFormat.await().isNotEmpty() -> {
|
audioFormatDeferred.await().isNotEmpty() -> {
|
||||||
setViewsAsMediaPlaceholder(binding)
|
setViewsAsMediaPlaceholder(binding)
|
||||||
dialog.setIcon(R.drawable.ic_voice_note)
|
dialog.setIcon(R.drawable.ic_voice_note)
|
||||||
setDialogPlayable(dialog, context, explorerItem)
|
setDialogPlayable(dialog, context, explorerItem)
|
||||||
@@ -126,7 +113,7 @@ object MediaInfoDialogManager {
|
|||||||
dialog.makeTextCopyable { it.titleView }
|
dialog.makeTextCopyable { it.titleView }
|
||||||
}
|
}
|
||||||
|
|
||||||
when (val containerFormat = deferredContainerFormat.await()) {
|
when (val containerFormat = containerFormatDeferred.await()) {
|
||||||
MEDIA_INFO_ERROR_OPENING_FILE -> {
|
MEDIA_INFO_ERROR_OPENING_FILE -> {
|
||||||
dialog.setContent(MEDIA_INFO_ERROR_OPENING_FILE)
|
dialog.setContent(MEDIA_INFO_ERROR_OPENING_FILE)
|
||||||
dialog.getActionButton(DialogAction.NEUTRAL).let {
|
dialog.getActionButton(DialogAction.NEUTRAL).let {
|
||||||
@@ -136,12 +123,12 @@ object MediaInfoDialogManager {
|
|||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
containerFormat.let { binding.containerFormatValue.bindWith(dialog, it) }
|
containerFormat.let { binding.containerFormatValue.bindWith(dialog, it) }
|
||||||
deferredAudioFormat.await().let { binding.audioFormatValue.bindWith(dialog, it) }
|
audioFormatDeferred.await().let { binding.audioFormatValue.bindWith(dialog, it) }
|
||||||
deferredVideoFormat.await().let { binding.videoFormatValue.bindWith(dialog, it) }
|
videoFormatDeferred.await().let { binding.videoFormatValue.bindWith(dialog, it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDialogNeutralButton(dialog, context, deferredMediaInfoTrimmed.await())
|
updateDialogNeutralButton(dialog, context, mediaInfoTrimmedDeferred.await())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,7 +242,7 @@ object MediaInfoDialogManager {
|
|||||||
|
|
||||||
private suspend fun TextView.bindWith(dialog: MaterialDialog, text: String) {
|
private suspend fun TextView.bindWith(dialog: MaterialDialog, text: String) {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
dialog.setCopyableText(this@bindWith) { text }
|
dialog.setCopyableText(this@bindWith, text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#Wed May 21 13:21:19 CST 2025
|
#Wed May 21 19:59:17 CST 2025
|
||||||
BUILD_TIME=1747804879230
|
BUILD_TIME=1747828757164
|
||||||
COMPILE_SDK_VERSION=35
|
COMPILE_SDK_VERSION=35
|
||||||
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
IMAGE_QUANT_CMAKE_VERSION=3.22.1
|
||||||
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
IMAGE_QUANT_NDK_VERSION=26.1.10909125
|
||||||
@@ -19,6 +19,6 @@ RAPID_OCR_OPENCV_MOBILE_LABEL_VERSION=13
|
|||||||
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
RAPID_OCR_OPENCV_MOBILE_VERSION=4.5.3
|
||||||
TARGET_SDK_VERSION=35
|
TARGET_SDK_VERSION=35
|
||||||
TARGET_SDK_VERSION_INRT=29
|
TARGET_SDK_VERSION_INRT=29
|
||||||
VERSION_BUILD=3240
|
VERSION_BUILD=3241
|
||||||
VERSION_NAME=6.6.3 Alpha5
|
VERSION_NAME=6.6.3 Alpha5
|
||||||
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
VSCODE_EXT_REQUIRED_VERSION=1.0.8
|
||||||
|
|||||||
Reference in New Issue
Block a user