diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt b/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt index d6525527..9fa03b86 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt @@ -34,7 +34,7 @@ class UnprivilegedEuiccManagementFragment : EuiccManagementFragment() { override fun onPrepareOptionsMenu(menu: Menu) { super.onPrepareOptionsMenu(menu) menu.findItem(R.id.open_sim_toolkit).apply { - intent = stk[slotId]?.intent + intent = stk[slotId] isVisible = intent != null } } diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt b/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt index a4a27a19..4fe52b37 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt @@ -3,7 +3,6 @@ package im.angry.openeuicc.util import android.content.ComponentName import android.content.Context import android.content.Intent -import android.content.pm.ActivityInfo import android.content.pm.PackageManager import android.net.Uri import android.provider.Settings @@ -13,84 +12,52 @@ import im.angry.openeuicc.core.EuiccChannelManager class SIMToolkit(private val context: Context) { private val slots = buildMap { - fun getComponentNames(@ArrayRes id: Int) = context.resources - .getStringArray(id).mapNotNull(ComponentName::unflattenFromString).toSet() - put(-1, getComponentNames(R.array.sim_toolkit_slot_selection)) - put(0, getComponentNames(R.array.sim_toolkit_slot_1)) - put(1, getComponentNames(R.array.sim_toolkit_slot_2)) + fun getIntents(@ArrayRes id: Int) = context.resources.getStringArray(id) + .mapNotNull(ComponentName::unflattenFromString) + .map(Intent::makeMainActivity) + put(-1, getIntents(R.array.sim_toolkit_slot_selection)) + put(0, getIntents(R.array.sim_toolkit_slot_1)) + put(1, getIntents(R.array.sim_toolkit_slot_2)) } val intents: Iterable - get() = listOf(get(0)?.intent, get(1)?.intent) + get() = listOf(get(0), get(1)) - operator fun get(slotId: Int): Slot? = when (slotId) { - -1, EuiccChannelManager.USB_CHANNEL_ID -> null - else -> Slot(context.packageManager, buildSet { - addAll(slots.getOrDefault(slotId, emptySet())) - addAll(slots.getOrDefault(-1, emptySet())) - }) + operator fun get(slotId: Int): Intent? { + if (slotId == -1 || slotId == EuiccChannelManager.USB_CHANNEL_ID) return null + val intents = (slots[slotId] ?: emptyList()) + slots[-1]!! + val packageNames = intents.mapNotNull(Intent::getPackage).toSet() + return getIntent(context.packageManager, intents) // try to find an exported activity first + ?: getLaunchIntent(context.packageManager, packageNames) // fallback to launch intent + ?: getDisabledPackageIntent(context.packageManager, packageNames) // app settings if disabled } - data class Slot(private val packageManager: PackageManager, private val components: Set) { - private val packageNames: Iterable - get() = components.map(ComponentName::getPackageName).toSet() - .filter(packageManager::isInstalledApp) - - private val launchIntent: Intent? - get() = packageNames.firstNotNullOfOrNull(packageManager::getLaunchIntentForPackage) - - private val activities: Iterable - get() = packageNames.flatMap(packageManager::getActivities) - .filter(ActivityInfo::exported).map { ComponentName(it.packageName, it.name) } - - private fun getActivityIntent(): Intent? { - for (activity in activities) { - if (!components.contains(activity)) continue - if (isDisabledState(packageManager.getComponentEnabledSetting(activity))) continue - return Intent.makeMainActivity(activity) - } - return launchIntent - } - - private fun getDisabledPackageIntent(): Intent? { - val disabledPackageName = packageNames - .find { isDisabledState(packageManager.getApplicationEnabledSetting(it)) } - ?: return null - val uri = Uri.fromParts("package", disabledPackageName, null) - return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri) - } - - val intent: Intent? - get() { - val intent = getActivityIntent() ?: getDisabledPackageIntent() ?: return null - if (intent.resolveActivity(packageManager) == null) return null - return intent - } - } - - fun isSelection(intent: Intent) = - slots.getOrDefault(-1, emptySet()).contains(intent.component) + fun isSelection(intent: Intent) = intent in slots[-1]!! companion object { fun getDisabledPackageName(intent: Intent?): String? { if (intent?.action != Settings.ACTION_APPLICATION_DETAILS_SETTINGS) return null - return intent.data?.schemeSpecificPart + return intent.data!!.schemeSpecificPart } } } -private fun isDisabledState(state: Int) = when (state) { - PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> true - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true - else -> false + +private fun getIntent(packageManager: PackageManager, intents: Iterable) = + intents.firstOrNull { it.resolveActivityInfo(packageManager, 0)?.exported ?: false } + +private fun getLaunchIntent(packageManager: PackageManager, packageNames: Iterable) = + packageNames.firstNotNullOfOrNull(packageManager::getLaunchIntentForPackage) + +private fun getDisabledPackageIntent(packageManager: PackageManager, packageNames: Iterable): Intent? { + val packageName = packageNames.firstOrNull(packageManager::isDisabledState) ?: return null + val uri = Uri.fromParts("package", packageName, null) + return Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri) } -private fun PackageManager.isInstalledApp(packageName: String) = try { - getPackageInfo(packageName, 0) - true -} catch (_: PackageManager.NameNotFoundException) { - false -} - -private fun PackageManager.getActivities(packageName: String) = - getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).activities?.toList() ?: emptyList() +private fun PackageManager.isDisabledState(packageName: String) = + when (getApplicationEnabledSetting(packageName)) { + PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> true + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true + else -> false + } diff --git a/app-unpriv/src/main/res/values-zh-rCN/strings.xml b/app-unpriv/src/main/res/values-zh-rCN/strings.xml index cd66a435..51c47125 100644 --- a/app-unpriv/src/main/res/values-zh-rCN/strings.xml +++ b/app-unpriv/src/main/res/values-zh-rCN/strings.xml @@ -2,7 +2,7 @@ 兼容性检查 打开 SIM 卡应用程序 ARA-M SHA-1 已拷贝到剪贴板 - 请启用您的“%s”应用程序 + 请启用您的 “%s” 应用程序 简易兼容性检测 您的手机可以管理兼容 %s 的卡片 您的手机与 %s 不兼容 @@ -14,4 +14,4 @@ 继续 不再显示此消息 未知 - \ No newline at end of file + diff --git a/app-unpriv/src/main/res/values-zh-rTW/strings.xml b/app-unpriv/src/main/res/values-zh-rTW/strings.xml index b668cba0..71d7b19d 100644 --- a/app-unpriv/src/main/res/values-zh-rTW/strings.xml +++ b/app-unpriv/src/main/res/values-zh-rTW/strings.xml @@ -2,7 +2,7 @@ 相容性檢查 啟動 SIM 卡應用程式 ARA-M SHA-1 已複製到剪貼簿 - 請啟用您的“%s”應用程式 + 請啟用您的「%s」應用程式 簡易相容性檢測 您的手機可以管理相容 %s 的卡片 您的手機與 %s 不相容 @@ -14,4 +14,4 @@ 繼續 不再顯示此訊息 未知 - \ No newline at end of file + diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index 3cb2885d..b0c0c630 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -4,8 +4,8 @@ SIM %d, SE %d Compatibility Check Open SIM Toolkit - SIM Toolkit - SIM Toolkit #%d + SIM Toolkit + SIM Toolkit #%d ARA-M SHA-1