package im.angry.openeuicc.util import android.os.Build import android.os.Bundle import androidx.fragment.app.Fragment import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.service.EuiccChannelManagerService import im.angry.openeuicc.ui.BaseEuiccAccessActivity private const val FIELD_SLOT_ID = "slotId" private const val FIELD_PORT_ID = "portId" private const val FIELD_SE_ID = "seId" 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, seId: EuiccChannel.SecureElementId, 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!!.putParcelable(FIELD_SE_ID, seId) arguments!!.addArguments() } // 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(FIELD_SLOT_ID) val T.portId: Int where T : Fragment, T : EuiccChannelFragmentMarker get() = requireArguments().getInt(FIELD_PORT_ID) val T.seId: EuiccChannel.SecureElementId where T : Fragment, T : EuiccChannelFragmentMarker get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { requireArguments().getParcelable( FIELD_SE_ID, EuiccChannel.SecureElementId::class.java )!! } else { @Suppress("DEPRECATION") requireArguments().getParcelable(FIELD_SE_ID)!! } val T.isUsb: Boolean where T : Fragment, T : EuiccChannelFragmentMarker get() = slotId == EuiccChannelManager.USB_CHANNEL_ID private fun T.requireEuiccActivity(): BaseEuiccAccessActivity where T : Fragment, T : OpenEuiccContextMarker = requireActivity() as BaseEuiccAccessActivity 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, seId, fn ) } 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() }