Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b079c95 authored by Peter Cai's avatar Peter Cai
Browse files

ui: wizard: Implement the download process

parent f2c233fe
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -196,6 +196,14 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
        NotificationManagerCompat.from(this).notify(TASK_FAILURE_ID, notification)
    }

    /**
     * Recover the subscriber to a foreground task that is recently launched.
     *
     * null if the task doesn't exist, or was launched too long ago.
     */
    fun recoverForegroundTaskSubscriber(taskId: Long): ForegroundTaskSubscriberFlow? =
        foregroundTaskSubscribers[taskId]

    /**
     * Launch a potentially blocking foreground task in this service's lifecycle context.
     * This function does not block, but returns a Flow that emits ForegroundTaskState
+10 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
        var matchingId: String?,
        var confirmationCode: String?,
        var imei: String?,
        var downloadStarted: Boolean,
        var downloadTaskID: Long,
    )

    private lateinit var state: DownloadWizardState
@@ -55,7 +57,9 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
            "",
            null,
            null,
            null
            null,
            false,
            -1
        )

        progressBar = requireViewById(R.id.progress)
@@ -104,6 +108,8 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
        outState.putString("matchingId", state.matchingId)
        outState.putString("confirmationCode", state.confirmationCode)
        outState.putString("imei", state.imei)
        outState.putBoolean("downloadStarted", state.downloadStarted)
        outState.putLong("downloadTaskID", state.downloadTaskID)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
@@ -117,6 +123,9 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
        state.smdp = savedInstanceState.getString("smdp", state.smdp)
        state.matchingId = savedInstanceState.getString("matchingId", state.matchingId)
        state.imei = savedInstanceState.getString("imei", state.imei)
        state.downloadStarted =
            savedInstanceState.getBoolean("downloadStarted", state.downloadStarted)
        state.downloadTaskID = savedInstanceState.getLong("downloadTaskID", state.downloadTaskID)
    }

    private fun onPrevPressed() {
+115 −2
Original line number Diff line number Diff line
@@ -7,12 +7,32 @@ import android.view.ViewGroup
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import im.angry.openeuicc.common.R
import im.angry.openeuicc.service.EuiccChannelManagerService
import im.angry.openeuicc.util.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import net.typeblog.lpac_jni.ProfileDownloadCallback

class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStepFragment() {
    companion object {
        /**
         * An array of LPA-side state types, mapping 1:1 to progressItems
         */
        val LPA_PROGRESS_STATES = arrayOf(
            ProfileDownloadCallback.DownloadState.Preparing,
            ProfileDownloadCallback.DownloadState.Connecting,
            ProfileDownloadCallback.DownloadState.Authenticating,
            ProfileDownloadCallback.DownloadState.Downloading,
            ProfileDownloadCallback.DownloadState.Finalizing,
        )
    }

    private enum class ProgressState {
        NotStarted,
        InProgress,
@@ -22,7 +42,7 @@ class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStep

    private data class ProgressItem(
        val titleRes: Int,
        val state: ProgressState
        var state: ProgressState
    )

    private val progressItems = arrayOf(
@@ -38,8 +58,10 @@ class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStep

    private val adapter = ProgressItemAdapter()

    private var isDone = false

    override val hasNext: Boolean
        get() = false
        get() = isDone
    override val hasPrev: Boolean
        get() = false

@@ -66,6 +88,97 @@ class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStep
        return view
    }

    override fun onStart() {
        super.onStart()

        lifecycleScope.launch {
            showProgressBar(-1) // set indeterminate first
            ensureEuiccChannelManager()

            val subscriber = startDownloadOrSubscribe()

            if (subscriber == null) {
                requireActivity().finish()
                return@launch
            }

            subscriber.onEach {
                when (it) {
                    is EuiccChannelManagerService.ForegroundTaskState.Done -> {
                        hideProgressBar()

                        // Change the state of the last InProgress item to Error
                        progressItems.forEachIndexed { index, progressItem ->
                            if (progressItem.state == ProgressState.InProgress) {
                                progressItem.state = ProgressState.Error
                            }

                            adapter.notifyItemChanged(index)
                        }

                        isDone = true
                        refreshButtons()
                    }

                    is EuiccChannelManagerService.ForegroundTaskState.InProgress -> {
                        updateProgress(it.progress)
                    }

                    else -> {}
                }
            }.collect()
        }
    }

    private suspend fun startDownloadOrSubscribe(): EuiccChannelManagerService.ForegroundTaskSubscriberFlow? =
        if (state.downloadStarted) {
            // This will also return null if task ID is -1 (uninitialized), too
            euiccChannelManagerService.recoverForegroundTaskSubscriber(state.downloadTaskID)
        } else {
            euiccChannelManagerService.waitForForegroundTask()

            val (slotId, portId) = euiccChannelManager.withEuiccChannel(state.selectedLogicalSlot) { channel ->
                Pair(channel.slotId, channel.portId)
            }

            // Set started to true even before we start -- in case we get killed in the middle
            state.downloadStarted = true

            val ret = euiccChannelManagerService.launchProfileDownloadTask(
                slotId,
                portId,
                state.smdp,
                state.matchingId,
                state.confirmationCode,
                state.imei
            )

            state.downloadTaskID = ret.taskId

            ret
        }

    private fun updateProgress(progress: Int) {
        showProgressBar(progress)

        val lpaState = ProfileDownloadCallback.lookupStateFromProgress(progress)
        val stateIndex = LPA_PROGRESS_STATES.indexOf(lpaState)

        if (stateIndex > 0) {
            for (i in (0..<stateIndex)) {
                if (progressItems[i].state != ProgressState.Done) {
                    progressItems[i].state = ProgressState.Done
                    adapter.notifyItemChanged(i)
                }
            }
        }

        if (progressItems[stateIndex].state != ProgressState.InProgress) {
            progressItems[stateIndex].state = ProgressState.InProgress
            adapter.notifyItemChanged(stateIndex)
        }
    }

    private inner class ProgressItemHolder(val root: View) : RecyclerView.ViewHolder(root) {
        private val title = root.requireViewById<TextView>(R.id.download_progress_item_title)
        private val progressBar =