瀏覽代碼

Add convience "Done" subscriber method for Flow<ForegroundTaskState>

Peter Cai 1 年之前
父節點
當前提交
837c34ba70

+ 16 - 7
app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt

@@ -21,6 +21,8 @@ import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.last
 import kotlinx.coroutines.flow.onCompletion
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.takeWhile
@@ -55,7 +57,14 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
         private const val TAG = "EuiccChannelManagerService"
         private const val CHANNEL_ID = "tasks"
         private const val FOREGROUND_ID = 1000
-        private const val TASK_FAILURE_ID = 1001
+        private const val TASK_FAILURE_ID = 1000
+
+        /**
+         * Utility function to wait for a foreground task to be done, return its
+         * error if any, or null on success.
+         */
+        suspend fun Flow<ForegroundTaskState>.waitDone(): Throwable? =
+            (this.last() as ForegroundTaskState.Done).error
     }
 
     inner class LocalBinder : Binder() {
@@ -185,7 +194,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
         failureTitle: String,
         iconRes: Int,
         task: suspend EuiccChannelManagerService.() -> Unit
-    ): Flow<ForegroundTaskState>? {
+    ): Flow<ForegroundTaskState> {
         // Atomically set the state to InProgress. If this returns true, we are
         // the only task currently in progress.
         if (!foregroundTaskState.compareAndSet(
@@ -193,7 +202,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
                 ForegroundTaskState.InProgress(0)
             )
         ) {
-            return null
+            return flow { emit(ForegroundTaskState.Done(IllegalStateException("There are tasks currently running"))) }
         }
 
         lifecycleScope.launch(Dispatchers.Main) {
@@ -280,7 +289,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
         matchingId: String?,
         confirmationCode: String?,
         imei: String?
-    ): Flow<ForegroundTaskState>? =
+    ): Flow<ForegroundTaskState> =
         launchForegroundTask(
             getString(R.string.task_profile_download),
             getString(R.string.task_profile_download_failure),
@@ -316,7 +325,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
         portId: Int,
         iccid: String,
         name: String
-    ): Flow<ForegroundTaskState>? =
+    ): Flow<ForegroundTaskState> =
         launchForegroundTask(
             getString(R.string.task_profile_rename),
             getString(R.string.task_profile_rename_failure),
@@ -338,7 +347,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
         slotId: Int,
         portId: Int,
         iccid: String
-    ): Flow<ForegroundTaskState>? =
+    ): Flow<ForegroundTaskState> =
         launchForegroundTask(
             getString(R.string.task_profile_delete),
             getString(R.string.task_profile_delete_failure),
@@ -361,7 +370,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
         iccid: String,
         enable: Boolean, // Enable or disable the profile indicated in iccid
         reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect, useful for USB readers
-    ): Flow<ForegroundTaskState>? =
+    ): Flow<ForegroundTaskState> =
         launchForegroundTask(
             getString(R.string.task_profile_switch),
             getString(R.string.task_profile_switch_failure),

+ 4 - 9
app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt

@@ -30,11 +30,11 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
 import net.typeblog.lpac_jni.LocalProfileInfo
 import im.angry.openeuicc.common.R
 import im.angry.openeuicc.service.EuiccChannelManagerService
+import im.angry.openeuicc.service.EuiccChannelManagerService.Companion.waitDone
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.last
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -209,7 +209,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
             ensureEuiccChannelManager()
             euiccChannelManagerService.waitForForegroundTask()
 
-            val res = euiccChannelManagerService.launchProfileSwitchTask(
+            val err = euiccChannelManagerService.launchProfileSwitchTask(
                 slotId,
                 portId,
                 iccid,
@@ -219,14 +219,9 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
                 } else {
                     30 * 1000
                 }
-            )?.last() as? EuiccChannelManagerService.ForegroundTaskState.Done
+            ).waitDone()
 
-            if (res == null) {
-                showSwitchFailureText()
-                return@launch
-            }
-
-            when (res.error) {
+            when (err) {
                 null -> {}
                 is EuiccChannelManagerService.SwitchingProfilesRefreshException -> {
                     // This is only really fatal for internal eSIMs

+ 3 - 3
app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt

@@ -8,8 +8,8 @@ import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.DialogFragment
 import androidx.lifecycle.lifecycleScope
 import im.angry.openeuicc.common.R
+import im.angry.openeuicc.service.EuiccChannelManagerService.Companion.waitDone
 import im.angry.openeuicc.util.*
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.launch
 
@@ -74,7 +74,7 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
                 slotId,
                 portId,
                 requireArguments().getString("iccid")!!
-            )!!.onStart {
+            ).onStart {
                 if (parentFragment is EuiccProfilesChangedListener) {
                     // Trigger a refresh in the parent fragment -- it should wait until
                     // any foreground task is completed before actually doing a refresh
@@ -86,7 +86,7 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
                 } catch (e: IllegalStateException) {
                     // Ignored
                 }
-            }.collect()
+            }.waitDone()
         }
     }
 }

+ 8 - 12
app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt

@@ -19,9 +19,9 @@ import com.journeyapps.barcodescanner.ScanContract
 import com.journeyapps.barcodescanner.ScanOptions
 import im.angry.openeuicc.common.R
 import im.angry.openeuicc.service.EuiccChannelManagerService
+import im.angry.openeuicc.service.EuiccChannelManagerService.Companion.waitDone
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.last
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -219,14 +219,11 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(),
         lifecycleScope.launch {
             ensureEuiccChannelManager()
             euiccChannelManagerService.waitForForegroundTask()
-            val res = doDownloadProfile(server, code, confirmationCode, imei)
+            val err = doDownloadProfile(server, code, confirmationCode, imei)
 
-            if (res == null || res.error != null) {
+            if (err != null) {
                 Log.d(TAG, "Error downloading profile")
-
-                if (res?.error != null) {
-                    Log.d(TAG, Log.getStackTraceString(res.error))
-                }
+                Log.d(TAG, Log.getStackTraceString(err))
 
                 Toast.makeText(requireContext(), R.string.profile_download_failed, Toast.LENGTH_LONG).show()
             }
@@ -250,14 +247,15 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(),
         imei: String?
     ) = withContext(Dispatchers.Main) {
         // The service is responsible for launching the actual blocking part on the IO context
-        val res = euiccChannelManagerService.launchProfileDownloadTask(
+        // On our side, we need the Main context because  of the UI updates
+        euiccChannelManagerService.launchProfileDownloadTask(
             slotId,
             portId,
             server,
             code,
             confirmationCode,
             imei
-        )!!.onEach {
+        ).onEach {
             if (it is EuiccChannelManagerService.ForegroundTaskState.InProgress) {
                 progress.progress = it.progress
                 progress.isIndeterminate = it.progress == 0
@@ -265,9 +263,7 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(),
                 progress.progress = 100
                 progress.isIndeterminate = false
             }
-        }.last()
-
-        res as? EuiccChannelManagerService.ForegroundTaskState.Done
+        }.waitDone()
     }
 
     override fun onDismiss(dialog: DialogInterface) {

+ 2 - 2
app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt

@@ -11,8 +11,8 @@ import androidx.appcompat.widget.Toolbar
 import androidx.lifecycle.lifecycleScope
 import com.google.android.material.textfield.TextInputLayout
 import im.angry.openeuicc.common.R
+import im.angry.openeuicc.service.EuiccChannelManagerService.Companion.waitDone
 import im.angry.openeuicc.util.*
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
 
 class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragmentMarker {
@@ -100,7 +100,7 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
                 portId,
                 requireArguments().getString("iccid")!!,
                 name
-            )?.collect()
+            ).waitDone()
 
             if (parentFragment is EuiccProfilesChangedListener) {
                 (parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()

+ 4 - 3
app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt

@@ -11,6 +11,7 @@ import android.util.Log
 import net.typeblog.lpac_jni.LocalProfileInfo
 import im.angry.openeuicc.core.EuiccChannel
 import im.angry.openeuicc.core.EuiccChannelManager
+import im.angry.openeuicc.service.EuiccChannelManagerService.Companion.waitDone
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.last
@@ -259,8 +260,8 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
         if (enabledAnywhere) return@withEuiccChannelManager RESULT_FIRST_USER
 
         euiccChannelManagerService.waitForForegroundTask()
-        val success = (euiccChannelManagerService.launchProfileDeleteTask(slotId, ports[0], iccid)
-            ?.last() as? EuiccChannelManagerService.ForegroundTaskState.Done)!!.error == null
+        val success = euiccChannelManagerService.launchProfileDeleteTask(slotId, ports[0], iccid)
+            .waitDone() == null
 
         return@withEuiccChannelManager if (success) {
             RESULT_OK
@@ -357,7 +358,7 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
             euiccChannelManagerService.waitForForegroundTask()
             val success =
                 (euiccChannelManagerService.launchProfileRenameTask(slotId, port, iccid, nickname!!)
-                    ?.last() as? EuiccChannelManagerService.ForegroundTaskState.Done)!!.error == null
+                    .waitDone()) == null
 
             euiccChannelManager.withEuiccChannel(slotId, port) { channel ->
                 appContainer.subscriptionManager.tryRefreshCachedEuiccInfo(channel.cardId)