From 9873634de76f9087e01953078f859ea3d3b6115a Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 13:20:19 +0800 Subject: [PATCH 1/8] chore: rename strings name --- .../angry/openeuicc/ui/UnprivilegedEuiccManagementFragment.kt | 2 +- app-unpriv/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 85be2ac..7128914 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 @@ -47,7 +47,7 @@ class UnprivilegedEuiccManagementFragment : EuiccManagementFragment() { val toast = Toast.makeText( context, getString( - R.string.toast_hint_enable_sim_toolkit, + R.string.toast_prompt_to_enable_sim_toolkit, context.packageManager.getApplicationLabel(appInfo) ), Toast.LENGTH_LONG, diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index 54c1843..4405067 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -9,7 +9,7 @@ ARA-M SHA-1 copied to clipboard - Please ENABLE your "%s" application + Please ENABLE your "%s" application System Features From 698021c7f4821d4d83aa2296ebd8930e2ee15a7b Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 13:42:44 +0800 Subject: [PATCH 2/8] refactor: sim toolkit --- .../im/angry/openeuicc/util/SIMToolkit.kt | 98 +++++++++---------- 1 file changed, 44 insertions(+), 54 deletions(-) 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 56e011e..eab3c92 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 @@ -1,57 +1,37 @@ package im.angry.openeuicc.util -import android.app.Activity import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.pm.ActivityInfo import android.content.pm.PackageManager import androidx.annotation.ArrayRes import im.angry.easyeuicc.R import im.angry.openeuicc.core.EuiccChannelManager class SIMToolkit(private val context: Context) { - private val pm: PackageManager - get() = context.packageManager - - private val slotSelection = getComponentNames(R.array.sim_toolkit_slot_selection) - private val slots = buildMap { - put(0, getComponentNames(R.array.sim_toolkit_slot_1)) - put(1, getComponentNames(R.array.sim_toolkit_slot_2)) + put(-1, context.getComponentNames(R.array.sim_toolkit_slot_selection)) + put(0, context.getComponentNames(R.array.sim_toolkit_slot_1)) + put(1, context.getComponentNames(R.array.sim_toolkit_slot_2)) } - private val packageNames = buildSet { - addAll(slotSelection.map { it.packageName }) - addAll(slots.values.flatten().map { it.packageName }) - } + private val packageNames: Iterable + get() = slots.values.flatten().map { it.packageName }.toSet() private val activities: Iterable - get() = packageNames.flatMap(::getActivities).toSet() + get() = packageNames.flatMap(context.packageManager::getActivities) + .filter { it.exported } + .map { ComponentName(it.packageName, it.name) } private val launchIntent: Intent? - get() = packageNames.firstNotNullOfOrNull(::getLaunchIntent) + get() = packageNames.firstNotNullOfOrNull(context.packageManager::getLaunchIntent) - private fun getLaunchIntent(packageName: String) = try { - pm.getLaunchIntentForPackage(packageName) - } catch (_: PackageManager.NameNotFoundException) { - null + private fun getComponentsBySlotId(slotId: Int) = buildSet { + addAll(slots.getOrDefault(slotId, emptySet())) + addAll(slots.getOrDefault(-1, emptySet())) } - private fun getActivities(packageName: String) = buildList { - val activities = try { - pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).activities ?: emptyArray() - } catch (_: PackageManager.NameNotFoundException) { - emptyArray() - } - for (activity in activities) { - if (!activity.exported) continue - add(ComponentName(packageName, activity.name)) - } - } - - private fun getComponentNames(@ArrayRes id: Int) = - context.resources.getStringArray(id).mapNotNull(ComponentName::unflattenFromString) - fun isAvailable(slotId: Int) = when (slotId) { -1 -> false EuiccChannelManager.USB_CHANNEL_ID -> false @@ -59,34 +39,44 @@ class SIMToolkit(private val context: Context) { } fun intent(slotId: Int): Intent? { - val components = slots.getOrDefault(slotId, emptySet()) + slotSelection - val intent = Intent(Intent.ACTION_MAIN, null).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK - component = components.find(activities::contains) - addCategory(Intent.CATEGORY_LAUNCHER) + val component = getComponentsBySlotId(slotId).find(activities::contains) + ?: return launchIntent + val enabledState = try { + context.packageManager.getComponentEnabledSetting(component) + } catch (e: IllegalArgumentException) { + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT } - return when (pm.getComponentEnabledSetting(intent.component ?: return launchIntent)) { - PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> intent - else -> null + if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) return null + return Intent(Intent.ACTION_MAIN, null).also { + it.flags = Intent.FLAG_ACTIVITY_NEW_TASK + it.component = component + it.addCategory(Intent.CATEGORY_LAUNCHER) } } - fun getDisabledPackageName(slotId: Int): String? { - val packageNames = buildSet { - addAll(slots.getOrDefault(slotId, emptySet()).map { it.packageName }) - addAll(slotSelection.map { it.packageName }) - } - return packageNames.find { + fun getDisabledPackageName(slotId: Int) = + getComponentsBySlotId(slotId).map { it.packageName }.toSet().find { val enabledState = try { - pm.getApplicationEnabledSetting(it) + context.packageManager.getApplicationEnabledSetting(it) } catch (e: IllegalArgumentException) { PackageManager.COMPONENT_ENABLED_STATE_DEFAULT } - when (enabledState) { - PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> true - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true - else -> false - } + enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER } - } } + +private fun PackageManager.getLaunchIntent(packageName: String) = try { + getLaunchIntentForPackage(packageName) +} catch (_: PackageManager.NameNotFoundException) { + null +} + +private fun PackageManager.getActivities(packageName: String) = try { + getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) + .activities?.toList() ?: emptyList() +} catch (_: PackageManager.NameNotFoundException) { + emptyList() +} + +private fun Context.getComponentNames(@ArrayRes id: Int) = + resources.getStringArray(id).mapNotNull(ComponentName::unflattenFromString) From e47eca289b942fd5a180791aa0e20d7ad27ec8bc Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 13:59:29 +0800 Subject: [PATCH 3/8] refactor: sim toolkit --- .../ui/UnprivilegedEuiccManagementFragment.kt | 35 +------------------ .../im/angry/openeuicc/util/SIMToolkit.kt | 31 ++++++++++++++-- 2 files changed, 30 insertions(+), 36 deletions(-) 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 7128914..22e3933 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 @@ -1,12 +1,7 @@ package im.angry.openeuicc.ui -import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri -import android.provider.Settings import android.view.Menu import android.view.MenuInflater -import android.widget.Toast import im.angry.easyeuicc.R import im.angry.openeuicc.util.SIMToolkit import im.angry.openeuicc.util.newInstanceEuicc @@ -30,35 +25,7 @@ class UnprivilegedEuiccManagementFragment : EuiccManagementFragment() { inflater.inflate(R.menu.fragment_sim_toolkit, menu) menu.findItem(R.id.open_sim_toolkit).apply { isVisible = stk.isAvailable(slotId) - setOnMenuItemClickListener { launchSIMToolkit() } - } - } - - private fun launchSIMToolkit(): Boolean { - val intent = stk.intent(slotId) - val disabledPackageName = stk.getDisabledPackageName(slotId) - if (disabledPackageName == null && intent != null) { - startActivity(intent) - return true - } else if (disabledPackageName != null) { - val context = requireContext() - val appInfo = context.packageManager - .getApplicationInfo(disabledPackageName, PackageManager.GET_ACTIVITIES) - val toast = Toast.makeText( - context, - getString( - R.string.toast_prompt_to_enable_sim_toolkit, - context.packageManager.getApplicationLabel(appInfo) - ), - Toast.LENGTH_LONG, - ) - startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - data = Uri.fromParts("package", disabledPackageName, null) - }) - toast.show() - return true - } else { - return false + setOnMenuItemClickListener { stk.launch(slotId) } } } } \ No newline at end of file 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 eab3c92..90e2c6d 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 @@ -5,6 +5,9 @@ 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 +import android.widget.Toast import androidx.annotation.ArrayRes import im.angry.easyeuicc.R import im.angry.openeuicc.core.EuiccChannelManager @@ -38,7 +41,7 @@ class SIMToolkit(private val context: Context) { else -> intent(slotId) != null || getDisabledPackageName(slotId) != null } - fun intent(slotId: Int): Intent? { + private fun intent(slotId: Int): Intent? { val component = getComponentsBySlotId(slotId).find(activities::contains) ?: return launchIntent val enabledState = try { @@ -54,7 +57,7 @@ class SIMToolkit(private val context: Context) { } } - fun getDisabledPackageName(slotId: Int) = + private fun getDisabledPackageName(slotId: Int) = getComponentsBySlotId(slotId).map { it.packageName }.toSet().find { val enabledState = try { context.packageManager.getApplicationEnabledSetting(it) @@ -63,8 +66,32 @@ class SIMToolkit(private val context: Context) { } enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER } + + fun launch(slotId: Int): Boolean { + val pkgName = getDisabledPackageName(slotId) + if (pkgName == null) { + context.startActivity(intent(slotId) ?: return false) + return true + } + val label = context.packageManager.getApplicationLabel(pkgName) + val toast = Toast.makeText( + context, + context.getString(R.string.toast_prompt_to_enable_sim_toolkit, label), + Toast.LENGTH_LONG, + ) + val intent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", pkgName, null) + ) + toast.show() + context.startActivity(intent) + return true + } } +private fun PackageManager.getApplicationLabel(packageName: String): CharSequence = + getApplicationLabel(getApplicationInfo(packageName, 0)) + private fun PackageManager.getLaunchIntent(packageName: String) = try { getLaunchIntentForPackage(packageName) } catch (_: PackageManager.NameNotFoundException) { From e24523f3e2df5ad6cd1560ae62959c1c623cfb6f Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 14:20:55 +0800 Subject: [PATCH 4/8] refactor: sim toolkit --- .../im/angry/openeuicc/util/SIMToolkit.kt | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) 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 90e2c6d..8914588 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 @@ -14,9 +14,11 @@ import im.angry.openeuicc.core.EuiccChannelManager class SIMToolkit(private val context: Context) { private val slots = buildMap { - put(-1, context.getComponentNames(R.array.sim_toolkit_slot_selection)) - put(0, context.getComponentNames(R.array.sim_toolkit_slot_1)) - put(1, context.getComponentNames(R.array.sim_toolkit_slot_2)) + fun getComponentNames(@ArrayRes id: Int) = context.resources + .getStringArray(id).mapNotNull(ComponentName::unflattenFromString) + 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)) } private val packageNames: Iterable @@ -49,12 +51,8 @@ class SIMToolkit(private val context: Context) { } catch (e: IllegalArgumentException) { PackageManager.COMPONENT_ENABLED_STATE_DEFAULT } - if (enabledState != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) return null - return Intent(Intent.ACTION_MAIN, null).also { - it.flags = Intent.FLAG_ACTIVITY_NEW_TASK - it.component = component - it.addCategory(Intent.CATEGORY_LAUNCHER) - } + if (isDisabledState(enabledState)) return null + return Intent.makeMainActivity(component) } private fun getDisabledPackageName(slotId: Int) = @@ -64,7 +62,7 @@ class SIMToolkit(private val context: Context) { } catch (e: IllegalArgumentException) { PackageManager.COMPONENT_ENABLED_STATE_DEFAULT } - enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER + isDisabledState(enabledState) } fun launch(slotId: Int): Boolean { @@ -89,6 +87,12 @@ class SIMToolkit(private val context: Context) { } } +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.getApplicationLabel(packageName: String): CharSequence = getApplicationLabel(getApplicationInfo(packageName, 0)) @@ -104,6 +108,3 @@ private fun PackageManager.getActivities(packageName: String) = try { } catch (_: PackageManager.NameNotFoundException) { emptyList() } - -private fun Context.getComponentNames(@ArrayRes id: Int) = - resources.getStringArray(id).mapNotNull(ComponentName::unflattenFromString) From cc5f798d3d6787206dc7683560ccc3eb4bafbc02 Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 14:32:23 +0800 Subject: [PATCH 5/8] fix: strings --- app-unpriv/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index 4405067..a6974d9 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -9,7 +9,7 @@ ARA-M SHA-1 copied to clipboard - Please ENABLE your "%s" application + Please ENABLE your "%s" application System Features From d9cbef886eeb089ab4cc388b6f0bd50de8e7bd19 Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 14:34:30 +0800 Subject: [PATCH 6/8] fix: strings --- app-unpriv/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index a6974d9..c57476f 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -9,7 +9,7 @@ ARA-M SHA-1 copied to clipboard - Please ENABLE your "%s" application + Please ENABLE your \"%s\" application System Features From 40d97f0291702147d3a04ead253703d0c99f2671 Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 14:56:35 +0800 Subject: [PATCH 7/8] refactor: sim toolkit --- .../im/angry/openeuicc/util/SIMToolkit.kt | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) 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 8914588..fe65023 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 @@ -37,51 +37,47 @@ class SIMToolkit(private val context: Context) { addAll(slots.getOrDefault(-1, emptySet())) } - fun isAvailable(slotId: Int) = when (slotId) { - -1 -> false - EuiccChannelManager.USB_CHANNEL_ID -> false - else -> intent(slotId) != null || getDisabledPackageName(slotId) != null - } - - private fun intent(slotId: Int): Intent? { + private fun intentActivity(slotId: Int): Intent? { val component = getComponentsBySlotId(slotId).find(activities::contains) ?: return launchIntent - val enabledState = try { - context.packageManager.getComponentEnabledSetting(component) + val disabled = try { + isDisabledState(context.packageManager.getComponentEnabledSetting(component)) } catch (e: IllegalArgumentException) { - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + true } - if (isDisabledState(enabledState)) return null + if (disabled) return null return Intent.makeMainActivity(component) } private fun getDisabledPackageName(slotId: Int) = getComponentsBySlotId(slotId).map { it.packageName }.toSet().find { - val enabledState = try { - context.packageManager.getApplicationEnabledSetting(it) + try { + isDisabledState(context.packageManager.getApplicationEnabledSetting(it)) } catch (e: IllegalArgumentException) { - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + false } - isDisabledState(enabledState) } + fun isAvailable(slotId: Int) = when (slotId) { + -1 -> false + EuiccChannelManager.USB_CHANNEL_ID -> false + else -> intentActivity(slotId) != null || getDisabledPackageName(slotId) != null + } + fun launch(slotId: Int): Boolean { - val pkgName = getDisabledPackageName(slotId) - if (pkgName == null) { - context.startActivity(intent(slotId) ?: return false) - return true + var intent = intentActivity(slotId) + if (intent == null) { + val pkgName = getDisabledPackageName(slotId) ?: return false + val message = context.getString( + R.string.toast_prompt_to_enable_sim_toolkit, + context.packageManager.getApplicationLabel(pkgName) + ) + intent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", pkgName, null) + ) + Toast.makeText(context, message, Toast.LENGTH_LONG).show() } - val label = context.packageManager.getApplicationLabel(pkgName) - val toast = Toast.makeText( - context, - context.getString(R.string.toast_prompt_to_enable_sim_toolkit, label), - Toast.LENGTH_LONG, - ) - val intent = Intent( - Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", pkgName, null) - ) - toast.show() context.startActivity(intent) return true } From d613ba403eb2b5378e2f605396b9ff402369c8b1 Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 5 Mar 2025 13:20:19 +0800 Subject: [PATCH 8/8] refactor: sim toolkit --- .../ui/UnprivilegedEuiccManagementFragment.kt | 39 +---- .../im/angry/openeuicc/util/SIMToolkit.kt | 139 ++++++++++-------- app-unpriv/src/main/res/values/strings.xml | 2 +- 3 files changed, 79 insertions(+), 101 deletions(-) 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 85be2ac..95eb8b4 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 @@ -1,12 +1,7 @@ package im.angry.openeuicc.ui -import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri -import android.provider.Settings import android.view.Menu import android.view.MenuInflater -import android.widget.Toast import im.angry.easyeuicc.R import im.angry.openeuicc.util.SIMToolkit import im.angry.openeuicc.util.newInstanceEuicc @@ -29,36 +24,10 @@ class UnprivilegedEuiccManagementFragment : EuiccManagementFragment() { super.onCreateOptionsMenu(menu, inflater) inflater.inflate(R.menu.fragment_sim_toolkit, menu) menu.findItem(R.id.open_sim_toolkit).apply { - isVisible = stk.isAvailable(slotId) - setOnMenuItemClickListener { launchSIMToolkit() } - } - } - - private fun launchSIMToolkit(): Boolean { - val intent = stk.intent(slotId) - val disabledPackageName = stk.getDisabledPackageName(slotId) - if (disabledPackageName == null && intent != null) { - startActivity(intent) - return true - } else if (disabledPackageName != null) { - val context = requireContext() - val appInfo = context.packageManager - .getApplicationInfo(disabledPackageName, PackageManager.GET_ACTIVITIES) - val toast = Toast.makeText( - context, - getString( - R.string.toast_hint_enable_sim_toolkit, - context.packageManager.getApplicationLabel(appInfo) - ), - Toast.LENGTH_LONG, - ) - startActivity(Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply { - data = Uri.fromParts("package", disabledPackageName, null) - }) - toast.show() - return true - } else { - return false + val slot = stk[slotId] + if (slot == null) return@apply + isVisible = true + setOnMenuItemClickListener { slot.launch() } } } } \ No newline at end of file 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 56e011e..ca3c0ad 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 @@ -1,92 +1,101 @@ package im.angry.openeuicc.util -import android.app.Activity import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.net.Uri +import android.provider.Settings +import android.widget.Toast import androidx.annotation.ArrayRes import im.angry.easyeuicc.R import im.angry.openeuicc.core.EuiccChannelManager class SIMToolkit(private val context: Context) { - private val pm: PackageManager - get() = context.packageManager - - private val slotSelection = getComponentNames(R.array.sim_toolkit_slot_selection) - private val slots = buildMap { + fun getComponentNames(@ArrayRes id: Int) = context.resources + .getStringArray(id).mapNotNull(ComponentName::unflattenFromString) + 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)) } - private val packageNames = buildSet { - addAll(slotSelection.map { it.packageName }) - addAll(slots.values.flatten().map { it.packageName }) + operator fun get(slotId: Int): Slot? = when (slotId) { + -1, EuiccChannelManager.USB_CHANNEL_ID -> null + else -> Slot(context, buildSet { + addAll(slots.getOrDefault(slotId, emptySet())) + addAll(slots.getOrDefault(-1, emptySet())) + }) } - private val activities: Iterable - get() = packageNames.flatMap(::getActivities).toSet() + data class Slot(private val context: Context, private val components: Set) { + private val packageNames: Iterable + get() = components.map { it.packageName }.toSet() - private val launchIntent: Intent? - get() = packageNames.firstNotNullOfOrNull(::getLaunchIntent) + private val launchIntent: Intent? + get() = packageNames.firstNotNullOfOrNull(context.packageManager::getLaunchIntent) - private fun getLaunchIntent(packageName: String) = try { - pm.getLaunchIntentForPackage(packageName) - } catch (_: PackageManager.NameNotFoundException) { - null - } + private val activities: Iterable + get() = packageNames.flatMap(context.packageManager::getActivities) + .filter { it.exported }.map { ComponentName(it.packageName, it.name) } - private fun getActivities(packageName: String) = buildList { - val activities = try { - pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES).activities ?: emptyArray() - } catch (_: PackageManager.NameNotFoundException) { - emptyArray() - } - for (activity in activities) { - if (!activity.exported) continue - add(ComponentName(packageName, activity.name)) - } - } - - private fun getComponentNames(@ArrayRes id: Int) = - context.resources.getStringArray(id).mapNotNull(ComponentName::unflattenFromString) - - fun isAvailable(slotId: Int) = when (slotId) { - -1 -> false - EuiccChannelManager.USB_CHANNEL_ID -> false - else -> intent(slotId) != null || getDisabledPackageName(slotId) != null - } - - fun intent(slotId: Int): Intent? { - val components = slots.getOrDefault(slotId, emptySet()) + slotSelection - val intent = Intent(Intent.ACTION_MAIN, null).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK - component = components.find(activities::contains) - addCategory(Intent.CATEGORY_LAUNCHER) - } - return when (pm.getComponentEnabledSetting(intent.component ?: return launchIntent)) { - PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> intent - else -> null - } - } - - fun getDisabledPackageName(slotId: Int): String? { - val packageNames = buildSet { - addAll(slots.getOrDefault(slotId, emptySet()).map { it.packageName }) - addAll(slotSelection.map { it.packageName }) - } - return packageNames.find { - val enabledState = try { - pm.getApplicationEnabledSetting(it) + private fun getIntent(): Intent? { + try { + val component = components.find(activities::contains) ?: return launchIntent + if (isDisabledState(context.packageManager.getComponentEnabledSetting(component))) + return null + return Intent.makeMainActivity(component) } catch (e: IllegalArgumentException) { - PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + return null } - when (enabledState) { - PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> true - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true - else -> false + } + + private fun getDisabledPackageName() = packageNames.find { + try { + isDisabledState(context.packageManager.getApplicationEnabledSetting(it)) + } catch (e: IllegalArgumentException) { + false } } + + fun launch(): Boolean { + var intent = getIntent() + if (intent == null) { + val pkgName = getDisabledPackageName() ?: return false + val message = context.getString( + R.string.toast_prompt_to_enable_sim_toolkit, + context.packageManager.getApplicationLabel(pkgName) + ) + intent = Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", pkgName, null) + ) + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + context.startActivity(intent) + return true + } } } + +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.getApplicationLabel(packageName: String): CharSequence = + getApplicationLabel(getApplicationInfo(packageName, 0)) + +private fun PackageManager.getLaunchIntent(packageName: String) = try { + getLaunchIntentForPackage(packageName) +} catch (_: PackageManager.NameNotFoundException) { + null +} + +private fun PackageManager.getActivities(packageName: String) = try { + getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) + .activities?.toList() ?: emptyList() +} catch (_: PackageManager.NameNotFoundException) { + emptyList() +} diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index 54c1843..c57476f 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -9,7 +9,7 @@ ARA-M SHA-1 copied to clipboard - Please ENABLE your "%s" application + Please ENABLE your \"%s\" application System Features