refactor: Create OpenEuiccUIContextMarker to facilitate easy access to application-global singletons

yes yes yes we should be using dependency injection but let's keep it
simple with AOSP building...
This commit is contained in:
Peter Cai 2024-02-04 20:26:54 -05:00
parent 7c0be54fd7
commit 632b6b4931
13 changed files with 166 additions and 39 deletions

View file

@ -0,0 +1,117 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View file

@ -8,27 +8,27 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class DirectProfileDownloadActivity : AppCompatActivity(), SlotSelectFragment.SlotSelectedListener { class DirectProfileDownloadActivity : AppCompatActivity(), SlotSelectFragment.SlotSelectedListener, OpenEuiccUIContextMarker {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
lifecycleScope.launch { lifecycleScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
openEuiccApplication.euiccChannelManager.enumerateEuiccChannels() euiccChannelManager.enumerateEuiccChannels()
} }
val knownChannels = openEuiccApplication.euiccChannelManager.knownChannels
when { when {
knownChannels.isEmpty() -> { euiccChannelManager.knownChannels.isEmpty() -> {
finish() finish()
} }
knownChannels.hasMultipleChips -> { euiccChannelManager.knownChannels.hasMultipleChips -> {
SlotSelectFragment.newInstance() SlotSelectFragment.newInstance()
.show(supportFragmentManager, SlotSelectFragment.TAG) .show(supportFragmentManager, SlotSelectFragment.TAG)
} }
else -> { else -> {
// If the device has only one eSIM "chip" (but may be mapped to multiple slots), // If the device has only one eSIM "chip" (but may be mapped to multiple slots),
// we can skip the slot selection dialog since there is only one chip to save to. // we can skip the slot selection dialog since there is only one chip to save to.
onSlotSelected(knownChannels[0].slotId, knownChannels[0].portId) onSlotSelected(euiccChannelManager.knownChannels[0].slotId,
euiccChannelManager.knownChannels[0].portId)
} }
} }
} }

View file

@ -2,11 +2,10 @@ package im.angry.openeuicc.ui
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.util.openEuiccApplication import im.angry.openeuicc.util.*
interface EuiccFragmentMarker interface EuiccFragmentMarker: OpenEuiccUIContextMarker
fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int, portId: Int, addArguments: Bundle.() -> Unit = {}): T where T: Fragment, T: EuiccFragmentMarker { fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int, portId: Int, addArguments: Bundle.() -> Unit = {}): T where T: Fragment, T: EuiccFragmentMarker {
val instance = clazz.newInstance() val instance = clazz.newInstance()
@ -23,9 +22,6 @@ val <T> T.slotId: Int where T: Fragment, T: EuiccFragmentMarker
val <T> T.portId: Int where T: Fragment, T: EuiccFragmentMarker val <T> T.portId: Int where T: Fragment, T: EuiccFragmentMarker
get() = requireArguments().getInt("portId") get() = requireArguments().getInt("portId")
val <T> T.euiccChannelManager: EuiccChannelManager where T: Fragment, T: EuiccFragmentMarker
get() = openEuiccApplication.euiccChannelManager
val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccFragmentMarker val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccFragmentMarker
get() = get() =
euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!! euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!!

View file

@ -31,7 +31,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.lang.Exception import java.lang.Exception
open class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesChangedListener { open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, EuiccFragmentMarker {
companion object { companion object {
const val TAG = "EuiccManagementFragment" const val TAG = "EuiccManagementFragment"

View file

@ -20,7 +20,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
open class MainActivity : AppCompatActivity() { open class MainActivity : AppCompatActivity(), OpenEuiccUIContextMarker {
companion object { companion object {
const val TAG = "MainActivity" const val TAG = "MainActivity"
} }
@ -43,9 +43,9 @@ open class MainActivity : AppCompatActivity() {
noEuiccPlaceholder = findViewById(R.id.no_euicc_placeholder) noEuiccPlaceholder = findViewById(R.id.no_euicc_placeholder)
tm = openEuiccApplication.telephonyManager tm = telephonyManager
manager = openEuiccApplication.euiccChannelManager manager = euiccChannelManager
spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item) spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)

View file

@ -19,16 +19,15 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import im.angry.openeuicc.OpenEuiccApplication
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.util.displayName import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.LocalProfileNotification import net.typeblog.lpac_jni.LocalProfileNotification
class NotificationsActivity: AppCompatActivity() { class NotificationsActivity: AppCompatActivity(), OpenEuiccUIContextMarker {
private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var swipeRefresh: SwipeRefreshLayout
private lateinit var notificationList: RecyclerView private lateinit var notificationList: RecyclerView
private val notificationAdapter = NotificationAdapter() private val notificationAdapter = NotificationAdapter()
@ -41,7 +40,7 @@ class NotificationsActivity: AppCompatActivity() {
setSupportActionBar(findViewById(R.id.toolbar)) setSupportActionBar(findViewById(R.id.toolbar))
supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportActionBar!!.setDisplayHomeAsUpEnabled(true)
euiccChannel = (application as OpenEuiccApplication).euiccChannelManager euiccChannel = euiccChannelManager
.findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!! .findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!!
swipeRefresh = findViewById(R.id.swipe_refresh) swipeRefresh = findViewById(R.id.swipe_refresh)

View file

@ -7,12 +7,9 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.util.preferenceRepository import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.LocalProfileNotification
import java.lang.Exception import java.lang.Exception
class ProfileDeleteFragment : DialogFragment(), EuiccFragmentMarker { class ProfileDeleteFragment : DialogFragment(), EuiccFragmentMarker {

View file

@ -17,9 +17,7 @@ import com.google.android.material.textfield.TextInputLayout
import com.journeyapps.barcodescanner.ScanContract import com.journeyapps.barcodescanner.ScanContract
import com.journeyapps.barcodescanner.ScanOptions import com.journeyapps.barcodescanner.ScanOptions
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.util.openEuiccApplication import im.angry.openeuicc.util.*
import im.angry.openeuicc.util.preferenceRepository
import im.angry.openeuicc.util.setWidthPercent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -27,7 +25,8 @@ import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.ProfileDownloadCallback import net.typeblog.lpac_jni.ProfileDownloadCallback
import kotlin.Exception import kotlin.Exception
class ProfileDownloadFragment : BaseMaterialDialogFragment(), EuiccFragmentMarker, Toolbar.OnMenuItemClickListener { class ProfileDownloadFragment : BaseMaterialDialogFragment(),
Toolbar.OnMenuItemClickListener, EuiccFragmentMarker {
companion object { companion object {
const val TAG = "ProfileDownloadFragment" const val TAG = "ProfileDownloadFragment"

View file

@ -13,7 +13,7 @@ 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
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.util.setWidthPercent import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext

View file

@ -10,10 +10,9 @@ import android.widget.Spinner
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.util.openEuiccApplication import im.angry.openeuicc.util.*
import im.angry.openeuicc.util.setWidthPercent
class SlotSelectFragment : BaseMaterialDialogFragment() { class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccUIContextMarker {
companion object { companion object {
const val TAG = "SlotSelectFragment" const val TAG = "SlotSelectFragment"
@ -30,7 +29,7 @@ class SlotSelectFragment : BaseMaterialDialogFragment() {
private lateinit var toolbar: Toolbar private lateinit var toolbar: Toolbar
private lateinit var spinner: Spinner private lateinit var spinner: Spinner
private val channels: List<EuiccChannel> by lazy { private val channels: List<EuiccChannel> by lazy {
openEuiccApplication.euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId } euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }
} }
override fun onCreateView( override fun onCreateView(

View file

@ -1,18 +1,32 @@
package im.angry.openeuicc.util package im.angry.openeuicc.util
import android.app.Activity import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Rect import android.graphics.Rect
import android.telephony.TelephonyManager
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.angry.openeuicc.OpenEuiccApplication import im.angry.openeuicc.OpenEuiccApplication
import im.angry.openeuicc.core.EuiccChannelManager
val Activity.openEuiccApplication: OpenEuiccApplication interface OpenEuiccUIContextMarker
get() = application as OpenEuiccApplication
val Fragment.openEuiccApplication: OpenEuiccApplication val OpenEuiccUIContextMarker.context: Context
get() = requireActivity().openEuiccApplication get() = when (this) {
is Context -> this
is Fragment -> requireContext()
else -> throw RuntimeException("OpenEuiccUIContextMarker shall only be used on Fragments or UI types that derive from Context")
}
val OpenEuiccUIContextMarker.openEuiccApplication: OpenEuiccApplication
get() = context.applicationContext as OpenEuiccApplication
val OpenEuiccUIContextMarker.euiccChannelManager: EuiccChannelManager
get() = openEuiccApplication.euiccChannelManager
val OpenEuiccUIContextMarker.telephonyManager: TelephonyManager
get() = openEuiccApplication.telephonyManager
// Source: <https://stackoverflow.com/questions/12478520/how-to-set-dialogfragments-width-and-height> // Source: <https://stackoverflow.com/questions/12478520/how-to-set-dialogfragments-width-and-height>
/** /**

View file

@ -26,7 +26,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class SlotMappingFragment: BaseMaterialDialogFragment(), OnMenuItemClickListener { class SlotMappingFragment: BaseMaterialDialogFragment(),
OnMenuItemClickListener, OpenEuiccUIContextMarker {
companion object { companion object {
const val TAG = "SlotMappingFragment" const val TAG = "SlotMappingFragment"
} }