Compare commits
No commits in common. "5517b7dcd13b97e0efb0e990a913b7e6c1c29484" and "7c0be54fd77784053d470789aa79381f47073a11" have entirely different histories.
5517b7dcd1
...
7c0be54fd7
15 changed files with 66 additions and 187 deletions
117
.idea/codeStyles/Project.xml
generated
117
.idea/codeStyles/Project.xml
generated
|
@ -1,117 +0,0 @@
|
||||||
<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>
|
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
|
@ -1,5 +0,0 @@
|
||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
|
@ -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, OpenEuiccContextMarker {
|
class DirectProfileDownloadActivity : AppCompatActivity(), SlotSelectFragment.SlotSelectedListener {
|
||||||
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) {
|
||||||
euiccChannelManager.enumerateEuiccChannels()
|
openEuiccApplication.euiccChannelManager.enumerateEuiccChannels()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val knownChannels = openEuiccApplication.euiccChannelManager.knownChannels
|
||||||
when {
|
when {
|
||||||
euiccChannelManager.knownChannels.isEmpty() -> {
|
knownChannels.isEmpty() -> {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
euiccChannelManager.knownChannels.hasMultipleChips -> {
|
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(euiccChannelManager.knownChannels[0].slotId,
|
onSlotSelected(knownChannels[0].slotId, knownChannels[0].portId)
|
||||||
euiccChannelManager.knownChannels[0].portId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
package im.angry.openeuicc.util
|
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
|
||||||
|
|
||||||
interface EuiccChannelFragmentMarker: OpenEuiccContextMarker
|
interface EuiccFragmentMarker
|
||||||
|
|
||||||
// We must use extension functions because there is no way to add bounds to the type of "self"
|
fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int, portId: Int, addArguments: Bundle.() -> Unit = {}): T where T: Fragment, T: EuiccFragmentMarker {
|
||||||
// in the definition of an interface, so the only way is to limit where the extension functions
|
|
||||||
// can be applied.
|
|
||||||
fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int, portId: Int, addArguments: Bundle.() -> Unit = {}): T where T: Fragment, T: EuiccChannelFragmentMarker {
|
|
||||||
val instance = clazz.newInstance()
|
val instance = clazz.newInstance()
|
||||||
instance.arguments = Bundle().apply {
|
instance.arguments = Bundle().apply {
|
||||||
putInt("slotId", slotId)
|
putInt("slotId", slotId)
|
||||||
|
@ -19,12 +18,15 @@ fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int, portId: Int, addArguments
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
val <T> T.slotId: Int where T: Fragment, T: EuiccChannelFragmentMarker
|
val <T> T.slotId: Int where T: Fragment, T: EuiccFragmentMarker
|
||||||
get() = requireArguments().getInt("slotId")
|
get() = requireArguments().getInt("slotId")
|
||||||
val <T> T.portId: Int where T: Fragment, T: EuiccChannelFragmentMarker
|
val <T> T.portId: Int where T: Fragment, T: EuiccFragmentMarker
|
||||||
get() = requireArguments().getInt("portId")
|
get() = requireArguments().getInt("portId")
|
||||||
|
|
||||||
val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccChannelFragmentMarker
|
val <T> T.euiccChannelManager: EuiccChannelManager where T: Fragment, T: EuiccFragmentMarker
|
||||||
|
get() = openEuiccApplication.euiccChannelManager
|
||||||
|
|
||||||
|
val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccFragmentMarker
|
||||||
get() =
|
get() =
|
||||||
euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!!
|
euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!!
|
||||||
|
|
|
@ -31,8 +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(), EuiccProfilesChangedListener,
|
open class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesChangedListener {
|
||||||
EuiccChannelFragmentMarker {
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "EuiccManagementFragment"
|
const val TAG = "EuiccManagementFragment"
|
||||||
|
|
||||||
|
|
|
@ -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(), OpenEuiccContextMarker {
|
open class MainActivity : AppCompatActivity() {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "MainActivity"
|
const val TAG = "MainActivity"
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,9 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
|
|
||||||
noEuiccPlaceholder = findViewById(R.id.no_euicc_placeholder)
|
noEuiccPlaceholder = findViewById(R.id.no_euicc_placeholder)
|
||||||
|
|
||||||
tm = telephonyManager
|
tm = openEuiccApplication.telephonyManager
|
||||||
|
|
||||||
manager = euiccChannelManager
|
manager = openEuiccApplication.euiccChannelManager
|
||||||
|
|
||||||
spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)
|
spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)
|
||||||
|
|
||||||
|
|
|
@ -19,15 +19,16 @@ 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.*
|
import im.angry.openeuicc.util.displayName
|
||||||
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(), OpenEuiccContextMarker {
|
class NotificationsActivity: AppCompatActivity() {
|
||||||
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()
|
||||||
|
@ -40,7 +41,7 @@ class NotificationsActivity: AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
setSupportActionBar(findViewById(R.id.toolbar))
|
setSupportActionBar(findViewById(R.id.toolbar))
|
||||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
euiccChannel = euiccChannelManager
|
euiccChannel = (application as OpenEuiccApplication).euiccChannelManager
|
||||||
.findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!!
|
.findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!!
|
||||||
|
|
||||||
swipeRefresh = findViewById(R.id.swipe_refresh)
|
swipeRefresh = findViewById(R.id.swipe_refresh)
|
||||||
|
|
|
@ -7,12 +7,15 @@ 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.*
|
import im.angry.openeuicc.util.preferenceRepository
|
||||||
|
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(), EuiccChannelFragmentMarker {
|
class ProfileDeleteFragment : DialogFragment(), EuiccFragmentMarker {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "ProfileDeleteFragment"
|
const val TAG = "ProfileDeleteFragment"
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,9 @@ 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.*
|
import im.angry.openeuicc.util.openEuiccApplication
|
||||||
|
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
|
||||||
|
@ -25,8 +27,7 @@ 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(),
|
class ProfileDownloadFragment : BaseMaterialDialogFragment(), EuiccFragmentMarker, Toolbar.OnMenuItemClickListener {
|
||||||
Toolbar.OnMenuItemClickListener, EuiccChannelFragmentMarker {
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "ProfileDownloadFragment"
|
const val TAG = "ProfileDownloadFragment"
|
||||||
|
|
||||||
|
|
|
@ -6,20 +6,21 @@ import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.Window
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
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
|
||||||
import im.angry.openeuicc.common.R
|
import im.angry.openeuicc.common.R
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.setWidthPercent
|
||||||
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 java.lang.Exception
|
import java.lang.Exception
|
||||||
import java.lang.RuntimeException
|
import java.lang.RuntimeException
|
||||||
|
|
||||||
class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragmentMarker {
|
class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccFragmentMarker {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "ProfileRenameFragment"
|
const val TAG = "ProfileRenameFragment"
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,10 @@ 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.*
|
import im.angry.openeuicc.util.openEuiccApplication
|
||||||
|
import im.angry.openeuicc.util.setWidthPercent
|
||||||
|
|
||||||
class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccContextMarker {
|
class SlotSelectFragment : BaseMaterialDialogFragment() {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "SlotSelectFragment"
|
const val TAG = "SlotSelectFragment"
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccContextMarker
|
||||||
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 {
|
||||||
euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }
|
openEuiccApplication.euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
package im.angry.openeuicc.util
|
package im.angry.openeuicc.util
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import im.angry.openeuicc.OpenEuiccApplication
|
||||||
|
|
||||||
|
val Activity.openEuiccApplication: OpenEuiccApplication
|
||||||
|
get() = application as OpenEuiccApplication
|
||||||
|
|
||||||
|
val Fragment.openEuiccApplication: OpenEuiccApplication
|
||||||
|
get() = requireActivity().openEuiccApplication
|
||||||
|
|
||||||
// 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>
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,11 +3,7 @@ package im.angry.openeuicc.util
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.se.omapi.SEService
|
import android.se.omapi.SEService
|
||||||
import android.telephony.TelephonyManager
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import im.angry.openeuicc.OpenEuiccApplication
|
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
@ -26,24 +22,6 @@ val Context.selfAppVersion: String
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OpenEuiccContextMarker {
|
|
||||||
val openEuiccMarkerContext: Context
|
|
||||||
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 openEuiccApplication: OpenEuiccApplication
|
|
||||||
get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication
|
|
||||||
|
|
||||||
val euiccChannelManager: EuiccChannelManager
|
|
||||||
get() = openEuiccApplication.euiccChannelManager
|
|
||||||
|
|
||||||
val telephonyManager: TelephonyManager
|
|
||||||
get() = openEuiccApplication.telephonyManager
|
|
||||||
}
|
|
||||||
|
|
||||||
val LocalProfileInfo.isEnabled: Boolean
|
val LocalProfileInfo.isEnabled: Boolean
|
||||||
get() = state == LocalProfileInfo.State.Enabled
|
get() = state == LocalProfileInfo.State.Enabled
|
||||||
|
|
||||||
|
|
|
@ -6,23 +6,30 @@ import android.telephony.euicc.DownloadableSubscription
|
||||||
import android.telephony.euicc.EuiccInfo
|
import android.telephony.euicc.EuiccInfo
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import net.typeblog.lpac_jni.LocalProfileInfo
|
import net.typeblog.lpac_jni.LocalProfileInfo
|
||||||
|
import im.angry.openeuicc.OpenEuiccApplication
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
class OpenEuiccService : EuiccService() {
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "OpenEuiccService"
|
const val TAG = "OpenEuiccService"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val openEuiccApplication
|
||||||
|
get() = application as OpenEuiccApplication
|
||||||
|
|
||||||
private fun findChannel(physicalSlotId: Int): EuiccChannel? =
|
private fun findChannel(physicalSlotId: Int): EuiccChannel? =
|
||||||
euiccChannelManager.findEuiccChannelByPhysicalSlotBlocking(physicalSlotId)
|
openEuiccApplication.euiccChannelManager
|
||||||
|
.findEuiccChannelByPhysicalSlotBlocking(physicalSlotId)
|
||||||
|
|
||||||
private fun findChannel(slotId: Int, portId: Int): EuiccChannel? =
|
private fun findChannel(slotId: Int, portId: Int): EuiccChannel? =
|
||||||
euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)
|
openEuiccApplication.euiccChannelManager
|
||||||
|
.findEuiccChannelByPortBlocking(slotId, portId)
|
||||||
|
|
||||||
private fun findAllChannels(physicalSlotId: Int): List<EuiccChannel>? =
|
private fun findAllChannels(physicalSlotId: Int): List<EuiccChannel>? =
|
||||||
euiccChannelManager.findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId)
|
openEuiccApplication.euiccChannelManager
|
||||||
|
.findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId)
|
||||||
|
|
||||||
override fun onGetEid(slotId: Int): String? =
|
override fun onGetEid(slotId: Int): String? =
|
||||||
findChannel(slotId)?.lpa?.eID
|
findChannel(slotId)?.lpa?.eID
|
||||||
|
@ -34,7 +41,7 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
lpa.profiles.any { it.iccid == iccid }
|
lpa.profiles.any { it.iccid == iccid }
|
||||||
|
|
||||||
private fun ensurePortIsMapped(slotId: Int, portId: Int) {
|
private fun ensurePortIsMapped(slotId: Int, portId: Int) {
|
||||||
val mappings = telephonyManager.simSlotMapping.toMutableList()
|
val mappings = openEuiccApplication.telephonyManager.simSlotMapping.toMutableList()
|
||||||
|
|
||||||
mappings.firstOrNull { it.physicalSlotIndex == slotId && it.portIndex == portId }?.let {
|
mappings.firstOrNull { it.physicalSlotIndex == slotId && it.portIndex == portId }?.let {
|
||||||
throw IllegalStateException("Slot $slotId port $portId has already been mapped")
|
throw IllegalStateException("Slot $slotId port $portId has already been mapped")
|
||||||
|
@ -50,14 +57,14 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
telephonyManager.simSlotMapping = mappings
|
openEuiccApplication.telephonyManager.simSlotMapping = mappings
|
||||||
return
|
return
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sometimes hardware supports one ordering but not the reverse
|
// Sometimes hardware supports one ordering but not the reverse
|
||||||
telephonyManager.simSlotMapping = mappings.reversed()
|
openEuiccApplication.telephonyManager.simSlotMapping = mappings.reversed()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> retryWithTimeout(timeoutMillis: Int, backoff: Int = 1000, f: () -> T?): T? {
|
private fun <T> retryWithTimeout(timeoutMillis: Int, backoff: Int = 1000, f: () -> T?): T? {
|
||||||
|
@ -226,7 +233,7 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return RESULT_FIRST_USER
|
return RESULT_FIRST_USER
|
||||||
} finally {
|
} finally {
|
||||||
euiccChannelManager.invalidate()
|
openEuiccApplication.euiccChannelManager.invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,7 @@ import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class SlotMappingFragment: BaseMaterialDialogFragment(),
|
class SlotMappingFragment: BaseMaterialDialogFragment(), OnMenuItemClickListener {
|
||||||
OnMenuItemClickListener, OpenEuiccContextMarker {
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TAG = "SlotMappingFragment"
|
const val TAG = "SlotMappingFragment"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue