ソースを参照

refactor: simplify sim toolkit design (#296)

Reviewed-on: https://gitea.angry.im/PeterCxy/OpenEUICC/pulls/296
Co-authored-by: septs <github@septs.pw>
Co-committed-by: septs <github@septs.pw>
septs 1 ヶ月 前
コミット
419db21b12

+ 1 - 1
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
         }
     }

+ 33 - 66
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<Intent?>
-        get() = listOf(get(0)?.intent, get(1)?.intent)
-
-    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()))
-        })
-    }
-
-    data class Slot(private val packageManager: PackageManager, private val components: Set<ComponentName>) {
-        private val packageNames: Iterable<String>
-            get() = components.map(ComponentName::getPackageName).toSet()
-                .filter(packageManager::isInstalledApp)
-
-        private val launchIntent: Intent?
-            get() = packageNames.firstNotNullOfOrNull(packageManager::getLaunchIntentForPackage)
-
-        private val activities: Iterable<ComponentName>
-            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
-            }
+        get() = listOf(get(0), get(1))
+
+    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
     }
 
-    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 PackageManager.isInstalledApp(packageName: String) = try {
-    getPackageInfo(packageName, 0)
-    true
-} catch (_: PackageManager.NameNotFoundException) {
-    false
+private fun getIntent(packageManager: PackageManager, intents: Iterable<Intent>) =
+    intents.firstOrNull { it.resolveActivityInfo(packageManager, 0)?.exported ?: false }
+
+private fun getLaunchIntent(packageManager: PackageManager, packageNames: Iterable<String>) =
+    packageNames.firstNotNullOfOrNull(packageManager::getLaunchIntentForPackage)
+
+private fun getDisabledPackageIntent(packageManager: PackageManager, packageNames: Iterable<String>): 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.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
+    }

+ 2 - 2
app-unpriv/src/main/res/values-zh-rCN/strings.xml

@@ -2,7 +2,7 @@
     <string name="compatibility_check">兼容性检查</string>
     <string name="open_sim_toolkit">打开 SIM 卡应用程序</string>
     <string name="toast_ara_m_copied">ARA-M SHA-1 已拷贝到剪贴板</string>
-    <string name="toast_prompt_to_enable_sim_toolkit">请启用您的“%s”应用程序</string>
+    <string name="toast_prompt_to_enable_sim_toolkit">请启用您的 “%s” 应用程序</string>
     <string name="quick_compatibility">简易兼容性检测</string>
     <string name="quick_compatibility_compatible">您的手机可以管理兼容 %s 的卡片</string>
     <string name="quick_compatibility_not_compatible">您的手机与 %s 不兼容</string>
@@ -14,4 +14,4 @@
     <string name="quick_compatibility_button_continue">继续</string>
     <string name="quick_compatibility_skip">不再显示此消息</string>
     <string name="quick_compatibility_unknown">未知</string>
-</resources>
+</resources>

+ 2 - 2
app-unpriv/src/main/res/values-zh-rTW/strings.xml

@@ -2,7 +2,7 @@
     <string name="compatibility_check">相容性檢查</string>
     <string name="open_sim_toolkit">啟動 SIM 卡應用程式</string>
     <string name="toast_ara_m_copied">ARA-M SHA-1 已複製到剪貼簿</string>
-    <string name="toast_prompt_to_enable_sim_toolkit">請啟用您的“%s”應用程式</string>
+    <string name="toast_prompt_to_enable_sim_toolkit">請啟用您的「%s」應用程式</string>
     <string name="quick_compatibility">簡易相容性檢測</string>
     <string name="quick_compatibility_compatible">您的手機可以管理相容 %s 的卡片</string>
     <string name="quick_compatibility_not_compatible">您的手機與 %s 不相容</string>
@@ -14,4 +14,4 @@
     <string name="quick_compatibility_button_continue">繼續</string>
     <string name="quick_compatibility_skip">不再顯示此訊息</string>
     <string name="quick_compatibility_unknown">未知</string>
-</resources>
+</resources>

+ 2 - 2
app-unpriv/src/main/res/values/strings.xml

@@ -4,8 +4,8 @@
     <string name="channel_name_format_unpriv_se" translatable="false">SIM %d, SE %d</string>
     <string name="compatibility_check">Compatibility Check</string>
     <string name="open_sim_toolkit">Open SIM Toolkit</string>
-    <string name="shortcut_sim_toolkit">SIM Toolkit</string>
-    <string name="shortcut_sim_toolkit_with_slot">SIM Toolkit #%d</string>
+    <string name="shortcut_sim_toolkit" translatable="false">SIM Toolkit</string>
+    <string name="shortcut_sim_toolkit_with_slot" translatable="false">SIM Toolkit #%d</string>
 
     <!-- Settings -->
     <string name="pref_developer_ara_m" translatable="false">ARA-M SHA-1</string>