Compare commits

...

3 commits

Author SHA1 Message Date
c4b513fc0a ui: Save selected slot as state in DownloadWizardActivity
All checks were successful
/ build-debug (push) Successful in 5m5s
2024-11-17 22:00:03 -05:00
6458f54db2 ui: Ensure EuiccChannelManager is available in slot select fragment 2024-11-17 21:42:41 -05:00
87f36f4166 ui: Expose more info in slot select fragment 2024-11-17 21:41:40 -05:00
6 changed files with 81 additions and 16 deletions

View file

@ -109,7 +109,10 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
fab.setOnClickListener { fab.setOnClickListener {
lifecycleScope.launch { lifecycleScope.launch {
if (preferenceRepository.experimentalDownloadWizardFlow.first()) { if (preferenceRepository.experimentalDownloadWizardFlow.first()) {
startActivity(Intent(requireContext(), DownloadWizardActivity::class.java)) Intent(requireContext(), DownloadWizardActivity::class.java).apply {
putExtra("selectedLogicalSlot", logicalSlotId)
startActivity(this)
}
} else { } else {
ProfileDownloadFragment.newInstance(slotId, portId) ProfileDownloadFragment.newInstance(slotId, portId)
.show(childFragmentManager, ProfileDownloadFragment.TAG) .show(childFragmentManager, ProfileDownloadFragment.TAG)

View file

@ -15,6 +15,12 @@ import im.angry.openeuicc.ui.BaseEuiccAccessActivity
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
class DownloadWizardActivity: BaseEuiccAccessActivity() { class DownloadWizardActivity: BaseEuiccAccessActivity() {
data class DownloadWizardState(
var selectedLogicalSlot: Int
)
private lateinit var state: DownloadWizardState
private lateinit var progressBar: ProgressBar private lateinit var progressBar: ProgressBar
private lateinit var nextButton: Button private lateinit var nextButton: Button
private lateinit var prevButton: Button private lateinit var prevButton: Button
@ -31,6 +37,10 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
} }
}) })
state = DownloadWizardState(
intent.getIntExtra("selectedLogicalSlot", 0)
)
progressBar = requireViewById(R.id.progress) progressBar = requireViewById(R.id.progress)
nextButton = requireViewById(R.id.download_wizard_next) nextButton = requireViewById(R.id.download_wizard_next)
prevButton = requireViewById(R.id.download_wizard_back) prevButton = requireViewById(R.id.download_wizard_back)
@ -89,10 +99,13 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
} }
abstract class DownloadWizardStepFragment : Fragment(), OpenEuiccContextMarker { abstract class DownloadWizardStepFragment : Fragment(), OpenEuiccContextMarker {
protected val state: DownloadWizardState
get() = (requireActivity() as DownloadWizardActivity).state
abstract val hasNext: Boolean abstract val hasNext: Boolean
abstract val hasPrev: Boolean abstract val hasPrev: Boolean
abstract fun createNextFragment(): DownloadWizardStepFragment abstract fun createNextFragment(): DownloadWizardStepFragment?
abstract fun createPrevFragment(): DownloadWizardStepFragment abstract fun createPrevFragment(): DownloadWizardStepFragment?
protected fun hideProgressBar() { protected fun hideProgressBar() {
(requireActivity() as DownloadWizardActivity).progressBar.visibility = View.GONE (requireActivity() as DownloadWizardActivity).progressBar.visibility = View.GONE

View file

@ -22,6 +22,9 @@ import net.typeblog.lpac_jni.LocalProfileInfo
class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardStepFragment() { class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardStepFragment() {
private data class SlotInfo( private data class SlotInfo(
val logicalSlotId: Int, val logicalSlotId: Int,
val isRemovable: Boolean,
val hasMultiplePorts: Boolean,
val portId: Int,
val eID: String, val eID: String,
val enabledProfileName: String? val enabledProfileName: String?
) )
@ -31,17 +34,15 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
private val adapter = SlotInfoAdapter() private val adapter = SlotInfoAdapter()
override val hasNext: Boolean override val hasNext: Boolean
get() = loaded get() = loaded && adapter.slots.isNotEmpty()
override val hasPrev: Boolean override val hasPrev: Boolean
get() = false get() = true
override fun createNextFragment(): DownloadWizardActivity.DownloadWizardStepFragment { override fun createNextFragment(): DownloadWizardActivity.DownloadWizardStepFragment? {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override fun createPrevFragment(): DownloadWizardActivity.DownloadWizardStepFragment { override fun createPrevFragment(): DownloadWizardActivity.DownloadWizardStepFragment? = null
TODO("Not yet implemented")
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -66,25 +67,42 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private suspend fun init() { private suspend fun init() {
ensureEuiccChannelManager()
showProgressBar(-1) showProgressBar(-1)
val slots = euiccChannelManager.flowEuiccPorts().map { (slotId, portId) -> val slots = euiccChannelManager.flowEuiccPorts().map { (slotId, portId) ->
euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
SlotInfo( SlotInfo(
channel.logicalSlotId, channel.logicalSlotId,
channel.port.card.isRemovable,
channel.port.card.ports.size > 1,
channel.portId,
channel.lpa.eID, channel.lpa.eID,
channel.lpa.profiles.find { it.state == LocalProfileInfo.State.Enabled }?.displayName channel.lpa.profiles.find { it.state == LocalProfileInfo.State.Enabled }?.displayName
) )
} }
}.toList() }.toList()
adapter.slots = slots adapter.slots = slots
// Ensure we always have a selected slot by default
val selectedIdx = slots.indexOfFirst { it.logicalSlotId == state.selectedLogicalSlot }
adapter.currentSelectedIdx = if (selectedIdx > 0) {
selectedIdx
} else {
if (slots.isNotEmpty()) {
state.selectedLogicalSlot = slots[0].logicalSlotId
}
0
}
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
hideProgressBar() hideProgressBar()
loaded = true loaded = true
refreshButtons() refreshButtons()
} }
private class SlotItemHolder(val adapter: SlotInfoAdapter, val root: View) : ViewHolder(root) { private inner class SlotItemHolder(val root: View) : ViewHolder(root) {
private val title = root.requireViewById<TextView>(R.id.slot_item_title) private val title = root.requireViewById<TextView>(R.id.slot_item_title)
private val type = root.requireViewById<TextView>(R.id.slot_item_type)
private val eID = root.requireViewById<TextView>(R.id.slot_item_eid) private val eID = root.requireViewById<TextView>(R.id.slot_item_eid)
private val activeProfile = root.requireViewById<TextView>(R.id.slot_item_active_profile) private val activeProfile = root.requireViewById<TextView>(R.id.slot_item_active_profile)
private val checkBox = root.requireViewById<CheckBox>(R.id.slot_checkbox) private val checkBox = root.requireViewById<CheckBox>(R.id.slot_checkbox)
@ -104,10 +122,24 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
adapter.currentSelectedIdx = curIdx adapter.currentSelectedIdx = curIdx
adapter.notifyItemChanged(lastIdx) adapter.notifyItemChanged(lastIdx)
adapter.notifyItemChanged(curIdx) adapter.notifyItemChanged(curIdx)
// Selected index isn't logical slot ID directly, needs a conversion
state.selectedLogicalSlot = adapter.slots[adapter.currentSelectedIdx].logicalSlotId
} }
fun bind(item: SlotInfo, idx: Int) { fun bind(item: SlotInfo, idx: Int) {
curIdx = idx curIdx = idx
type.text = if (item.isRemovable) {
root.context.getString(R.string.download_wizard_slot_type_removable)
} else if (!item.hasMultiplePorts) {
root.context.getString(R.string.download_wizard_slot_type_internal)
} else {
root.context.getString(
R.string.download_wizard_slot_type_internal_port,
item.portId
)
}
title.text = root.context.getString(R.string.download_wizard_slot_title, item.logicalSlotId) title.text = root.context.getString(R.string.download_wizard_slot_title, item.logicalSlotId)
eID.text = item.eID eID.text = item.eID
activeProfile.text = item.enabledProfileName ?: root.context.getString(R.string.unknown) activeProfile.text = item.enabledProfileName ?: root.context.getString(R.string.unknown)
@ -115,13 +147,13 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
} }
} }
private class SlotInfoAdapter : RecyclerView.Adapter<SlotItemHolder>() { private inner class SlotInfoAdapter : RecyclerView.Adapter<SlotItemHolder>() {
var slots: List<SlotInfo> = listOf() var slots: List<SlotInfo> = listOf()
var currentSelectedIdx = 0 var currentSelectedIdx = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SlotItemHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SlotItemHolder {
val root = LayoutInflater.from(parent.context).inflate(R.layout.download_slot_item, parent, false) val root = LayoutInflater.from(parent.context).inflate(R.layout.download_slot_item, parent, false)
return SlotItemHolder(this, root) return SlotItemHolder(root)
} }
override fun getItemCount(): Int = slots.size override fun getItemCount(): Int = slots.size

View file

@ -19,6 +19,20 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/slot_item_type_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="100dp"
android:text="@string/download_wizard_slot_type"
android:textSize="14sp" />
<TextView
android:id="@+id/slot_item_type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="14sp" />
<TextView <TextView
android:id="@+id/slot_item_eid_label" android:id="@+id/slot_item_eid_label"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -54,7 +68,7 @@
android:layout_marginStart="10sp" android:layout_marginStart="10sp"
android:layout_marginTop="20sp" android:layout_marginTop="20sp"
android:layout_marginEnd="10sp" android:layout_marginEnd="10sp"
app:constraint_referenced_ids="slot_item_eid_label,slot_item_eid,slot_item_active_profile_label,slot_item_active_profile" app:constraint_referenced_ids="slot_item_type_label,slot_item_type,slot_item_eid_label,slot_item_eid,slot_item_active_profile_label,slot_item_active_profile"
app:flow_wrapMode="aligned" app:flow_wrapMode="aligned"
app:flow_horizontalAlign="start" app:flow_horizontalAlign="start"
app:flow_horizontalBias="1" app:flow_horizontalBias="1"

View file

@ -23,7 +23,6 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constrainedHeight="true" app:layout_constrainedHeight="true" />
app:layout_constraintHeight_max="300dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -63,6 +63,10 @@
<string name="download_wizard_next">Next</string> <string name="download_wizard_next">Next</string>
<string name="download_wizard_slot_select">Confirm the eSIM slot:</string> <string name="download_wizard_slot_select">Confirm the eSIM slot:</string>
<string name="download_wizard_slot_title">Logical slot %d</string> <string name="download_wizard_slot_title">Logical slot %d</string>
<string name="download_wizard_slot_type">Type:</string>
<string name="download_wizard_slot_type_removable">Removable</string>
<string name="download_wizard_slot_type_internal">Internal</string>
<string name="download_wizard_slot_type_internal_port">Internal, port %d</string>
<string name="download_wizard_slot_eid">eID:</string> <string name="download_wizard_slot_eid">eID:</string>
<string name="download_wizard_slot_active_profile">Active Profile:</string> <string name="download_wizard_slot_active_profile">Active Profile:</string>