Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 419db21b authored by septs's avatar septs Committed by Peter Cai
Browse files

refactor: simplify sim toolkit design (#296)

parent 94963701
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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
Original line number Diff line number Diff line
@@ -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)
    fun isSelection(intent: Intent) = intent in slots[-1]!!

        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)
    companion object {
        fun getDisabledPackageName(intent: Intent?): String? {
            if (intent?.action != Settings.ACTION_APPLICATION_DETAILS_SETTINGS) return null
            return intent.data!!.schemeSpecificPart
        }
            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)
private fun getIntent(packageManager: PackageManager, intents: Iterable<Intent>) =
    intents.firstOrNull { it.resolveActivityInfo(packageManager, 0)?.exported ?: false }

    companion object {
        fun getDisabledPackageName(intent: Intent?): String? {
            if (intent?.action != Settings.ACTION_APPLICATION_DETAILS_SETTINGS) return null
            return intent.data?.schemeSpecificPart
        }
    }
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 isDisabledState(state: Int) = when (state) {
private fun PackageManager.isDisabledState(packageName: String) =
    when (getApplicationEnabledSetting(packageName)) {
        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 PackageManager.getActivities(packageName: String) =
    getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).activities?.toList() ?: emptyList()
+2 −2
Original line number Diff line number Diff line
@@ -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>
+2 −2
Original line number Diff line number Diff line
@@ -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>
+2 −2

File changed.

Contains only whitespace changes.