ソースを参照

refactor: app shortcuts (#261)

Reviewed-on: https://gitea.angry.im/PeterCxy/OpenEUICC/pulls/261
Co-authored-by: septs <github@septs.pw>
Co-committed-by: septs <github@septs.pw>
septs 2 ヶ月 前
コミット
f2c7e79bee

+ 0 - 6
app-common/src/main/AndroidManifest.xml

@@ -36,12 +36,6 @@
             android:exported="true"
             android:name="im.angry.openeuicc.ui.wizard.DownloadWizardActivity"
             android:label="@string/download_wizard">
-
-            <intent-filter>
-                <action android:name="im.angry.openeuicc.action.DOWNLOAD_WIZARD_ACTIVITY" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
 

+ 17 - 0
app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt

@@ -16,6 +16,9 @@ import android.view.MenuItem
 import android.view.View
 import android.widget.ProgressBar
 import androidx.activity.enableEdgeToEdge
+import androidx.core.content.pm.ShortcutInfoCompat
+import androidx.core.content.pm.ShortcutManagerCompat
+import androidx.core.graphics.drawable.IconCompat
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.lifecycleScope
 import androidx.viewpager2.adapter.FragmentStateAdapter
@@ -24,6 +27,7 @@ import com.google.android.material.tabs.TabLayout
 import com.google.android.material.tabs.TabLayoutMediator
 import im.angry.openeuicc.common.R
 import im.angry.openeuicc.core.EuiccChannelManager
+import im.angry.openeuicc.ui.wizard.DownloadWizardActivity
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.collect
@@ -216,6 +220,8 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
             ensureNotificationPermissions()
         }
 
+        ShortcutManagerCompat.setDynamicShortcuts(this, buildShortcuts().take(4))
+
         refreshing = false
     }
 
@@ -234,4 +240,15 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
             init(fromUsbEvent) // will set refreshing = false
         }
     }
+
+    protected open fun buildShortcuts(): List<ShortcutInfoCompat> {
+        val downloadShortcut = ShortcutInfoCompat.Builder(this, "download")
+            .setShortLabel(getString(R.string.profile_download))
+            .setIcon(IconCompat.createWithResource(this, R.drawable.ic_task_sim_card_download))
+            .setIntent(Intent(this, DownloadWizardActivity::class.java).apply {
+                action = Intent.ACTION_VIEW
+            })
+            .build()
+        return listOf(downloadShortcut)
+    }
 }

+ 1 - 1
app-common/src/main/res/values-zh-rCN/strings.xml

@@ -35,7 +35,7 @@
     <string name="task_profile_delete_failure">无法删除 eSIM 配置文件</string>
     <string name="task_profile_switch">正在切换 eSIM 配置文件</string>
     <string name="task_profile_switch_failure">无法切换 eSIM 配置文件</string>
-    <string name="profile_download">添加新 eSIM</string>
+    <string name="profile_download">下载新 eSIM</string>
     <string name="profile_download_server">服务器 (RSP / SM-DP+)</string>
     <string name="profile_download_code">激活码</string>
     <string name="profile_download_confirmation_code">确认码 (可选)</string>

+ 1 - 1
app-common/src/main/res/values-zh-rTW/strings.xml

@@ -35,7 +35,7 @@
     <string name="task_profile_delete_failure">無法刪除 eSIM 設定檔</string>
     <string name="task_profile_switch">正在切換 eSIM 設定檔</string>
     <string name="task_profile_switch_failure">無法切換 eSIM 設定檔</string>
-    <string name="profile_download">新增新 eSIM</string>
+    <string name="profile_download">下載新 eSIM</string>
     <string name="profile_download_server">伺服器 (RSP / SM-DP+)</string>
     <string name="profile_download_code">啟用碼</string>
     <string name="profile_download_confirmation_code">確認碼 (可選)</string>

+ 0 - 11
app-common/src/main/res/xml/shortcuts.xml

@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- https://developer.android.com/develop/ui/views/launch/shortcuts -->
-    <shortcut
-        android:enabled="true"
-        android:icon="@drawable/ic_task_sim_card_download"
-        android:shortcutId="download"
-        android:shortcutShortLabel="@string/profile_download">
-        <intent android:action="im.angry.openeuicc.action.DOWNLOAD_WIZARD_ACTIVITY" />
-    </shortcut>
-</shortcuts>

+ 0 - 4
app-unpriv/src/main/AndroidManifest.xml

@@ -21,10 +21,6 @@
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
-
-            <meta-data
-                android:name="android.app.shortcuts"
-                android:resource="@xml/shortcuts" />
         </activity>
 
         <activity

+ 32 - 0
app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedMainActivity.kt

@@ -4,12 +4,20 @@ import android.content.Intent
 import android.os.Bundle
 import android.view.Menu
 import android.view.MenuItem
+import androidx.core.content.pm.ShortcutInfoCompat
+import androidx.core.graphics.drawable.IconCompat
+import androidx.core.graphics.drawable.toBitmap
 import im.angry.easyeuicc.R
+import im.angry.openeuicc.util.SIMToolkit
 import im.angry.openeuicc.util.UnprivilegedEuiccContextMarker
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.runBlocking
 
 class UnprivilegedMainActivity : MainActivity(), UnprivilegedEuiccContextMarker {
+    private val stk by lazy {
+        SIMToolkit(this)
+    }
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         if (runBlocking { !preferenceRepository.skipQuickCompatibilityFlow.first() }) {
@@ -31,4 +39,28 @@ class UnprivilegedMainActivity : MainActivity(), UnprivilegedEuiccContextMarker
             }
             else -> super.onOptionsItemSelected(item)
         }
+
+    override fun buildShortcuts() = buildList {
+        addAll(super.buildShortcuts())
+        val context = this@UnprivilegedMainActivity
+        fun addShortcut(intent: Intent, index: Int, label: String) {
+            val id = "stk_slot_$index"
+            val icon = packageManager.getActivityIcon(intent)
+            val shortcut = ShortcutInfoCompat.Builder(context, id)
+                .setShortLabel(label)
+                .setIcon(IconCompat.createWithBitmap(icon.toBitmap()))
+                .setIntent(intent)
+                .build()
+            add(shortcut)
+        }
+        for ((index, intent) in stk.intents.withIndex()) {
+            if (stk.isSelection(intent ?: continue)) {
+                val label = getString(R.string.shortcut_sim_toolkit)
+                addShortcut(intent, index, label)
+                break
+            }
+            val label = getString(R.string.shortcut_sim_toolkit_with_slot, index)
+            addShortcut(intent, index, label)
+        }
+    }
 }

+ 12 - 2
app-unpriv/src/main/java/im/angry/openeuicc/util/SIMToolkit.kt

@@ -14,12 +14,15 @@ 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)
+            .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))
     }
 
+    val intents: Iterable<Intent?>
+        get() = listOf(get(0)?.intent, get(1)?.intent)
+
     operator fun get(slotId: Int): Slot? = when (slotId) {
         -1, EuiccChannelManager.USB_CHANNEL_ID -> null
         else -> Slot(context.packageManager, buildSet {
@@ -58,9 +61,16 @@ class SIMToolkit(private val context: Context) {
         }
 
         val intent: Intent?
-            get() = getActivityIntent() ?: getDisabledPackageIntent()
+            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)
+
     companion object {
         fun getDisabledPackageName(intent: Intent?): String? {
             if (intent?.action != Settings.ACTION_APPLICATION_DETAILS_SETTINGS) return null

+ 2 - 0
app-unpriv/src/main/res/values/strings.xml

@@ -4,6 +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>
 
     <!-- Settings -->
     <string name="pref_developer_ara_m" translatable="false">ARA-M SHA-1</string>