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 fad03fd..617cbec 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,7 +1,10 @@ package im.angry.openeuicc.ui +import android.content.pm.PackageManager +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 @@ -24,8 +27,22 @@ 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) - intent = stk.intent(slotId) + val slot = stk[slotId] ?: return@apply + isVisible = slot.intent != null + setOnMenuItemClickListener { + val intent = slot.intent ?: return@setOnMenuItemClickListener false + if (intent.action == Settings.ACTION_APPLICATION_DETAILS_SETTINGS) { + val packageName = intent.data!!.schemeSpecificPart + val label = requireContext().packageManager.getApplicationLabel(packageName) + val message = requireContext().getString(R.string.toast_prompt_to_enable_sim_toolkit, label) + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } + startActivity(intent) + true + } } } -} \ No newline at end of file +} + +private fun PackageManager.getApplicationLabel(packageName: String): CharSequence = + getApplicationLabel(getApplicationInfo(packageName, 0)) 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 ced813a..99824ff 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 @@ -3,65 +3,87 @@ 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 +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 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.packageManager, buildSet { + addAll(slots.getOrDefault(slotId, emptySet())) + addAll(slots.getOrDefault(-1, emptySet())) + }) } - private val activities = packageNames.flatMap(::getActivities).toSet() + data class Slot(private val packageManager: PackageManager, private val components: Set) { + private val packageNames: Iterable + get() = components.map(ComponentName::getPackageName).toSet() - private val launchIntent by lazy { - packageNames.firstNotNullOfOrNull(::getLaunchIntent) - } + private val launchIntent: Intent? + get() = packageNames.firstNotNullOfOrNull(packageManager::getLaunchIntent) - private fun getLaunchIntent(packageName: String) = try { - val pm = context.packageManager - pm.getLaunchIntentForPackage(packageName) - } catch (_: PackageManager.NameNotFoundException) { - null - } + private val activities: Iterable + get() = packageNames.flatMap(packageManager::getActivities) + .filter(ActivityInfo::exported).map { ComponentName(it.packageName, it.name) } - private fun getActivities(packageName: String): List { - return try { - val pm = context.packageManager - val packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES) - val activities = packageInfo.activities - if (activities.isNullOrEmpty()) return emptyList() - activities.filter { it.exported }.map { ComponentName(it.packageName, it.name) } - } catch (_: PackageManager.NameNotFoundException) { - emptyList() + 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 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 - } - - 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) + private fun getDisabledPackageIntent(): Intent? { + val disabledPackageName = packageNames.find { + try { + isDisabledState(packageManager.getApplicationEnabledSetting(it)) + } catch (_: IllegalArgumentException) { + false + } + } + if (disabledPackageName == null) return null + return Intent( + Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", disabledPackageName, null) + ) } - return if (intent.component != null) intent else launchIntent + + val intent: Intent? + get() = getActivityIntent() ?: getDisabledPackageIntent() } } + +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.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 afed295..43bf44f 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -9,6 +9,7 @@ ARA-M SHA-1 copied to clipboard + Please ENABLE your \"%s\" application System Features