diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt index 7f82f22..38d1bc6 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDeleteFragment.kt @@ -20,13 +20,10 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker { private const val FIELD_ICCID = "iccid" private const val FIELD_NAME = "name" - fun newInstance(slotId: Int, portId: Int, iccid: String, name: String): ProfileDeleteFragment { - val instance = newInstanceEuicc(ProfileDeleteFragment::class.java, slotId, portId) - instance.requireArguments().apply { + fun newInstance(slotId: Int, portId: Int, iccid: String, name: String) = + newInstanceEuicc(ProfileDeleteFragment::class.java, slotId, portId) { putString(FIELD_ICCID, iccid) putString(FIELD_NAME, name) - } - return instance } } @@ -91,19 +88,12 @@ class ProfileDeleteFragment : DialogFragment(), EuiccChannelFragmentMarker { requireParentFragment().lifecycleScope.launch { ensureEuiccChannelManager() euiccChannelManagerService.waitForForegroundTask() - euiccChannelManagerService.launchProfileDeleteTask(slotId, portId, iccid).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 - (parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged() + euiccChannelManagerService.launchProfileDeleteTask(slotId, portId, iccid) + .onStart { + parentFragment?.notifyEuiccProfilesChanged() + runCatching(::dismiss) } - - try { - dismiss() - } catch (e: IllegalStateException) { - // Ignored - } - }.waitDone() + .waitDone() } } } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt index 25c5273..c588254 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt @@ -7,6 +7,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ProgressBar import android.widget.Toast +import androidx.annotation.StringRes import androidx.appcompat.widget.Toolbar import androidx.lifecycle.lifecycleScope import com.google.android.material.textfield.TextInputLayout @@ -18,16 +19,16 @@ import net.typeblog.lpac_jni.LocalProfileAssistant class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragmentMarker { companion object { + private const val FIELD_ICCID = "iccid" + private const val FIELD_CURRENT_NAME = "currentName" + const val TAG = "ProfileRenameFragment" - fun newInstance(slotId: Int, portId: Int, iccid: String, currentName: String): ProfileRenameFragment { - val instance = newInstanceEuicc(ProfileRenameFragment::class.java, slotId, portId) - instance.requireArguments().apply { - putString("iccid", iccid) - putString("currentName", currentName) + fun newInstance(slotId: Int, portId: Int, iccid: String, currentName: String) = + newInstanceEuicc(ProfileRenameFragment::class.java, slotId, portId) { + putString(FIELD_ICCID, iccid) + putString(FIELD_CURRENT_NAME, currentName) } - return instance - } } private lateinit var toolbar: Toolbar @@ -36,6 +37,14 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment 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( inflater: LayoutInflater, container: ViewGroup?, @@ -54,7 +63,7 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - profileRenameNewName.editText!!.setText(requireArguments().getString("currentName")) + profileRenameNewName.editText!!.setText(currentName) toolbar.apply { setTitle(R.string.rename) setNavigationOnClickListener { @@ -78,12 +87,8 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment } } - private fun showErrorAndCancel(errorStrRes: Int) { - Toast.makeText( - requireContext(), - errorStrRes, - Toast.LENGTH_LONG - ).show() + private fun showErrorAndCancel(@StringRes resId: Int) { + Toast.makeText(requireContext(), resId, Toast.LENGTH_LONG).show() renaming = false progress.visibility = View.GONE @@ -94,17 +99,15 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment progress.isIndeterminate = true progress.visibility = View.VISIBLE + val newName = profileRenameNewName.editText!!.text.toString().trim() + lifecycleScope.launch { ensureEuiccChannelManager() euiccChannelManagerService.waitForForegroundTask() - val res = euiccChannelManagerService.launchProfileRenameTask( - slotId, - portId, - requireArguments().getString("iccid")!!, - profileRenameNewName.editText!!.text.toString().trim() - ).waitDone() + val response = euiccChannelManagerService + .launchProfileRenameTask(slotId, portId, iccid, newName).waitDone() - when (res) { + when (response) { is LocalProfileAssistant.ProfileNameTooLongException -> { showErrorAndCancel(R.string.profile_rename_too_long) } @@ -118,15 +121,9 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment } else -> { - if (parentFragment is EuiccProfilesChangedListener) { - (parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged() - } + parentFragment?.notifyEuiccProfilesChanged() - try { - dismiss() - } catch (e: IllegalStateException) { - // Ignored - } + runCatching(::dismiss) } } } diff --git a/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt b/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt index 3f3c4ee..b44bef8 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt @@ -7,43 +7,65 @@ import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.service.EuiccChannelManagerService 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" // in the definition of an interface, so the only way is to limit where the extension functions // can be applied. -fun newInstanceEuicc(clazz: Class, slotId: Int, portId: Int, addArguments: Bundle.() -> Unit = {}): T where T: Fragment, T: EuiccChannelFragmentMarker { - val instance = clazz.newInstance() - instance.arguments = Bundle().apply { - putInt("slotId", slotId) - putInt("portId", portId) - addArguments() +fun newInstanceEuicc(clazz: Class, slotId: Int, portId: Int, addArguments: BundleSetter = {}): T + where T : Fragment, T : EuiccChannelFragmentMarker = + clazz.getDeclaredConstructor().newInstance().apply { + arguments = Bundle() + arguments!!.putInt(FIELD_SLOT_ID, slotId) + arguments!!.putInt(FIELD_PORT_ID, portId) + arguments!!.addArguments() } - return instance -} // Convenient methods to avoid using `channel` for these // `channel` requires that the channel actually exists in EuiccChannelManager, which is // not always the case during operations such as switching -val T.slotId: Int where T: Fragment, T: EuiccChannelFragmentMarker - get() = requireArguments().getInt("slotId") -val T.portId: Int where T: Fragment, T: EuiccChannelFragmentMarker - get() = requireArguments().getInt("portId") -val T.isUsb: Boolean where T: Fragment, T: EuiccChannelFragmentMarker - get() = requireArguments().getInt("slotId") == EuiccChannelManager.USB_CHANNEL_ID +val T.slotId: Int + where T : Fragment, T : EuiccChannelFragmentMarker + get() = requireArguments().getInt(FIELD_SLOT_ID) +val T.portId: Int + where T : Fragment, T : EuiccChannelFragmentMarker + get() = requireArguments().getInt(FIELD_PORT_ID) +val T.isUsb: Boolean + where T : Fragment, T : EuiccChannelFragmentMarker + get() = slotId == EuiccChannelManager.USB_CHANNEL_ID -val T.euiccChannelManager: EuiccChannelManager where T: Fragment, T: OpenEuiccContextMarker - get() = (requireActivity() as BaseEuiccAccessActivity).euiccChannelManager -val T.euiccChannelManagerService: EuiccChannelManagerService where T: Fragment, T: OpenEuiccContextMarker - get() = (requireActivity() as BaseEuiccAccessActivity).euiccChannelManagerService +private fun T.requireEuiccActivity(): BaseEuiccAccessActivity + where T : Fragment, T : OpenEuiccContextMarker = + requireActivity() as BaseEuiccAccessActivity -suspend fun T.withEuiccChannel(fn: suspend (EuiccChannel) -> R): R where T : Fragment, T : EuiccChannelFragmentMarker { +val T.euiccChannelManager: EuiccChannelManager + where T : Fragment, T : OpenEuiccContextMarker + get() = requireEuiccActivity().euiccChannelManager + +val T.euiccChannelManagerService: EuiccChannelManagerService + where T : Fragment, T : OpenEuiccContextMarker + get() = requireEuiccActivity().euiccChannelManagerService + +suspend fun T.withEuiccChannel(fn: suspend (EuiccChannel) -> R): R + where T : Fragment, T : EuiccChannelFragmentMarker { ensureEuiccChannelManager() return euiccChannelManager.withEuiccChannel(slotId, portId, fn) } -suspend fun T.ensureEuiccChannelManager() where T: Fragment, T: OpenEuiccContextMarker = - (requireActivity() as BaseEuiccAccessActivity).euiccChannelManagerLoaded.await() +suspend fun T.ensureEuiccChannelManager() where T : Fragment, T : OpenEuiccContextMarker = + requireEuiccActivity().euiccChannelManagerLoaded.await() + +fun 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 { fun onEuiccProfilesChanged()