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 {
lifecycleScope.launch {
if (preferenceRepository.experimentalDownloadWizardFlow.first()) {
startActivity(Intent(requireContext(), DownloadWizardActivity::class.java))
Intent(requireContext(), DownloadWizardActivity::class.java).apply {
putExtra("selectedLogicalSlot", logicalSlotId)
startActivity(this)
}
} else {
ProfileDownloadFragment.newInstance(slotId, portId)
.show(childFragmentManager, ProfileDownloadFragment.TAG)

View file

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

View file

@ -22,6 +22,9 @@ import net.typeblog.lpac_jni.LocalProfileInfo
class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardStepFragment() {
private data class SlotInfo(
val logicalSlotId: Int,
val isRemovable: Boolean,
val hasMultiplePorts: Boolean,
val portId: Int,
val eID: String,
val enabledProfileName: String?
)
@ -31,17 +34,15 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
private val adapter = SlotInfoAdapter()
override val hasNext: Boolean
get() = loaded
get() = loaded && adapter.slots.isNotEmpty()
override val hasPrev: Boolean
get() = false
get() = true
override fun createNextFragment(): DownloadWizardActivity.DownloadWizardStepFragment {
override fun createNextFragment(): DownloadWizardActivity.DownloadWizardStepFragment? {
TODO("Not yet implemented")
}
override fun createPrevFragment(): DownloadWizardActivity.DownloadWizardStepFragment {
TODO("Not yet implemented")
}
override fun createPrevFragment(): DownloadWizardActivity.DownloadWizardStepFragment? = null
override fun onCreateView(
inflater: LayoutInflater,
@ -66,25 +67,42 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
@SuppressLint("NotifyDataSetChanged")
private suspend fun init() {
ensureEuiccChannelManager()
showProgressBar(-1)
val slots = euiccChannelManager.flowEuiccPorts().map { (slotId, portId) ->
euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
SlotInfo(
channel.logicalSlotId,
channel.port.card.isRemovable,
channel.port.card.ports.size > 1,
channel.portId,
channel.lpa.eID,
channel.lpa.profiles.find { it.state == LocalProfileInfo.State.Enabled }?.displayName
)
}
}.toList()
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()
hideProgressBar()
loaded = true
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 type = root.requireViewById<TextView>(R.id.slot_item_type)
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 checkBox = root.requireViewById<CheckBox>(R.id.slot_checkbox)
@ -104,10 +122,24 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
adapter.currentSelectedIdx = curIdx
adapter.notifyItemChanged(lastIdx)
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) {
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)
eID.text = item.eID
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 currentSelectedIdx = 0
var currentSelectedIdx = -1
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SlotItemHolder {
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

View file

@ -19,6 +19,20 @@
app:layout_constraintStart_toStartOf="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
android:id="@+id/slot_item_eid_label"
android:layout_width="wrap_content"
@ -54,7 +68,7 @@
android:layout_marginStart="10sp"
android:layout_marginTop="20sp"
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_horizontalAlign="start"
app:flow_horizontalBias="1"

View file

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

View file

@ -63,6 +63,10 @@
<string name="download_wizard_next">Next</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_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_active_profile">Active Profile:</string>