瀏覽代碼

[6/n] Show special footer for SIM cards with MEP support

Peter Cai 2 年之前
父節點
當前提交
81b61c76c4

+ 1 - 0
app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt

@@ -12,6 +12,7 @@ abstract class EuiccChannel(
     val cardId = port.card.cardId
     val name = "SLOT $logicalSlotId"
     val removable = port.card.isRemovable
+    val isMEP = port.card.isMultipleEnabledProfilesSupported
 
     abstract val lpa: LocalProfileAssistant
     val valid: Boolean

+ 70 - 10
app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt

@@ -8,6 +8,7 @@ import android.view.LayoutInflater
 import android.view.MenuItem
 import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.ImageButton
 import android.widget.PopupMenu
 import android.widget.TextView
@@ -26,7 +27,7 @@ import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 import java.lang.Exception
 
-class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesChangedListener {
+open class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesChangedListener {
     companion object {
         const val TAG = "EuiccManagementFragment"
 
@@ -38,7 +39,7 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
     private lateinit var fab: FloatingActionButton
     private lateinit var profileList: RecyclerView
 
-    private val adapter = EuiccProfileAdapter(listOf())
+    private val adapter = EuiccProfileAdapter()
 
     override fun onCreateView(
         inflater: LayoutInflater,
@@ -76,6 +77,8 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
         refresh()
     }
 
+    protected open suspend fun onCreateFooterViews(parent: ViewGroup): List<View> = listOf()
+
     @SuppressLint("NotifyDataSetChanged")
     private fun refresh() {
         swipeRefresh.isRefreshing = true
@@ -88,6 +91,7 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
 
             withContext(Dispatchers.Main) {
                 adapter.profiles = profiles.operational
+                adapter.footerViews = onCreateFooterViews(profileList)
                 adapter.notifyDataSetChanged()
                 swipeRefresh.isRefreshing = false
             }
@@ -130,7 +134,30 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
             channel.lpa.disableProfile(iccid)
         }
 
-    inner class ViewHolder(private val root: View) : RecyclerView.ViewHolder(root) {
+    sealed class ViewHolder(root: View) : RecyclerView.ViewHolder(root) {
+        enum class Type(val value: Int) {
+            PROFILE(0),
+            FOOTER(1);
+
+            companion object {
+                fun fromInt(value: Int) =
+                    Type.values().first { it.value == value }
+            }
+        }
+    }
+
+    inner class FooterViewHolder: ViewHolder(FrameLayout(requireContext())) {
+        fun attach(view: View) {
+            view.parent?.let { (it as ViewGroup).removeView(view) }
+            (itemView as FrameLayout).addView(view)
+        }
+
+        fun detach() {
+            (itemView as FrameLayout).removeAllViews()
+        }
+    }
+
+    inner class ProfileViewHolder(private val root: View) : ViewHolder(root) {
         private val iccid: TextView = root.findViewById(R.id.iccid)
         private val name: TextView = root.findViewById(R.id.name)
         private val state: TextView = root.findViewById(R.id.state)
@@ -208,16 +235,49 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
             }
     }
 
-    inner class EuiccProfileAdapter(var profiles: List<LocalProfileInfo>) : RecyclerView.Adapter<ViewHolder>() {
-        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
-            val view = LayoutInflater.from(parent.context).inflate(R.layout.euicc_profile, parent, false)
-            return ViewHolder(view)
-        }
+    inner class EuiccProfileAdapter : RecyclerView.Adapter<ViewHolder>() {
+        var profiles: List<LocalProfileInfo> = listOf()
+        var footerViews: List<View> = listOf()
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
+            when (ViewHolder.Type.fromInt(viewType)) {
+                ViewHolder.Type.PROFILE -> {
+                    val view = LayoutInflater.from(parent.context).inflate(R.layout.euicc_profile, parent, false)
+                    ProfileViewHolder(view)
+                }
+                ViewHolder.Type.FOOTER -> {
+                    FooterViewHolder()
+                }
+            }
+
+        override fun getItemViewType(position: Int): Int =
+            when {
+                position < profiles.size -> {
+                    ViewHolder.Type.PROFILE.value
+                }
+                position >= profiles.size && position < profiles.size + footerViews.size -> {
+                    ViewHolder.Type.FOOTER.value
+                }
+                else -> -1
+            }
 
         override fun onBindViewHolder(holder: ViewHolder, position: Int) {
-            holder.setProfile(profiles[position])
+            when (holder) {
+                is ProfileViewHolder -> {
+                    holder.setProfile(profiles[position])
+                }
+                is FooterViewHolder -> {
+                    holder.attach(footerViews[position - profiles.size])
+                }
+            }
+        }
+
+        override fun onViewRecycled(holder: ViewHolder) {
+            if (holder is FooterViewHolder) {
+                holder.detach()
+            }
         }
 
-        override fun getItemCount(): Int = profiles.size
+        override fun getItemCount(): Int = profiles.size + footerViews.size
     }
 }

+ 5 - 1
app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt

@@ -11,6 +11,7 @@ import android.widget.Spinner
 import androidx.appcompat.app.AppCompatActivity
 import androidx.lifecycle.lifecycleScope
 import im.angry.openeuicc.common.R
+import im.angry.openeuicc.core.EuiccChannel
 import im.angry.openeuicc.core.EuiccChannelManager
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
@@ -72,6 +73,9 @@ open class MainActivity : AppCompatActivity() {
         return true
     }
 
+    protected open fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment =
+        EuiccManagementFragment.newInstance(channel.slotId, channel.portId)
+
     private suspend fun init() {
         withContext(Dispatchers.IO) {
             manager.enumerateEuiccChannels()
@@ -88,7 +92,7 @@ open class MainActivity : AppCompatActivity() {
         withContext(Dispatchers.Main) {
             manager.knownChannels.forEach { channel ->
                 spinnerAdapter.add(channel.name)
-                fragments.add(EuiccManagementFragment.newInstance(channel.slotId, channel.portId))
+                fragments.add(createEuiccManagementFragment(channel))
             }
 
             if (fragments.isNotEmpty()) {

+ 24 - 0
app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt

@@ -0,0 +1,24 @@
+package im.angry.openeuicc.ui
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import im.angry.openeuicc.R
+
+class PrivilegedEuiccManagementFragment: EuiccManagementFragment() {
+    companion object {
+        fun newInstance(slotId: Int, portId: Int): EuiccManagementFragment =
+            newInstanceEuicc(PrivilegedEuiccManagementFragment::class.java, slotId, portId)
+    }
+
+    override suspend fun onCreateFooterViews(parent: ViewGroup): List<View> =
+        if (channel.isMEP) {
+            val view = layoutInflater.inflate(R.layout.footer_mep, parent, false)
+            view.findViewById<Button>(R.id.footer_mep_slot_mapping).setOnClickListener {
+                (requireActivity() as PrivilegedMainActivity).showSlotMappingFragment()
+            }
+            listOf(view)
+        } else {
+            listOf()
+        }
+}

+ 8 - 1
app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt

@@ -4,6 +4,7 @@ import android.view.Menu
 import android.view.MenuItem
 import android.widget.Toast
 import im.angry.openeuicc.R
+import im.angry.openeuicc.core.EuiccChannel
 import im.angry.openeuicc.util.*
 
 class PrivilegedMainActivity : MainActivity() {
@@ -20,6 +21,9 @@ class PrivilegedMainActivity : MainActivity() {
         return true
     }
 
+    internal fun showSlotMappingFragment() =
+        SlotMappingFragment().show(supportFragmentManager, SlotMappingFragment.TAG)
+
     override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
         R.id.dsds -> {
             tm.dsdsEnabled = !item.isChecked
@@ -28,9 +32,12 @@ class PrivilegedMainActivity : MainActivity() {
             true
         }
         R.id.slot_mapping -> {
-            SlotMappingFragment().show(supportFragmentManager, SlotMappingFragment.TAG)
+            showSlotMappingFragment()
             true
         }
         else -> super.onOptionsItemSelected(item)
     }
+
+    override fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment =
+        PrivilegedEuiccManagementFragment.newInstance(channel.slotId, channel.portId)
 }

+ 32 - 0
app/src/main/res/layout/footer_mep.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/footer_mep_text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="40dp"
+        android:layout_marginEnd="40dp"
+        android:gravity="center"
+        android:text="@string/footer_mep"
+        android:textStyle="italic"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/footer_mep_slot_mapping"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <Button
+        android:id="@+id/footer_mep_slot_mapping"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/slot_mapping"
+        android:textColor="?attr/colorAccent"
+        style="?android:attr/borderlessButtonStyle"
+        app:layout_constraintTop_toBottomOf="@id/footer_mep_text"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>

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

@@ -6,6 +6,8 @@
 
     <string name="toast_dsds_switched">DSDS state switched. Please wait until the modem restarts.</string>
 
+    <string name="footer_mep">Multiple Enabled Profiles (MEP) is supported by this slot. To enable or disable this feature, use the \"Slot Mapping\" tool.</string>
+
     <string name="slot_mapping">Slot Mapping</string>
     <string name="slot_mapping_logical_slot">Logical slot %d:</string>
     <string name="slot_mapping_port">Slot %d Port %d</string>