Compare commits
No commits in common. "2721f9127723d3d123109f452e6cb368243f5283" and "cf5704be4220e11221bdf71c88ddfe4527c8c3a7" have entirely different histories.
2721f91277
...
cf5704be42
4 changed files with 28 additions and 79 deletions
|
@ -24,8 +24,6 @@ import kotlinx.coroutines.flow.takeWhile
|
||||||
import kotlinx.coroutines.flow.transformWhile
|
import kotlinx.coroutines.flow.transformWhile
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
|
||||||
import kotlinx.coroutines.yield
|
|
||||||
import net.typeblog.lpac_jni.ProfileDownloadCallback
|
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 =
|
val channel =
|
||||||
NotificationChannelCompat.Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
|
NotificationChannelCompat.Builder(CHANNEL_ID, NotificationManagerCompat.IMPORTANCE_LOW)
|
||||||
.setName(getString(R.string.task_notification))
|
.setName(getString(R.string.task_notification))
|
||||||
|
@ -119,7 +117,6 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
.setProgress(100, state.progress, state.progress == 0)
|
.setProgress(100, state.progress, state.progress == 0)
|
||||||
.setSmallIcon(iconRes)
|
.setSmallIcon(iconRes)
|
||||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
.setOngoing(true)
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
if (state.progress == 0) {
|
if (state.progress == 0) {
|
||||||
|
@ -127,10 +124,6 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
} else if (checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
} else if (checkSelfPermission(android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
||||||
NotificationManagerCompat.from(this).notify(FOREGROUND_ID, notification)
|
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 {
|
} else {
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
}
|
}
|
||||||
|
@ -140,14 +133,11 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
* Launch a potentially blocking foreground task in this service's lifecycle context.
|
* Launch a potentially blocking foreground task in this service's lifecycle context.
|
||||||
* This function does not block, but returns a Flow that emits ForegroundTaskState
|
* 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
|
* 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
|
* always ForegroundTaskState.Done.
|
||||||
* foreground task to run.
|
|
||||||
*
|
*
|
||||||
* The task closure is expected to update foregroundTaskState whenever appropriate.
|
* The task closure is expected to update foregroundTaskState whenever appropriate.
|
||||||
* If a foreground task is already running, this function returns null.
|
* 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.
|
* The function will set the state back to Idle once it sees ForegroundTaskState.Done.
|
||||||
*/
|
*/
|
||||||
private fun launchForegroundTask(
|
private fun launchForegroundTask(
|
||||||
|
@ -168,32 +158,21 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
// Wait until our self-start command has succeeded.
|
// Wait until our self-start command has succeeded.
|
||||||
// We can only call startForeground() after that
|
// We can only call startForeground() after that
|
||||||
val res = withTimeoutOrNull(30 * 1000) {
|
foregroundStarted.first()
|
||||||
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)
|
updateForegroundNotification(title, iconRes)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
this@EuiccChannelManagerService.task()
|
this@EuiccChannelManagerService.task()
|
||||||
}
|
}
|
||||||
// This update will be sent by the subscriber (as shown below)
|
|
||||||
foregroundTaskState.value = ForegroundTaskState.Done(null)
|
foregroundTaskState.value = ForegroundTaskState.Done(null)
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
foregroundTaskState.value = ForegroundTaskState.Done(t)
|
foregroundTaskState.value = ForegroundTaskState.Done(t)
|
||||||
} finally {
|
} finally {
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateForegroundNotification(title, iconRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should be the only task running, so we can subscribe to foregroundTaskState
|
// 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.
|
// it has been completed by that point.
|
||||||
return foregroundTaskState.transformWhile {
|
return foregroundTaskState.transformWhile {
|
||||||
// Also update our notification when we see an update
|
// Also update our notification when we see an update
|
||||||
// But ignore the first progress = 0 update -- that is the current value.
|
withContext(Dispatchers.Main) {
|
||||||
// we need that to be handled by the main coroutine after it finishes.
|
updateForegroundNotification(title, iconRes)
|
||||||
if (it !is ForegroundTaskState.InProgress || it.progress != 0) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
updateForegroundNotification(title, iconRes)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
emit(it)
|
emit(it)
|
||||||
it !is ForegroundTaskState.Done
|
it !is ForegroundTaskState.Done
|
||||||
}.onStart {
|
}.onStart {
|
||||||
// When this Flow is started, we unblock the coroutine launched above by
|
// When this Flow is started, we unblock the coroutine launched above by
|
||||||
// self-starting as a foreground service.
|
// self-starting as a foreground service.
|
||||||
withContext(Dispatchers.Main) {
|
startForegroundService(
|
||||||
startForegroundService(
|
Intent(
|
||||||
Intent(
|
this@EuiccChannelManagerService,
|
||||||
this@EuiccChannelManagerService,
|
this@EuiccChannelManagerService::class.java
|
||||||
this@EuiccChannelManagerService::class.java
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
}.onCompletion { foregroundTaskState.value = ForegroundTaskState.Idle }
|
}.onCompletion { foregroundTaskState.value = ForegroundTaskState.Idle }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,23 +262,4 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
throw RuntimeException("Profile not renamed")
|
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -3,15 +3,16 @@ package im.angry.openeuicc.ui
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
|
import android.util.Log
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import im.angry.openeuicc.common.R
|
import im.angry.openeuicc.common.R
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.onStart
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.lang.Exception
|
||||||
|
|
||||||
class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
|
class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -66,23 +67,23 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
|
||||||
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
|
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
|
||||||
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false
|
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false
|
||||||
|
|
||||||
requireParentFragment().lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
ensureEuiccChannelManager()
|
try {
|
||||||
euiccChannelManagerService.waitForForegroundTask()
|
doDelete()
|
||||||
|
} catch (e: Exception) {
|
||||||
euiccChannelManagerService.launchProfileDeleteTask(
|
Log.d(ProfileDownloadFragment.TAG, "Error deleting profile")
|
||||||
slotId,
|
Log.d(ProfileDownloadFragment.TAG, Log.getStackTraceString(e))
|
||||||
portId,
|
} finally {
|
||||||
requireArguments().getString("iccid")!!
|
|
||||||
)!!.onStart {
|
|
||||||
if (parentFragment is EuiccProfilesChangedListener) {
|
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()
|
(parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
dismiss()
|
dismiss()
|
||||||
}.collect()
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun doDelete() = beginTrackedOperation {
|
||||||
|
channel.lpa.deleteProfile(requireArguments().getString("iccid")!!)
|
||||||
|
preferenceRepository.notificationDeleteFlow.first()
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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>
|
|
|
@ -36,7 +36,6 @@
|
||||||
<string name="task_notification">Long-running Tasks</string>
|
<string name="task_notification">Long-running Tasks</string>
|
||||||
<string name="task_profile_download">Downloading eSIM profile</string>
|
<string name="task_profile_download">Downloading eSIM profile</string>
|
||||||
<string name="task_profile_rename">Renaming 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">New eSIM</string>
|
||||||
<string name="profile_download_server">Server (RSP / SM-DP+)</string>
|
<string name="profile_download_server">Server (RSP / SM-DP+)</string>
|
||||||
|
|
Loading…
Add table
Reference in a new issue