From c8075885214edc3064a5c3dc97869cb596947041 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Thu, 1 Feb 2024 21:43:31 -0500 Subject: [PATCH 1/4] feat: Direct profile download interface with slot selection This can be used by both the system LuiActivity and potentially in the unprivileged version as a carrier interface as well. --- app-common/src/main/AndroidManifest.xml | 5 ++ .../ui/BaseMaterialDialogFragment.kt | 7 ++ .../ui/DirectProfileDownloadActivity.kt | 30 ++++++++ .../angry/openeuicc/ui/SlotSelectFragment.kt | 76 +++++++++++++++++++ .../res/layout/fragment_profile_download.xml | 3 +- .../main/res/layout/fragment_slot_select.xml | 27 +++++++ .../main/res/menu/fragment_slot_select.xml | 9 +++ app-common/src/main/res/values/strings.xml | 3 + app-common/src/main/res/values/themes.xml | 10 +++ .../java/im/angry/openeuicc/ui/LuiActivity.kt | 3 +- 10 files changed, 170 insertions(+), 3 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt create mode 100644 app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt create mode 100644 app-common/src/main/res/layout/fragment_slot_select.xml create mode 100644 app-common/src/main/res/menu/fragment_slot_select.xml diff --git a/app-common/src/main/AndroidManifest.xml b/app-common/src/main/AndroidManifest.xml index debf765..bf80c5e 100644 --- a/app-common/src/main/AndroidManifest.xml +++ b/app-common/src/main/AndroidManifest.xml @@ -16,6 +16,11 @@ android:name="im.angry.openeuicc.ui.NotificationsActivity" android:label="@string/profile_notifications" /> + + by lazy { + openEuiccApplication.euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.fragment_slot_select, container, false) + + toolbar = view.findViewById(R.id.toolbar) + toolbar.setTitle(R.string.slot_select) + toolbar.inflateMenu(R.menu.fragment_slot_select) + + val adapter = ArrayAdapter(inflater.context, R.layout.spinner_item) + + spinner = view.findViewById(R.id.spinner) + spinner.adapter = adapter + + channels.forEach { channel -> + adapter.add(getString(R.string.channel_name_format, channel.logicalSlotId)) + } + + toolbar.setNavigationOnClickListener { listener.onCancel() } + toolbar.setOnMenuItemClickListener { + val channel = channels[spinner.selectedItemPosition] + listener.onSlotSelected(channel.slotId, channel.portId) + dismiss() + true + } + + return view + } + + override fun onResume() { + super.onResume() + setWidthPercent(75) + } + + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + listener.onCancel() + } +} \ No newline at end of file diff --git a/app-common/src/main/res/layout/fragment_profile_download.xml b/app-common/src/main/res/layout/fragment_profile_download.xml index acd47e4..78274dc 100644 --- a/app-common/src/main/res/layout/fragment_profile_download.xml +++ b/app-common/src/main/res/layout/fragment_profile_download.xml @@ -2,7 +2,8 @@ + android:layout_height="match_parent" + android:background="?attr/colorSurface"> + + + + + + + \ No newline at end of file diff --git a/app-common/src/main/res/menu/fragment_slot_select.xml b/app-common/src/main/res/menu/fragment_slot_select.xml new file mode 100644 index 0000000..e129008 --- /dev/null +++ b/app-common/src/main/res/menu/fragment_slot_select.xml @@ -0,0 +1,9 @@ + + + + \ 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 4916084..71440c0 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -19,6 +19,9 @@ Cannot switch to new eSIM profile. Nickname cannot be longer than 64 characters + Select Slot + Select + New eSIM Server (RSP / SM-DP+) Activation Code diff --git a/app-common/src/main/res/values/themes.xml b/app-common/src/main/res/values/themes.xml index 3fd5916..cc7104d 100644 --- a/app-common/src/main/res/values/themes.xml +++ b/app-common/src/main/res/values/themes.xml @@ -28,4 +28,14 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt b/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt index 89baf93..e60dfa7 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt @@ -11,11 +11,10 @@ class LuiActivity : AppCompatActivity() { setContentView(R.layout.activity_lui) findViewById(R.id.lui_skip).setOnClickListener { finish() } - // TODO: Allow users to select slots, and then hand over directly to ProfileDownloadFragment // TODO: Deactivate LuiActivity if there is no eSIM found. // TODO: Support pre-filled download info (from carrier apps); UX findViewById(R.id.lui_download).setOnClickListener { - startActivity(Intent(this, PrivilegedMainActivity::class.java)) + startActivity(Intent(this, DirectProfileDownloadActivity::class.java)) } } } \ No newline at end of file From a55fbd2767e59e73dd5e2abc239d14569f9f4ac4 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Thu, 1 Feb 2024 21:54:49 -0500 Subject: [PATCH 2/4] ui: Fi styling for all DialogFragments --- .../java/im/angry/openeuicc/ui/BaseMaterialDialogFragment.kt | 5 ++++- app/src/main/res/layout/fragment_slot_mapping.xml | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/BaseMaterialDialogFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/BaseMaterialDialogFragment.kt index 1e8e0d4..5c33bee 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/BaseMaterialDialogFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/BaseMaterialDialogFragment.kt @@ -6,12 +6,15 @@ import android.view.LayoutInflater import android.view.Window import androidx.appcompat.view.ContextThemeWrapper import androidx.fragment.app.DialogFragment +import com.google.android.material.color.DynamicColors import im.angry.openeuicc.common.R abstract class BaseMaterialDialogFragment: DialogFragment() { override fun onGetLayoutInflater(savedInstanceState: Bundle?): LayoutInflater { val inflater = super.onGetLayoutInflater(savedInstanceState) - return inflater.cloneInContext(ContextThemeWrapper(requireContext(), R.style.Theme_OpenEUICC)) + val wrappedContext = ContextThemeWrapper(requireContext(), R.style.Theme_OpenEUICC) + val dynamicWrappedContext = DynamicColors.wrapContextIfAvailable(wrappedContext) + return inflater.cloneInContext(dynamicWrappedContext) } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { diff --git a/app/src/main/res/layout/fragment_slot_mapping.xml b/app/src/main/res/layout/fragment_slot_mapping.xml index 0fc9faf..52a341c 100644 --- a/app/src/main/res/layout/fragment_slot_mapping.xml +++ b/app/src/main/res/layout/fragment_slot_mapping.xml @@ -8,7 +8,6 @@ android:id="@+id/toolbar" android:layout_width="0dp" android:layout_height="wrap_content" - android:theme="@style/Theme.OpenEUICC" android:elevation="4dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" From fd332bcabeae5dc10a10dadb6e63a5714a7fb591 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Thu, 1 Feb 2024 21:57:14 -0500 Subject: [PATCH 3/4] SlotSelectFragment: do not use constructor parameters --- .../ui/DirectProfileDownloadActivity.kt | 4 ++-- .../im/angry/openeuicc/ui/SlotSelectFragment.kt | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt index 18c2294..b4e40c3 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt @@ -16,7 +16,7 @@ class DirectProfileDownloadActivity : AppCompatActivity(), SlotSelectFragment.Sl openEuiccApplication.euiccChannelManager.enumerateEuiccChannels() } - SlotSelectFragment.newInstance(this@DirectProfileDownloadActivity) + SlotSelectFragment.newInstance() .show(supportFragmentManager, SlotSelectFragment.TAG) } } @@ -26,5 +26,5 @@ class DirectProfileDownloadActivity : AppCompatActivity(), SlotSelectFragment.Sl .show(supportFragmentManager, ProfileDownloadFragment.TAG) } - override fun onCancel() = finish() + override fun onSlotSelectCancelled() = finish() } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt index bbe73f3..7ad27e2 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt @@ -13,18 +13,18 @@ import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.util.openEuiccApplication import im.angry.openeuicc.util.setWidthPercent -class SlotSelectFragment(private var listener: SlotSelectedListener) : BaseMaterialDialogFragment() { +class SlotSelectFragment : BaseMaterialDialogFragment() { companion object { const val TAG = "SlotSelectFragment" - fun newInstance(listener: SlotSelectedListener): SlotSelectFragment { - return SlotSelectFragment(listener) + fun newInstance(): SlotSelectFragment { + return SlotSelectFragment() } } interface SlotSelectedListener { fun onSlotSelected(slotId: Int, portId: Int) - fun onCancel() + fun onSlotSelectCancelled() } private lateinit var toolbar: Toolbar @@ -53,10 +53,12 @@ class SlotSelectFragment(private var listener: SlotSelectedListener) : BaseMater adapter.add(getString(R.string.channel_name_format, channel.logicalSlotId)) } - toolbar.setNavigationOnClickListener { listener.onCancel() } + toolbar.setNavigationOnClickListener { + (requireActivity() as SlotSelectedListener).onSlotSelectCancelled() + } toolbar.setOnMenuItemClickListener { val channel = channels[spinner.selectedItemPosition] - listener.onSlotSelected(channel.slotId, channel.portId) + (requireActivity() as SlotSelectedListener).onSlotSelected(channel.slotId, channel.portId) dismiss() true } @@ -71,6 +73,6 @@ class SlotSelectFragment(private var listener: SlotSelectedListener) : BaseMater override fun onCancel(dialog: DialogInterface) { super.onCancel(dialog) - listener.onCancel() + (requireActivity() as SlotSelectedListener).onSlotSelectCancelled() } } \ No newline at end of file From efeaea256708a892a44502969f4f24b5b3190088 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Thu, 1 Feb 2024 22:02:35 -0500 Subject: [PATCH 4/4] ProfileDownloadFragment: support finishing activity when done --- .../ui/DirectProfileDownloadActivity.kt | 2 +- .../openeuicc/ui/EuiccChannelFragmentUtils.kt | 3 +- .../openeuicc/ui/ProfileDownloadFragment.kt | 29 +++++++++++++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt index b4e40c3..a7983a5 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/DirectProfileDownloadActivity.kt @@ -22,7 +22,7 @@ class DirectProfileDownloadActivity : AppCompatActivity(), SlotSelectFragment.Sl } override fun onSlotSelected(slotId: Int, portId: Int) { - ProfileDownloadFragment.newInstance(slotId, portId) + ProfileDownloadFragment.newInstance(slotId, portId, finishWhenDone = true) .show(supportFragmentManager, ProfileDownloadFragment.TAG) } diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt index 34f4240..f187bf4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccChannelFragmentUtils.kt @@ -8,11 +8,12 @@ import im.angry.openeuicc.util.openEuiccApplication interface EuiccFragmentMarker -fun newInstanceEuicc(clazz: Class, slotId: Int, portId: Int): T where T: Fragment, T: EuiccFragmentMarker { +fun newInstanceEuicc(clazz: Class, slotId: Int, portId: Int, addArguments: Bundle.() -> Unit = {}): T where T: Fragment, T: EuiccFragmentMarker { val instance = clazz.newInstance() instance.arguments = Bundle().apply { putInt("slotId", slotId) putInt("portId", portId) + addArguments() } return instance } diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt index 737d322..81caa11 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt @@ -2,6 +2,7 @@ package im.angry.openeuicc.ui import android.annotation.SuppressLint import android.app.Dialog +import android.content.DialogInterface import android.os.Bundle import android.text.Editable import android.text.format.Formatter @@ -30,8 +31,10 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(), EuiccFragmentMarke companion object { const val TAG = "ProfileDownloadFragment" - fun newInstance(slotId: Int, portId: Int): ProfileDownloadFragment = - newInstanceEuicc(ProfileDownloadFragment::class.java, slotId, portId) + fun newInstance(slotId: Int, portId: Int, finishWhenDone: Boolean = false): ProfileDownloadFragment = + newInstanceEuicc(ProfileDownloadFragment::class.java, slotId, portId) { + putBoolean("finishWhenDone", finishWhenDone) + } } private lateinit var toolbar: Toolbar @@ -46,6 +49,10 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(), EuiccFragmentMarke private var downloading = false + private val finishWhenDone by lazy { + requireArguments().getBoolean("finishWhenDone", false) + } + private val barcodeScannerLauncher = registerForActivityResult(ScanContract()) { result -> result.contents?.let { content -> Log.d(TAG, content) @@ -81,7 +88,9 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(), EuiccFragmentMarke toolbar.apply { setTitle(R.string.profile_download) setNavigationOnClickListener { - if (!downloading) dismiss() + if (!downloading) { + dismiss() + } } setOnMenuItemClickListener(this@ProfileDownloadFragment) } @@ -195,4 +204,18 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(), EuiccFragmentMarke // Only send notifications if the user allowed us to preferenceRepository.notificationDownloadFlow.first() } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + if (finishWhenDone) { + activity?.finish() + } + } + + override fun onCancel(dialog: DialogInterface) { + super.onCancel(dialog) + if (finishWhenDone) { + activity?.finish() + } + } } \ No newline at end of file