Compare commits

..

2 commits

Author SHA1 Message Date
aed2479044 ui: wizard: Prevent de-selecting checkboxes 2024-12-13 08:34:21 -05:00
f294fb5e17 ui: wizard: Reintroduce support for downloading to USB readers
...and fixup our API which caused this problem in the first place.
2024-12-13 08:33:02 -05:00
5 changed files with 29 additions and 7 deletions

View file

@ -13,7 +13,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -208,7 +208,7 @@ open class DefaultEuiccChannelManager(
} }
} }
override fun flowEuiccPorts(): Flow<Pair<Int, Int>> = flow { override fun flowInternalEuiccPorts(): Flow<Pair<Int, Int>> = flow {
uiccCards.forEach { info -> uiccCards.forEach { info ->
info.ports.forEach { port -> info.ports.forEach { port ->
tryOpenEuiccChannel(port)?.also { tryOpenEuiccChannel(port)?.also {
@ -223,6 +223,13 @@ open class DefaultEuiccChannelManager(
} }
}.flowOn(Dispatchers.IO) }.flowOn(Dispatchers.IO)
override fun flowAllOpenEuiccPorts(): Flow<Pair<Int, Int>> =
merge(flowInternalEuiccPorts(), flow {
if (tryOpenUsbEuiccChannel().second) {
emit(Pair(EuiccChannelManager.USB_CHANNEL_ID, 0))
}
})
override suspend fun tryOpenUsbEuiccChannel(): Pair<UsbDevice?, Boolean> = override suspend fun tryOpenUsbEuiccChannel(): Pair<UsbDevice?, Boolean> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
usbManager.deviceList.values.forEach { device -> usbManager.deviceList.values.forEach { device ->

View file

@ -26,7 +26,16 @@ interface EuiccChannelManager {
* *
* To obtain a temporary reference to a EuiccChannel, use `withEuiccChannel()`. * To obtain a temporary reference to a EuiccChannel, use `withEuiccChannel()`.
*/ */
fun flowEuiccPorts(): Flow<Pair<Int, Int>> fun flowInternalEuiccPorts(): Flow<Pair<Int, Int>>
/**
* Same as flowInternalEuiccPorts(), except that this includes non-device internal eUICC chips
* as well. Namely, this includes the USB reader.
*
* Non-internal readers will only be included if they have been opened properly, i.e. with permissions
* granted by the user.
*/
fun flowAllOpenEuiccPorts(): Flow<Pair<Int, Int>>
/** /**
* Scan all possible USB devices for CCID readers that may contain eUICC cards. * Scan all possible USB devices for CCID readers that may contain eUICC cards.

View file

@ -151,7 +151,7 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
val newPages: MutableList<Page> = mutableListOf() val newPages: MutableList<Page> = mutableListOf()
euiccChannelManager.flowEuiccPorts().onEach { (slotId, portId) -> euiccChannelManager.flowInternalEuiccPorts().onEach { (slotId, portId) ->
Log.d(TAG, "slot $slotId port $portId") Log.d(TAG, "slot $slotId port $portId")
euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->

View file

@ -14,6 +14,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.recyclerview.widget.RecyclerView.ViewHolder
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
@ -91,7 +92,7 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
private suspend fun init() { private suspend fun init() {
ensureEuiccChannelManager() ensureEuiccChannelManager()
showProgressBar(-1) showProgressBar(-1)
val slots = euiccChannelManager.flowEuiccPorts().map { (slotId, portId) -> val slots = euiccChannelManager.flowAllOpenEuiccPorts().map { (slotId, portId) ->
euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
SlotInfo( SlotInfo(
channel.logicalSlotId, channel.logicalSlotId,
@ -150,6 +151,7 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
fun onSelect(view: View) { fun onSelect(view: View) {
if (curIdx < 0) return if (curIdx < 0) return
checkBox.isChecked = true
if (adapter.currentSelectedIdx == curIdx) return if (adapter.currentSelectedIdx == curIdx) return
val lastIdx = adapter.currentSelectedIdx val lastIdx = adapter.currentSelectedIdx
adapter.currentSelectedIdx = curIdx adapter.currentSelectedIdx = curIdx
@ -174,7 +176,11 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
) )
} }
title.text = root.context.getString(R.string.download_wizard_slot_title, item.logicalSlotId) title.text = if (item.logicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
root.context.getString(R.string.usb)
} else {
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)
freeSpace.text = formatFreeSpace(item.freeSpace) freeSpace.text = formatFreeSpace(item.freeSpace)

View file

@ -18,7 +18,7 @@ val TelephonyManager.dsdsEnabled: Boolean
fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) { fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) {
// Disable all eSIM profiles before performing a DSDS switch (only for internal eSIMs) // Disable all eSIM profiles before performing a DSDS switch (only for internal eSIMs)
runBlocking { runBlocking {
euiccManager.flowEuiccPorts().onEach { (slotId, portId) -> euiccManager.flowInternalEuiccPorts().onEach { (slotId, portId) ->
euiccManager.withEuiccChannel(slotId, portId) { euiccManager.withEuiccChannel(slotId, portId) {
if (!it.port.card.isRemovable) { if (!it.port.card.isRemovable) {
it.lpa.disableActiveProfile(false) it.lpa.disableActiveProfile(false)