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