refactor: EuiccChannelFragmentUtils #164

Merged
PeterCxy merged 4 commits from septs/OpenEUICC:refactor-euicc-channel-fragment into master 2025-03-08 16:52:20 +01:00
3 changed files with 77 additions and 68 deletions

View file

@ -20,14 +20,11 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
private const val FIELD_ICCID = "iccid" private const val FIELD_ICCID = "iccid"
private const val FIELD_NAME = "name" private const val FIELD_NAME = "name"
fun newInstance(slotId: Int, portId: Int, iccid: String, name: String): ProfileDeleteFragment { fun newInstance(slotId: Int, portId: Int, iccid: String, name: String) =
val instance = newInstanceEuicc(ProfileDeleteFragment::class.java, slotId, portId) newInstanceEuicc(ProfileDeleteFragment::class.java, slotId, portId) {
instance.requireArguments().apply {
putString(FIELD_ICCID, iccid) putString(FIELD_ICCID, iccid)
putString(FIELD_NAME, name) putString(FIELD_NAME, name)
} }
return instance
}
} }
private val iccid by lazy { private val iccid by lazy {
@ -91,19 +88,12 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker {
requireParentFragment().lifecycleScope.launch { requireParentFragment().lifecycleScope.launch {
ensureEuiccChannelManager() ensureEuiccChannelManager()
euiccChannelManagerService.waitForForegroundTask() euiccChannelManagerService.waitForForegroundTask()
euiccChannelManagerService.launchProfileDeleteTask(slotId, portId, iccid).onStart { euiccChannelManagerService.launchProfileDeleteTask(slotId, portId, iccid)
if (parentFragment is EuiccProfilesChangedListener) { .onStart {
// Trigger a refresh in the parent fragment -- it should wait until parentFragment?.notifyEuiccProfilesChanged()
// any foreground task is completed before actually doing a refresh runCatching(::dismiss)
(parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
} }
.waitDone()
try {
dismiss()
} catch (e: IllegalStateException) {
// Ignored
}
}.waitDone()
} }
} }
} }

View file

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.Toast import android.widget.Toast
import androidx.annotation.StringRes
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
@ -18,15 +19,15 @@ import net.typeblog.lpac_jni.LocalProfileAssistant
class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragmentMarker { class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragmentMarker {
companion object { companion object {
private const val FIELD_ICCID = "iccid"
private const val FIELD_CURRENT_NAME = "currentName"
const val TAG = "ProfileRenameFragment" const val TAG = "ProfileRenameFragment"
fun newInstance(slotId: Int, portId: Int, iccid: String, currentName: String): ProfileRenameFragment { fun newInstance(slotId: Int, portId: Int, iccid: String, currentName: String) =
val instance = newInstanceEuicc(ProfileRenameFragment::class.java, slotId, portId) newInstanceEuicc(ProfileRenameFragment::class.java, slotId, portId) {
instance.requireArguments().apply { putString(FIELD_ICCID, iccid)
putString("iccid", iccid) putString(FIELD_CURRENT_NAME, currentName)
putString("currentName", currentName)
}
return instance
} }
} }
@ -36,6 +37,14 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
private var renaming = false private var renaming = false
private val iccid: String by lazy {
requireArguments().getString(FIELD_ICCID)!!
}
private val currentName: String by lazy {
requireArguments().getString(FIELD_CURRENT_NAME)!!
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -54,7 +63,7 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
profileRenameNewName.editText!!.setText(requireArguments().getString("currentName")) profileRenameNewName.editText!!.setText(currentName)
toolbar.apply { toolbar.apply {
setTitle(R.string.rename) setTitle(R.string.rename)
setNavigationOnClickListener { setNavigationOnClickListener {
@ -78,12 +87,8 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
} }
} }
private fun showErrorAndCancel(errorStrRes: Int) { private fun showErrorAndCancel(@StringRes resId: Int) {
Toast.makeText( Toast.makeText(requireContext(), resId, Toast.LENGTH_LONG).show()
requireContext(),
errorStrRes,
Toast.LENGTH_LONG
).show()
renaming = false renaming = false
progress.visibility = View.GONE progress.visibility = View.GONE
@ -94,17 +99,15 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
progress.isIndeterminate = true progress.isIndeterminate = true
progress.visibility = View.VISIBLE progress.visibility = View.VISIBLE
val newName = profileRenameNewName.editText!!.text.toString().trim()
lifecycleScope.launch { lifecycleScope.launch {
ensureEuiccChannelManager() ensureEuiccChannelManager()
euiccChannelManagerService.waitForForegroundTask() euiccChannelManagerService.waitForForegroundTask()
val res = euiccChannelManagerService.launchProfileRenameTask( val response = euiccChannelManagerService
slotId, .launchProfileRenameTask(slotId, portId, iccid, newName).waitDone()
portId,
requireArguments().getString("iccid")!!,
profileRenameNewName.editText!!.text.toString().trim()
).waitDone()
when (res) { when (response) {
is LocalProfileAssistant.ProfileNameTooLongException -> { is LocalProfileAssistant.ProfileNameTooLongException -> {
showErrorAndCancel(R.string.profile_rename_too_long) showErrorAndCancel(R.string.profile_rename_too_long)
} }
@ -118,15 +121,9 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
} }
else -> { else -> {
if (parentFragment is EuiccProfilesChangedListener) { parentFragment?.notifyEuiccProfilesChanged()
(parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
}
try { runCatching(::dismiss)
dismiss()
} catch (e: IllegalStateException) {
// Ignored
}
} }
} }
} }

View file

@ -7,43 +7,65 @@ import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.service.EuiccChannelManagerService import im.angry.openeuicc.service.EuiccChannelManagerService
import im.angry.openeuicc.ui.BaseEuiccAccessActivity import im.angry.openeuicc.ui.BaseEuiccAccessActivity
interface EuiccChannelFragmentMarker: OpenEuiccContextMarker private const val FIELD_SLOT_ID = "slotId"
private const val FIELD_PORT_ID = "portId"
interface EuiccChannelFragmentMarker : OpenEuiccContextMarker
private typealias BundleSetter = Bundle.() -> Unit
// We must use extension functions because there is no way to add bounds to the type of "self" // We must use extension functions because there is no way to add bounds to the type of "self"
// in the definition of an interface, so the only way is to limit where the extension functions // in the definition of an interface, so the only way is to limit where the extension functions
// can be applied. // can be applied.
fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int, portId: Int, addArguments: Bundle.() -> Unit = {}): T where T: Fragment, T: EuiccChannelFragmentMarker { fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int, portId: Int, addArguments: BundleSetter = {}): T
val instance = clazz.newInstance() where T : Fragment, T : EuiccChannelFragmentMarker =
instance.arguments = Bundle().apply { clazz.getDeclaredConstructor().newInstance().apply {
putInt("slotId", slotId) arguments = Bundle()
putInt("portId", portId) arguments!!.putInt(FIELD_SLOT_ID, slotId)
addArguments() arguments!!.putInt(FIELD_PORT_ID, portId)
arguments!!.addArguments()
} }
return instance
}
// Convenient methods to avoid using `channel` for these // Convenient methods to avoid using `channel` for these
// `channel` requires that the channel actually exists in EuiccChannelManager, which is // `channel` requires that the channel actually exists in EuiccChannelManager, which is
// not always the case during operations such as switching // not always the case during operations such as switching
val <T> T.slotId: Int where T: Fragment, T: EuiccChannelFragmentMarker val <T> T.slotId: Int
get() = requireArguments().getInt("slotId") where T : Fragment, T : EuiccChannelFragmentMarker
val <T> T.portId: Int where T: Fragment, T: EuiccChannelFragmentMarker get() = requireArguments().getInt(FIELD_SLOT_ID)
get() = requireArguments().getInt("portId") val <T> T.portId: Int
val <T> T.isUsb: Boolean where T: Fragment, T: EuiccChannelFragmentMarker where T : Fragment, T : EuiccChannelFragmentMarker
get() = requireArguments().getInt("slotId") == EuiccChannelManager.USB_CHANNEL_ID get() = requireArguments().getInt(FIELD_PORT_ID)
val <T> T.isUsb: Boolean
where T : Fragment, T : EuiccChannelFragmentMarker
get() = slotId == EuiccChannelManager.USB_CHANNEL_ID
val <T> T.euiccChannelManager: EuiccChannelManager where T: Fragment, T: OpenEuiccContextMarker private fun <T> T.requireEuiccActivity(): BaseEuiccAccessActivity
get() = (requireActivity() as BaseEuiccAccessActivity).euiccChannelManager where T : Fragment, T : OpenEuiccContextMarker =
val <T> T.euiccChannelManagerService: EuiccChannelManagerService where T: Fragment, T: OpenEuiccContextMarker requireActivity() as BaseEuiccAccessActivity
get() = (requireActivity() as BaseEuiccAccessActivity).euiccChannelManagerService
suspend fun <T, R> T.withEuiccChannel(fn: suspend (EuiccChannel) -> R): R where T : Fragment, T : EuiccChannelFragmentMarker { val <T> T.euiccChannelManager: EuiccChannelManager
where T : Fragment, T : OpenEuiccContextMarker
get() = requireEuiccActivity().euiccChannelManager
val <T> T.euiccChannelManagerService: EuiccChannelManagerService
where T : Fragment, T : OpenEuiccContextMarker
get() = requireEuiccActivity().euiccChannelManagerService
suspend fun <T, R> T.withEuiccChannel(fn: suspend (EuiccChannel) -> R): R
where T : Fragment, T : EuiccChannelFragmentMarker {
ensureEuiccChannelManager() ensureEuiccChannelManager()
return euiccChannelManager.withEuiccChannel(slotId, portId, fn) return euiccChannelManager.withEuiccChannel(slotId, portId, fn)
} }
suspend fun <T> T.ensureEuiccChannelManager() where T: Fragment, T: OpenEuiccContextMarker = suspend fun <T> T.ensureEuiccChannelManager() where T : Fragment, T : OpenEuiccContextMarker =
(requireActivity() as BaseEuiccAccessActivity).euiccChannelManagerLoaded.await() requireEuiccActivity().euiccChannelManagerLoaded.await()
fun <T> T.notifyEuiccProfilesChanged() where T : Fragment {
if (this !is EuiccProfilesChangedListener) return
// Trigger a refresh in the parent fragment -- it should wait until
// any foreground task is completed before actually doing a refresh
this.onEuiccProfilesChanged()
}
interface EuiccProfilesChangedListener { interface EuiccProfilesChangedListener {
fun onEuiccProfilesChanged() fun onEuiccProfilesChanged()