Compare commits

..

No commits in common. "2721f9127723d3d123109f452e6cb368243f5283" and "cf5704be4220e11221bdf71c88ddfe4527c8c3a7" have entirely different histories.

4 changed files with 28 additions and 79 deletions

View file

@ -24,8 +24,6 @@ import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.coroutines.yield
import net.typeblog.lpac_jni.ProfileDownloadCallback
/**
@ -103,7 +101,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
}
}
private suspend fun updateForegroundNotification(title: String, iconRes: Int) {
private fun updateForegroundNotification(title: String, iconRes: Int) {
val channel =
NotificationChannelCompat.Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
.setName(getString(R.string.task_notification))
@ -119,7 +117,6 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
.setProgress(100, state.progress, state.progress == 0)
.setSmallIcon(iconRes)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true)
.build()
if (state.progress == 0) {
@ -127,10 +124,6 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
} else if (checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
NotificationManagerCompat.from(this).notify(FOREGROUND_ID, notification)
}
// Yield out so that the main looper can handle the notification event
// Without this yield, the notification sent above will not be shown in time.
yield()
} else {
stopForeground(STOP_FOREGROUND_REMOVE)
}
@ -140,14 +133,11 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
* Launch a potentially blocking foreground task in this service's lifecycle context.
* This function does not block, but returns a Flow that emits ForegroundTaskState
* updates associated with this task. The last update the returned flow will emit is
* always ForegroundTaskState.Done. The returned flow MUST be started in order for the
* foreground task to run.
* always ForegroundTaskState.Done.
*
* The task closure is expected to update foregroundTaskState whenever appropriate.
* If a foreground task is already running, this function returns null.
*
* To wait for foreground tasks to be available, use waitForForegroundTask().
*
* The function will set the state back to Idle once it sees ForegroundTaskState.Done.
*/
private fun launchForegroundTask(
@ -168,32 +158,21 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
lifecycleScope.launch(Dispatchers.Main) {
// Wait until our self-start command has succeeded.
// We can only call startForeground() after that
val res = withTimeoutOrNull(30 * 1000) {
foregroundStarted.first()
}
if (res == null) {
// The only case where the wait above could time out is if the subscriber
// to the flow is stuck. Or we failed to start foreground.
// In that case, we should just set our state back to Idle -- setting it
// to Done wouldn't help much because nothing is going to then set it Idle.
foregroundTaskState.value = ForegroundTaskState.Idle
return@launch
}
updateForegroundNotification(title, iconRes)
try {
withContext(Dispatchers.IO) {
this@EuiccChannelManagerService.task()
}
// This update will be sent by the subscriber (as shown below)
foregroundTaskState.value = ForegroundTaskState.Done(null)
} catch (t: Throwable) {
foregroundTaskState.value = ForegroundTaskState.Done(t)
} finally {
stopSelf()
}
updateForegroundNotification(title, iconRes)
}
// We should be the only task running, so we can subscribe to foregroundTaskState
@ -203,26 +182,20 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
// it has been completed by that point.
return foregroundTaskState.transformWhile {
// Also update our notification when we see an update
// But ignore the first progress = 0 update -- that is the current value.
// we need that to be handled by the main coroutine after it finishes.
if (it !is ForegroundTaskState.InProgress || it.progress != 0) {
withContext(Dispatchers.Main) {
updateForegroundNotification(title, iconRes)
}
}
emit(it)
it !is ForegroundTaskState.Done
}.onStart {
// When this Flow is started, we unblock the coroutine launched above by
// self-starting as a foreground service.
withContext(Dispatchers.Main) {
startForegroundService(
Intent(
this@EuiccChannelManagerService,
this@EuiccChannelManagerService::class.java
)
)
}
}.onCompletion { foregroundTaskState.value = ForegroundTaskState.Idle }
}
@ -289,23 +262,4 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
throw RuntimeException("Profile not renamed")
}
}
fun launchProfileDeleteTask(
slotId: Int,
portId: Int,
iccid: String
): Flow<ForegroundTaskState>? =
launchForegroundTask(
getString(R.string.task_profile_delete),
R.drawable.ic_task_delete
) {
euiccChannelManager.beginTrackedOperationBlocking(slotId, portId) {
euiccChannelManager.findEuiccChannelByPort(
slotId,
portId
)!!.lpa.deleteProfile(iccid)
preferenceRepository.notificationDeleteFlow.first()
}
}
}

View file

@ -3,15 +3,16 @@ package im.angry.openeuicc.ui
import android.app.Dialog
import android.os.Bundle
import android.text.Editable
import android.util.Log
import android.widget.EditText
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import im.angry.openeuicc.common.R
import im.angry.openeuicc.util.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import java.lang.Exception
class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
companion object {
@ -66,23 +67,23 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false
requireParentFragment().lifecycleScope.launch {
ensureEuiccChannelManager()
euiccChannelManagerService.waitForForegroundTask()
euiccChannelManagerService.launchProfileDeleteTask(
slotId,
portId,
requireArguments().getString("iccid")!!
)!!.onStart {
lifecycleScope.launch {
try {
doDelete()
} catch (e: Exception) {
Log.d(ProfileDownloadFragment.TAG, "Error deleting profile")
Log.d(ProfileDownloadFragment.TAG, Log.getStackTraceString(e))
} finally {
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
(parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
}
dismiss()
}.collect()
}
}
}
private suspend fun doDelete() = beginTrackedOperation {
channel.lpa.deleteProfile(requireArguments().getString("iccid")!!)
preferenceRepository.notificationDeleteFlow.first()
}
}

View file

@ -1,5 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View file

@ -36,7 +36,6 @@
<string name="task_notification">Long-running Tasks</string>
<string name="task_profile_download">Downloading eSIM profile</string>
<string name="task_profile_rename">Renaming eSIM profile</string>
<string name="task_profile_delete">Deleting eSIM profile</string>
<string name="profile_download">New eSIM</string>
<string name="profile_download_server">Server (RSP / SM-DP+)</string>