refactor: simplify sim toolkit design #296
5 changed files with 40 additions and 73 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
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<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
|
||||
}
|
||||
}
|
||||
|
||||
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<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.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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue