Compare commits

...

7 commits

Author SHA1 Message Date
40d97f0291
refactor: sim toolkit 2025-03-05 14:56:35 +08:00
d9cbef886e
fix: strings 2025-03-05 14:34:30 +08:00
cc5f798d3d
fix: strings 2025-03-05 14:32:23 +08:00
e24523f3e2
refactor: sim toolkit 2025-03-05 14:20:55 +08:00
e47eca289b
refactor: sim toolkit 2025-03-05 13:59:29 +08:00
698021c7f4
refactor: sim toolkit 2025-03-05 13:42:44 +08:00
9873634de7
chore: rename strings name 2025-03-05 13:20:19 +08:00
3 changed files with 73 additions and 92 deletions

View file

@ -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_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
setOnMenuItemClickListener { stk.launch(slotId) }
}
}
}

View file

@ -1,92 +1,106 @@
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 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 })
}
private val packageNames: Iterable<String>
get() = slots.values.flatten().map { it.packageName }.toSet()
private val activities: Iterable<ComponentName>
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 intentActivity(slotId: Int): Intent? {
val component = getComponentsBySlotId(slotId).find(activities::contains)
?: return launchIntent
val disabled = try {
isDisabledState(context.packageManager.getComponentEnabledSetting(component))
} catch (e: IllegalArgumentException) {
true
}
if (disabled) return null
return Intent.makeMainActivity(component)
}
private fun getComponentNames(@ArrayRes id: Int) =
context.resources.getStringArray(id).mapNotNull(ComponentName::unflattenFromString)
private fun getDisabledPackageName(slotId: Int) =
getComponentsBySlotId(slotId).map { it.packageName }.toSet().find {
try {
isDisabledState(context.packageManager.getApplicationEnabledSetting(it))
} catch (e: IllegalArgumentException) {
false
}
}
fun isAvailable(slotId: Int) = when (slotId) {
-1 -> false
EuiccChannelManager.USB_CHANNEL_ID -> false
else -> intent(slotId) != null || getDisabledPackageName(slotId) != null
else -> intentActivity(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)
} 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
}
fun launch(slotId: Int): Boolean {
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()
}
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<ActivityInfo>()
}

View file

@ -9,7 +9,7 @@
<!-- Toast -->
<string name="toast_ara_m_copied">ARA-M SHA-1 copied to clipboard</string>
<string name="toast_hint_enable_sim_toolkit">Please ENABLE your "%s" application</string>
<string name="toast_prompt_to_enable_sim_toolkit">Please ENABLE your \&quot;%s\&quot; application</string>
<!-- Compatibility Check Descriptions -->
<string name="compatibility_check_system_features">System Features</string>