diff --git a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt
index 760f1af..9e900b3 100644
--- a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt
+++ b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt
@@ -495,4 +495,19 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
preferenceRepository.notificationSwitchFlow.first()
}
}
+
+ fun launchMemoryReset(slotId: Int, portId: Int): ForegroundTaskSubscriberFlow =
+ launchForegroundTask(
+ getString(R.string.task_euicc_memory_reset),
+ getString(R.string.task_euicc_memory_reset_failure),
+ R.drawable.ic_task_delete
+ ) {
+ euiccChannelManager.beginTrackedOperation(slotId, portId) {
+ euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
+ channel.lpa.euiccMemoryReset()
+ }
+
+ preferenceRepository.euiccMemoryResetFlow.first()
+ }
+ }
}
\ No newline at end of file
diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt
index e02b7be..a23c166 100644
--- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt
+++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt
@@ -56,6 +56,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
private lateinit var fab: FloatingActionButton
private lateinit var profileList: RecyclerView
private var logicalSlotId: Int = -1
+ private var eid: String? = null
private val adapter = EuiccProfileAdapter()
@@ -160,38 +161,8 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
}
R.id.euicc_memory_reset -> {
- AlertDialog.Builder(requireContext()).apply {
- setTitle(R.string.euicc_memory_reset_title)
- setMessage(R.string.euicc_memory_reset_message)
- setPositiveButton(android.R.string.ok) { dialog, _ ->
- dialog.dismiss()
- lifecycleScope.launch {
- ensureEuiccChannelManager()
- euiccChannelManagerService.waitForForegroundTask()
-
-// val err = euiccChannelManagerService.launchMemoryResetTask(
-// slotId,
-// portId
-// ).waitDone()
-//
-// if (err != null) {
-// withContext(Dispatchers.Main) {
-// AlertDialog.Builder(requireContext()).apply {
-// setMessage(R.string.euicc_memory_reset_failed)
-// setPositiveButton(android.R.string.ok) { dialog, _ ->
-// dialog.dismiss()
-// }
-// show()
-// }
-// }
-// }
- }
- }
- setNegativeButton(android.R.string.cancel) { dialog, _ ->
- dialog.dismiss()
- }
- show()
- }
+ EuiccMemoryResetFragment.newInstance(slotId, portId, eid!!)
+ .show(childFragmentManager, EuiccMemoryResetFragment.TAG)
true
}
@@ -234,6 +205,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
val profiles = withEuiccChannel { channel ->
logicalSlotId = channel.logicalSlotId
+ eid = channel.lpa.eID
euiccChannelManager.notifyEuiccProfilesChanged(channel.logicalSlotId)
if (unfilteredProfileListFlow.value)
channel.lpa.profiles
diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccMemoryResetFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccMemoryResetFragment.kt
new file mode 100644
index 0000000..418e156
--- /dev/null
+++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccMemoryResetFragment.kt
@@ -0,0 +1,129 @@
+package im.angry.openeuicc.ui
+
+import android.graphics.Typeface
+import android.os.Bundle
+import android.text.Editable
+import android.util.Log
+import android.widget.EditText
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import im.angry.openeuicc.common.R
+import im.angry.openeuicc.service.EuiccChannelManagerService.Companion.waitDone
+import im.angry.openeuicc.util.EuiccChannelFragmentMarker
+import im.angry.openeuicc.util.EuiccProfilesChangedListener
+import im.angry.openeuicc.util.ensureEuiccChannelManager
+import im.angry.openeuicc.util.euiccChannelManagerService
+import im.angry.openeuicc.util.newInstanceEuicc
+import im.angry.openeuicc.util.portId
+import im.angry.openeuicc.util.slotId
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+
+class EuiccMemoryResetFragment : DialogFragment(), EuiccChannelFragmentMarker {
+ companion object {
+ const val TAG = "EuiccMemoryResetFragment"
+
+ private const val FIELD_EID = "eid"
+
+ fun newInstance(slotId: Int, portId: Int, eid: String) =
+ newInstanceEuicc(EuiccMemoryResetFragment::class.java, slotId, portId).apply {
+ requireArguments().putString(FIELD_EID, eid)
+ }
+ }
+
+ private val eid: String by lazy { requireArguments().getString(FIELD_EID)!! }
+
+ private val confirmText: String by lazy {
+ getString(R.string.euicc_memory_reset_confirm_text, eid.takeLast(8))
+ }
+
+ private inline val isMatched: Boolean
+ get() = editText.text.toString() == confirmText
+
+ private var confirmed = false
+
+ private var toast: Toast? = null
+ set(value) {
+ toast?.cancel()
+ field = value
+ value?.show()
+ }
+
+ private val editText by lazy {
+ val edit = EditText(requireContext())
+ edit.isLongClickable = false
+ edit.typeface = Typeface.MONOSPACE
+ edit.hint = Editable.Factory.getInstance()
+ .newEditable(getString(R.string.euicc_memory_reset_hint_text, confirmText))
+ edit
+ }
+
+ private inline val alertDialog: AlertDialog
+ get() = requireDialog() as AlertDialog
+
+ override fun onCreateDialog(savedInstanceState: Bundle?) =
+ AlertDialog.Builder(requireContext(), R.style.AlertDialogTheme)
+ .setTitle(R.string.euicc_memory_reset_title)
+ .setMessage(getString(R.string.euicc_memory_reset_message, eid, confirmText))
+ .setView(editText)
+ // Set listener to null to prevent auto closing
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(R.string.euicc_memory_reset_invoke_button, null)
+ .create()
+
+ override fun onResume() {
+ super.onResume()
+ alertDialog.setCanceledOnTouchOutside(false)
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE)
+ .setOnClickListener { if (!confirmed) confirmation() }
+ alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE)
+ .setOnClickListener { if (!confirmed) dismiss() }
+ }
+
+ private fun confirmation() {
+ toast?.cancel()
+ if (!isMatched) {
+ Log.d(TAG, buildString {
+ appendLine("User input is mismatch:")
+ appendLine(editText.text)
+ appendLine(confirmText)
+ })
+ val resId = R.string.toast_euicc_memory_reset_confirm_text_mismatched
+ toast = Toast.makeText(requireContext(), resId, Toast.LENGTH_LONG)
+ return
+ }
+ confirmed = true
+ preventUserAction()
+
+ requireParentFragment().lifecycleScope.launch {
+ ensureEuiccChannelManager()
+ euiccChannelManagerService.waitForForegroundTask()
+
+ euiccChannelManagerService.launchMemoryReset(slotId, portId)
+ .onStart {
+ emitEuiccProfilesChanged(parentFragment)
+ runCatching(::dismiss)
+ }
+ .waitDone()
+ }
+ }
+
+ private fun preventUserAction() {
+ editText.isEnabled = false
+ alertDialog.setCancelable(false)
+ alertDialog.setCanceledOnTouchOutside(false)
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
+ alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false
+ }
+}
+
+private fun emitEuiccProfilesChanged(fragment: Fragment?) {
+ if (fragment is EuiccProfilesChangedListener) {
+ // Trigger a refresh in the parent fragment -- it should wait until
+ // any foreground task is completed before actually doing a refresh
+ fragment.onEuiccProfilesChanged()
+ }
+}
\ No newline at end of file
diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml
index 5674c0d..d5e7c8b 100644
--- a/app-common/src/main/res/values/strings.xml
+++ b/app-common/src/main/res/values/strings.xml
@@ -30,6 +30,7 @@
Cannot switch to new eSIM profile.
Confirmation string mismatch
+ Confirmation string mismatch
ICCID copied to clipboard
Serial Number copied to clipboard
EID copied to clipboard
@@ -48,6 +49,8 @@
Failed to delete eSIM profile
Switching eSIM profile
Failed to switch eSIM profile
+ Erasing eSIM chip
+ Failed to erase eSIM chip
New eSIM
Server (RSP / SM-DP+)
@@ -145,7 +148,10 @@
Erase the Chip
Erase the Chip
- I confirm to clear all profiles on this chip and understand that this operation is irreversible.
+ I confirm to delete all profiles on this chip and understand that this operation is irreversible.\n\nEID: %s\n\n%s
+ Type \'%s\' here to confirm erase the chip
+ I CONFIRM TO ERASE ALL PROFILES WITH EID ENDING WITH %s AND UNDERSTAND THIS IRREVERSIBLE
+ Erase
Yes
No