[6/n] Show special footer for SIM cards with MEP support
This commit is contained in:
parent
e708deea7c
commit
81b61c76c4
|
@ -12,6 +12,7 @@ abstract class EuiccChannel(
|
||||||
val cardId = port.card.cardId
|
val cardId = port.card.cardId
|
||||||
val name = "SLOT $logicalSlotId"
|
val name = "SLOT $logicalSlotId"
|
||||||
val removable = port.card.isRemovable
|
val removable = port.card.isRemovable
|
||||||
|
val isMEP = port.card.isMultipleEnabledProfilesSupported
|
||||||
|
|
||||||
abstract val lpa: LocalProfileAssistant
|
abstract val lpa: LocalProfileAssistant
|
||||||
val valid: Boolean
|
val valid: Boolean
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.FrameLayout
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.PopupMenu
|
import android.widget.PopupMenu
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -26,7 +27,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesChangedListener {
|
open class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesChangedListener {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "EuiccManagementFragment"
|
const val TAG = "EuiccManagementFragment"
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
|
||||||
private lateinit var fab: FloatingActionButton
|
private lateinit var fab: FloatingActionButton
|
||||||
private lateinit var profileList: RecyclerView
|
private lateinit var profileList: RecyclerView
|
||||||
|
|
||||||
private val adapter = EuiccProfileAdapter(listOf())
|
private val adapter = EuiccProfileAdapter()
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
|
@ -76,6 +77,8 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open suspend fun onCreateFooterViews(parent: ViewGroup): List<View> = listOf()
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
private fun refresh() {
|
private fun refresh() {
|
||||||
swipeRefresh.isRefreshing = true
|
swipeRefresh.isRefreshing = true
|
||||||
|
@ -88,6 +91,7 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
adapter.profiles = profiles.operational
|
adapter.profiles = profiles.operational
|
||||||
|
adapter.footerViews = onCreateFooterViews(profileList)
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
swipeRefresh.isRefreshing = false
|
swipeRefresh.isRefreshing = false
|
||||||
}
|
}
|
||||||
|
@ -130,7 +134,30 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
|
||||||
channel.lpa.disableProfile(iccid)
|
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 iccid: TextView = root.findViewById(R.id.iccid)
|
||||||
private val name: TextView = root.findViewById(R.id.name)
|
private val name: TextView = root.findViewById(R.id.name)
|
||||||
private val state: TextView = root.findViewById(R.id.state)
|
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>() {
|
inner class EuiccProfileAdapter : RecyclerView.Adapter<ViewHolder>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
var profiles: List<LocalProfileInfo> = listOf()
|
||||||
val view = LayoutInflater.from(parent.context).inflate(R.layout.euicc_profile, parent, false)
|
var footerViews: List<View> = listOf()
|
||||||
return ViewHolder(view)
|
|
||||||
}
|
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) {
|
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 getItemCount(): Int = profiles.size
|
override fun onViewRecycled(holder: ViewHolder) {
|
||||||
|
if (holder is FooterViewHolder) {
|
||||||
|
holder.detach()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = profiles.size + footerViews.size
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,6 +11,7 @@ import android.widget.Spinner
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import im.angry.openeuicc.common.R
|
import im.angry.openeuicc.common.R
|
||||||
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
import im.angry.openeuicc.core.EuiccChannelManager
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -72,6 +73,9 @@ open class MainActivity : AppCompatActivity() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment =
|
||||||
|
EuiccManagementFragment.newInstance(channel.slotId, channel.portId)
|
||||||
|
|
||||||
private suspend fun init() {
|
private suspend fun init() {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
manager.enumerateEuiccChannels()
|
manager.enumerateEuiccChannels()
|
||||||
|
@ -88,7 +92,7 @@ open class MainActivity : AppCompatActivity() {
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
manager.knownChannels.forEach { channel ->
|
manager.knownChannels.forEach { channel ->
|
||||||
spinnerAdapter.add(channel.name)
|
spinnerAdapter.add(channel.name)
|
||||||
fragments.add(EuiccManagementFragment.newInstance(channel.slotId, channel.portId))
|
fragments.add(createEuiccManagementFragment(channel))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fragments.isNotEmpty()) {
|
if (fragments.isNotEmpty()) {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import im.angry.openeuicc.R
|
import im.angry.openeuicc.R
|
||||||
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
|
|
||||||
class PrivilegedMainActivity : MainActivity() {
|
class PrivilegedMainActivity : MainActivity() {
|
||||||
|
@ -20,6 +21,9 @@ class PrivilegedMainActivity : MainActivity() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun showSlotMappingFragment() =
|
||||||
|
SlotMappingFragment().show(supportFragmentManager, SlotMappingFragment.TAG)
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
||||||
R.id.dsds -> {
|
R.id.dsds -> {
|
||||||
tm.dsdsEnabled = !item.isChecked
|
tm.dsdsEnabled = !item.isChecked
|
||||||
|
@ -28,9 +32,12 @@ class PrivilegedMainActivity : MainActivity() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.slot_mapping -> {
|
R.id.slot_mapping -> {
|
||||||
SlotMappingFragment().show(supportFragmentManager, SlotMappingFragment.TAG)
|
showSlotMappingFragment()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> super.onOptionsItemSelected(item)
|
else -> super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment =
|
||||||
|
PrivilegedEuiccManagementFragment.newInstance(channel.slotId, channel.portId)
|
||||||
}
|
}
|
32
app/src/main/res/layout/footer_mep.xml
Normal file
32
app/src/main/res/layout/footer_mep.xml
Normal file
|
@ -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>
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
<string name="toast_dsds_switched">DSDS state switched. Please wait until the modem restarts.</string>
|
<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">Slot Mapping</string>
|
||||||
<string name="slot_mapping_logical_slot">Logical slot %d:</string>
|
<string name="slot_mapping_logical_slot">Logical slot %d:</string>
|
||||||
<string name="slot_mapping_port">Slot %d Port %d</string>
|
<string name="slot_mapping_port">Slot %d Port %d</string>
|
||||||
|
|
Loading…
Reference in a new issue