forked from PeterCxy/OpenEUICC
Compare commits
5 commits
a4c474a405
...
2270e4aa53
Author | SHA1 | Date | |
---|---|---|---|
2270e4aa53 | |||
999462c294 | |||
2061e6fea3 | |||
6977a32e80 | |||
3a0d805eb2 |
32 changed files with 200 additions and 106 deletions
|
@ -24,7 +24,7 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha
|
||||||
|
|
||||||
Log.i(DefaultEuiccChannelManager.TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}")
|
Log.i(DefaultEuiccChannelManager.TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}")
|
||||||
try {
|
try {
|
||||||
return OmapiChannel(seService!!, port)
|
return EuiccChannel(port, OmapiApduInterface(seService!!, port))
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
// Failed
|
// Failed
|
||||||
Log.w(
|
Log.w(
|
||||||
|
|
|
@ -1,16 +1,21 @@
|
||||||
package im.angry.openeuicc.core
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
import net.typeblog.lpac_jni.LocalProfileAssistant
|
||||||
|
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
|
||||||
|
import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
|
||||||
|
|
||||||
abstract class EuiccChannel(
|
class EuiccChannel(
|
||||||
val port: UiccPortInfoCompat
|
val port: UiccPortInfoCompat,
|
||||||
|
apduInterface: ApduInterface,
|
||||||
) {
|
) {
|
||||||
val slotId = port.card.physicalSlotIndex // PHYSICAL slot
|
val slotId = port.card.physicalSlotIndex // PHYSICAL slot
|
||||||
val logicalSlotId = port.logicalSlotIndex
|
val logicalSlotId = port.logicalSlotIndex
|
||||||
val portId = port.portIndex
|
val portId = port.portIndex
|
||||||
|
|
||||||
abstract val lpa: LocalProfileAssistant
|
val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(apduInterface, HttpInterfaceImpl())
|
||||||
|
|
||||||
val valid: Boolean
|
val valid: Boolean
|
||||||
get() = lpa.valid
|
get() = lpa.valid
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,6 @@ import android.se.omapi.SEService
|
||||||
import android.se.omapi.Session
|
import android.se.omapi.Session
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import net.typeblog.lpac_jni.ApduInterface
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
|
||||||
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
|
|
||||||
import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
|
|
||||||
|
|
||||||
class OmapiApduInterface(
|
class OmapiApduInterface(
|
||||||
private val service: SEService,
|
private val service: SEService,
|
||||||
|
@ -47,13 +44,4 @@ class OmapiApduInterface(
|
||||||
return lastChannel.transmit(tx)
|
return lastChannel.transmit(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class OmapiChannel(
|
|
||||||
service: SEService,
|
|
||||||
port: UiccPortInfoCompat,
|
|
||||||
) : EuiccChannel(port) {
|
|
||||||
override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
|
|
||||||
OmapiApduInterface(service, port),
|
|
||||||
HttpInterfaceImpl())
|
|
||||||
}
|
|
|
@ -1,9 +1,13 @@
|
||||||
package im.angry.openeuicc.di
|
package im.angry.openeuicc.di
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.ui.EuiccManagementFragment
|
import im.angry.openeuicc.ui.EuiccManagementFragment
|
||||||
|
import im.angry.openeuicc.ui.NoEuiccPlaceholderFragment
|
||||||
|
|
||||||
open class DefaultUiComponentFactory : UiComponentFactory {
|
open class DefaultUiComponentFactory : UiComponentFactory {
|
||||||
override fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment =
|
override fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment =
|
||||||
EuiccManagementFragment.newInstance(channel.slotId, channel.portId)
|
EuiccManagementFragment.newInstance(channel.slotId, channel.portId)
|
||||||
|
|
||||||
|
override fun createNoEuiccPlaceholderFragment(): Fragment = NoEuiccPlaceholderFragment()
|
||||||
}
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
package im.angry.openeuicc.di
|
package im.angry.openeuicc.di
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.ui.EuiccManagementFragment
|
import im.angry.openeuicc.ui.EuiccManagementFragment
|
||||||
|
|
||||||
interface UiComponentFactory {
|
interface UiComponentFactory {
|
||||||
fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment
|
fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment
|
||||||
|
fun createNoEuiccPlaceholderFragment(): Fragment
|
||||||
}
|
}
|
|
@ -58,9 +58,9 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
|
||||||
): View {
|
): View {
|
||||||
val view = inflater.inflate(R.layout.fragment_euicc, container, false)
|
val view = inflater.inflate(R.layout.fragment_euicc, container, false)
|
||||||
|
|
||||||
swipeRefresh = view.findViewById(R.id.swipe_refresh)
|
swipeRefresh = view.requireViewById(R.id.swipe_refresh)
|
||||||
fab = view.findViewById(R.id.fab)
|
fab = view.requireViewById(R.id.fab)
|
||||||
profileList = view.findViewById(R.id.profile_list)
|
profileList = view.requireViewById(R.id.profile_list)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
@ -191,11 +191,11 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ProfileViewHolder(private val root: View) : ViewHolder(root) {
|
inner class ProfileViewHolder(private val root: View) : ViewHolder(root) {
|
||||||
private val iccid: TextView = root.findViewById(R.id.iccid)
|
private val iccid: TextView = root.requireViewById(R.id.iccid)
|
||||||
private val name: TextView = root.findViewById(R.id.name)
|
private val name: TextView = root.requireViewById(R.id.name)
|
||||||
private val state: TextView = root.findViewById(R.id.state)
|
private val state: TextView = root.requireViewById(R.id.state)
|
||||||
private val provider: TextView = root.findViewById(R.id.provider)
|
private val provider: TextView = root.requireViewById(R.id.provider)
|
||||||
private val profileMenu: ImageButton = root.findViewById(R.id.profile_menu)
|
private val profileMenu: ImageButton = root.requireViewById(R.id.profile_menu)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
iccid.setOnClickListener {
|
iccid.setOnClickListener {
|
||||||
|
|
|
@ -39,12 +39,12 @@ class LogsActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_logs)
|
setContentView(R.layout.activity_logs)
|
||||||
setSupportActionBar(findViewById(R.id.toolbar))
|
setSupportActionBar(requireViewById(R.id.toolbar))
|
||||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
swipeRefresh = findViewById(R.id.swipe_refresh)
|
swipeRefresh = requireViewById(R.id.swipe_refresh)
|
||||||
scrollView = findViewById(R.id.scroll_view)
|
scrollView = requireViewById(R.id.scroll_view)
|
||||||
logText = findViewById(R.id.log_text)
|
logText = requireViewById(R.id.log_text)
|
||||||
|
|
||||||
swipeRefresh.setOnRefreshListener {
|
swipeRefresh.setOnRefreshListener {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
|
|
@ -25,20 +25,22 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var spinnerAdapter: ArrayAdapter<String>
|
private lateinit var spinnerAdapter: ArrayAdapter<String>
|
||||||
|
private lateinit var spinnerItem: MenuItem
|
||||||
private lateinit var spinner: Spinner
|
private lateinit var spinner: Spinner
|
||||||
|
|
||||||
private val fragments = arrayListOf<EuiccManagementFragment>()
|
private val fragments = arrayListOf<EuiccManagementFragment>()
|
||||||
|
|
||||||
private lateinit var noEuiccPlaceholder: View
|
|
||||||
|
|
||||||
protected lateinit var tm: TelephonyManager
|
protected lateinit var tm: TelephonyManager
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
setSupportActionBar(findViewById(R.id.toolbar))
|
setSupportActionBar(requireViewById(R.id.toolbar))
|
||||||
|
|
||||||
noEuiccPlaceholder = findViewById(R.id.no_euicc_placeholder)
|
supportFragmentManager.beginTransaction().replace(
|
||||||
|
R.id.fragment_root,
|
||||||
|
appContainer.uiComponentFactory.createNoEuiccPlaceholderFragment()
|
||||||
|
).commit()
|
||||||
|
|
||||||
tm = telephonyManager
|
tm = telephonyManager
|
||||||
|
|
||||||
|
@ -53,7 +55,11 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
menuInflater.inflate(R.menu.activity_main, menu)
|
menuInflater.inflate(R.menu.activity_main, menu)
|
||||||
|
|
||||||
if (!this::spinner.isInitialized) {
|
if (!this::spinner.isInitialized) {
|
||||||
spinner = menu.findItem(R.id.spinner).actionView as Spinner
|
spinnerItem = menu.findItem(R.id.spinner)
|
||||||
|
spinner = spinnerItem.actionView as Spinner
|
||||||
|
if (spinnerAdapter.isEmpty) {
|
||||||
|
spinnerItem.isVisible = false
|
||||||
|
}
|
||||||
spinner.adapter = spinnerAdapter
|
spinner.adapter = spinnerAdapter
|
||||||
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(
|
override fun onItemSelected(
|
||||||
|
@ -108,7 +114,9 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fragments.isNotEmpty()) {
|
if (fragments.isNotEmpty()) {
|
||||||
noEuiccPlaceholder.visibility = View.GONE
|
if (this@MainActivity::spinner.isInitialized) {
|
||||||
|
spinnerItem.isVisible = true
|
||||||
|
}
|
||||||
supportFragmentManager.beginTransaction().replace(R.id.fragment_root, fragments.first()).commit()
|
supportFragmentManager.beginTransaction().replace(R.id.fragment_root, fragments.first()).commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package im.angry.openeuicc.ui
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import im.angry.openeuicc.common.R
|
||||||
|
|
||||||
|
class NoEuiccPlaceholderFragment : Fragment() {
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_no_euicc_placeholder, container, false)
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,14 +37,14 @@ class NotificationsActivity: AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_notifications)
|
setContentView(R.layout.activity_notifications)
|
||||||
setSupportActionBar(findViewById(R.id.toolbar))
|
setSupportActionBar(requireViewById(R.id.toolbar))
|
||||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
euiccChannel = euiccChannelManager
|
euiccChannel = euiccChannelManager
|
||||||
.findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!!
|
.findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!!
|
||||||
|
|
||||||
swipeRefresh = findViewById(R.id.swipe_refresh)
|
swipeRefresh = requireViewById(R.id.swipe_refresh)
|
||||||
notificationList = findViewById(R.id.recycler_view)
|
notificationList = requireViewById(R.id.recycler_view)
|
||||||
|
|
||||||
notificationList.layoutManager =
|
notificationList.layoutManager =
|
||||||
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||||
|
@ -118,8 +118,8 @@ class NotificationsActivity: AppCompatActivity(), OpenEuiccContextMarker {
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
inner class NotificationViewHolder(private val root: View):
|
inner class NotificationViewHolder(private val root: View):
|
||||||
RecyclerView.ViewHolder(root), View.OnCreateContextMenuListener, OnMenuItemClickListener {
|
RecyclerView.ViewHolder(root), View.OnCreateContextMenuListener, OnMenuItemClickListener {
|
||||||
private val address: TextView = root.findViewById(R.id.notification_address)
|
private val address: TextView = root.requireViewById(R.id.notification_address)
|
||||||
private val profileName: TextView = root.findViewById(R.id.notification_profile_name)
|
private val profileName: TextView = root.requireViewById(R.id.notification_profile_name)
|
||||||
|
|
||||||
private lateinit var notification: LocalProfileNotificationWrapper
|
private lateinit var notification: LocalProfileNotificationWrapper
|
||||||
|
|
||||||
|
|
|
@ -69,13 +69,13 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(),
|
||||||
): View {
|
): View {
|
||||||
val view = inflater.inflate(R.layout.fragment_profile_download, container, false)
|
val view = inflater.inflate(R.layout.fragment_profile_download, container, false)
|
||||||
|
|
||||||
toolbar = view.findViewById(R.id.toolbar)
|
toolbar = view.requireViewById(R.id.toolbar)
|
||||||
profileDownloadServer = view.findViewById(R.id.profile_download_server)
|
profileDownloadServer = view.requireViewById(R.id.profile_download_server)
|
||||||
profileDownloadCode = view.findViewById(R.id.profile_download_code)
|
profileDownloadCode = view.requireViewById(R.id.profile_download_code)
|
||||||
profileDownloadConfirmationCode = view.findViewById(R.id.profile_download_confirmation_code)
|
profileDownloadConfirmationCode = view.requireViewById(R.id.profile_download_confirmation_code)
|
||||||
profileDownloadIMEI = view.findViewById(R.id.profile_download_imei)
|
profileDownloadIMEI = view.requireViewById(R.id.profile_download_imei)
|
||||||
profileDownloadFreeSpace = view.findViewById(R.id.profile_download_free_space)
|
profileDownloadFreeSpace = view.requireViewById(R.id.profile_download_free_space)
|
||||||
progress = view.findViewById(R.id.progress)
|
progress = view.requireViewById(R.id.progress)
|
||||||
|
|
||||||
toolbar.inflateMenu(R.menu.fragment_profile_download)
|
toolbar.inflateMenu(R.menu.fragment_profile_download)
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,9 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
|
||||||
): View {
|
): View {
|
||||||
val view = inflater.inflate(R.layout.fragment_profile_rename, container, false)
|
val view = inflater.inflate(R.layout.fragment_profile_rename, container, false)
|
||||||
|
|
||||||
toolbar = view.findViewById(R.id.toolbar)
|
toolbar = view.requireViewById(R.id.toolbar)
|
||||||
profileRenameNewName = view.findViewById(R.id.profile_rename_new_name)
|
profileRenameNewName = view.requireViewById(R.id.profile_rename_new_name)
|
||||||
progress = view.findViewById(R.id.progress)
|
progress = view.requireViewById(R.id.progress)
|
||||||
|
|
||||||
toolbar.inflateMenu(R.menu.fragment_profile_rename)
|
toolbar.inflateMenu(R.menu.fragment_profile_rename)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ class SettingsActivity: AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_settings)
|
setContentView(R.layout.activity_settings)
|
||||||
setSupportActionBar(findViewById(R.id.toolbar))
|
setSupportActionBar(requireViewById(R.id.toolbar))
|
||||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||||
supportFragmentManager.beginTransaction()
|
supportFragmentManager.beginTransaction()
|
||||||
.replace(R.id.settings_container, SettingsFragment())
|
.replace(R.id.settings_container, SettingsFragment())
|
||||||
|
|
|
@ -39,13 +39,13 @@ class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccContextMarker
|
||||||
): View? {
|
): View? {
|
||||||
val view = inflater.inflate(R.layout.fragment_slot_select, container, false)
|
val view = inflater.inflate(R.layout.fragment_slot_select, container, false)
|
||||||
|
|
||||||
toolbar = view.findViewById(R.id.toolbar)
|
toolbar = view.requireViewById(R.id.toolbar)
|
||||||
toolbar.setTitle(R.string.slot_select)
|
toolbar.setTitle(R.string.slot_select)
|
||||||
toolbar.inflateMenu(R.menu.fragment_slot_select)
|
toolbar.inflateMenu(R.menu.fragment_slot_select)
|
||||||
|
|
||||||
val adapter = ArrayAdapter<String>(inflater.context, R.layout.spinner_item)
|
val adapter = ArrayAdapter<String>(inflater.context, R.layout.spinner_item)
|
||||||
|
|
||||||
spinner = view.findViewById(R.id.spinner)
|
spinner = view.requireViewById(R.id.spinner)
|
||||||
spinner.adapter = adapter
|
spinner.adapter = adapter
|
||||||
|
|
||||||
channels.forEach { channel ->
|
channels.forEach { channel ->
|
||||||
|
|
|
@ -18,4 +18,4 @@ class LongSummaryPreferenceCategory: PreferenceCategory {
|
||||||
summaryText.isSingleLine = false
|
summaryText.isSingleLine = false
|
||||||
summaryText.maxLines = 10
|
summaryText.maxLines = 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ val Context.selfAppVersion: String
|
||||||
get() =
|
get() =
|
||||||
try {
|
try {
|
||||||
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
||||||
pInfo.versionName
|
pInfo.versionName!!
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
@ -91,4 +91,4 @@ suspend fun connectSEService(context: Context): SEService = suspendCoroutine { c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,15 +21,6 @@
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/toolbar">
|
app:layout_constraintTop_toBottomOf="@id/toolbar"/>
|
||||||
<TextView
|
|
||||||
android:id="@+id/no_euicc_placeholder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginStart="40dp"
|
|
||||||
android:layout_marginEnd="40dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:text="@string/no_euicc" />
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_euicc_placeholder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="40dp"
|
||||||
|
android:layout_marginEnd="40dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/no_euicc" />
|
||||||
|
</FrameLayout>
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="no_euicc">No eUICC card on this device is accessible by this app.\nInsert a supported eUICC card, or try out the privileged OpenEUICC app instead.</string>
|
<string name="no_euicc">No removable eUICC card accessible by this app is detected on this device.</string>
|
||||||
<string name="unknown">Unknown</string>
|
<string name="unknown">Unknown</string>
|
||||||
<string name="help">Help</string>
|
<string name="help">Help</string>
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
package im.angry.openeuicc
|
package im.angry.openeuicc
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import im.angry.openeuicc.di.UnprivilegedAppContainer
|
||||||
import im.angry.openeuicc.ui.LogsActivity
|
import im.angry.openeuicc.ui.LogsActivity
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class UnprivilegedOpenEuiccApplication : OpenEuiccApplication() {
|
class UnprivilegedOpenEuiccApplication : OpenEuiccApplication() {
|
||||||
|
override val appContainer by lazy {
|
||||||
|
UnprivilegedAppContainer(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package im.angry.openeuicc.di
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
class UnprivilegedAppContainer(context: Context) : DefaultAppContainer(context) {
|
||||||
|
override val uiComponentFactory by lazy {
|
||||||
|
UnprivilegedUiComponentFactory()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package im.angry.openeuicc.di
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import im.angry.openeuicc.ui.UnprivilegedNoEuiccPlaceholderFragment
|
||||||
|
|
||||||
|
class UnprivilegedUiComponentFactory : DefaultUiComponentFactory() {
|
||||||
|
override fun createNoEuiccPlaceholderFragment(): Fragment =
|
||||||
|
UnprivilegedNoEuiccPlaceholderFragment()
|
||||||
|
}
|
|
@ -24,10 +24,10 @@ class CompatibilityCheckActivity: AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_compatibility_check)
|
setContentView(R.layout.activity_compatibility_check)
|
||||||
setSupportActionBar(findViewById(R.id.toolbar))
|
setSupportActionBar(requireViewById(R.id.toolbar))
|
||||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
compatibilityCheckList = findViewById(R.id.recycler_view)
|
compatibilityCheckList = requireViewById(R.id.recycler_view)
|
||||||
compatibilityCheckList.layoutManager =
|
compatibilityCheckList.layoutManager =
|
||||||
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||||
compatibilityCheckList.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
|
compatibilityCheckList.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
|
||||||
|
@ -51,9 +51,9 @@ class CompatibilityCheckActivity: AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(private val root: View): RecyclerView.ViewHolder(root) {
|
inner class ViewHolder(private val root: View): RecyclerView.ViewHolder(root) {
|
||||||
private val titleView: TextView = root.findViewById(R.id.compatibility_check_title)
|
private val titleView: TextView = root.requireViewById(R.id.compatibility_check_title)
|
||||||
private val descView: TextView = root.findViewById(R.id.compatibility_check_desc)
|
private val descView: TextView = root.requireViewById(R.id.compatibility_check_desc)
|
||||||
private val statusContainer: ViewGroup = root.findViewById(R.id.compatibility_check_status_container)
|
private val statusContainer: ViewGroup = root.requireViewById(R.id.compatibility_check_status_container)
|
||||||
|
|
||||||
fun bindItem(item: CompatibilityCheck) {
|
fun bindItem(item: CompatibilityCheck) {
|
||||||
titleView.text = item.title
|
titleView.text = item.title
|
||||||
|
@ -65,16 +65,16 @@ class CompatibilityCheckActivity: AppCompatActivity() {
|
||||||
|
|
||||||
when (item.state) {
|
when (item.state) {
|
||||||
CompatibilityCheck.State.SUCCESS -> {
|
CompatibilityCheck.State.SUCCESS -> {
|
||||||
root.findViewById<View>(R.id.compatibility_check_checkmark).visibility = View.VISIBLE
|
root.requireViewById<View>(R.id.compatibility_check_checkmark).visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
CompatibilityCheck.State.FAILURE -> {
|
CompatibilityCheck.State.FAILURE -> {
|
||||||
root.findViewById<View>(R.id.compatibility_check_error).visibility = View.VISIBLE
|
root.requireViewById<View>(R.id.compatibility_check_error).visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
CompatibilityCheck.State.FAILURE_UNKNOWN -> {
|
CompatibilityCheck.State.FAILURE_UNKNOWN -> {
|
||||||
root.findViewById<View>(R.id.compatibility_check_unknown).visibility = View.VISIBLE
|
root.requireViewById<View>(R.id.compatibility_check_unknown).visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
root.findViewById<View>(R.id.compatibility_check_progress_bar).visibility = View.VISIBLE
|
root.requireViewById<View>(R.id.compatibility_check_progress_bar).visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package im.angry.openeuicc.ui
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import im.angry.easyeuicc.R
|
||||||
|
|
||||||
|
class UnprivilegedNoEuiccPlaceholderFragment : Fragment() {
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
val view = inflater.inflate(
|
||||||
|
R.layout.fragment_no_euicc_placeholder_unprivileged,
|
||||||
|
container,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
view.findViewById<View>(R.id.compatibility_check).setOnClickListener {
|
||||||
|
startActivity(Intent(requireContext(), CompatibilityCheckActivity::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
return view
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/no_euicc_placeholder"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="40dp"
|
||||||
|
android:layout_marginEnd="40dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/no_euicc"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/compatibility_check"
|
||||||
|
android:text="@string/compatibility_check"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/no_euicc_placeholder"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -26,9 +26,7 @@ class PrivilegedEuiccChannelFactory(context: Context) : DefaultEuiccChannelFacto
|
||||||
"Trying TelephonyManager for slot ${port.card.physicalSlotIndex} port ${port.portIndex}"
|
"Trying TelephonyManager for slot ${port.card.physicalSlotIndex} port ${port.portIndex}"
|
||||||
)
|
)
|
||||||
try {
|
try {
|
||||||
return TelephonyManagerChannel(
|
return EuiccChannel(port, TelephonyManagerApduInterface(port, tm))
|
||||||
port, tm
|
|
||||||
)
|
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
// Failed
|
// Failed
|
||||||
Log.w(
|
Log.w(
|
||||||
|
|
|
@ -3,10 +3,7 @@ package im.angry.openeuicc.core
|
||||||
import android.telephony.IccOpenLogicalChannelResponse
|
import android.telephony.IccOpenLogicalChannelResponse
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
|
||||||
import net.typeblog.lpac_jni.ApduInterface
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
|
|
||||||
import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
|
|
||||||
|
|
||||||
class TelephonyManagerApduInterface(
|
class TelephonyManagerApduInterface(
|
||||||
private val port: UiccPortInfoCompat,
|
private val port: UiccPortInfoCompat,
|
||||||
|
@ -54,14 +51,4 @@ class TelephonyManagerApduInterface(
|
||||||
cla, instruction, p1, p2, p3, p4)?.decodeHex() ?: byteArrayOf()
|
cla, instruction, p1, p2, p3, p4)?.decodeHex() ?: byteArrayOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class TelephonyManagerChannel(
|
|
||||||
port: UiccPortInfoCompat,
|
|
||||||
private val tm: TelephonyManager
|
|
||||||
) : EuiccChannel(port) {
|
|
||||||
override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
|
|
||||||
TelephonyManagerApduInterface(port, tm),
|
|
||||||
HttpInterfaceImpl()
|
|
||||||
)
|
|
||||||
}
|
}
|
|
@ -104,9 +104,9 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
return GetDefaultDownloadableSubscriptionListResult(RESULT_OK, arrayOf())
|
return GetDefaultDownloadableSubscriptionListResult(RESULT_OK, arrayOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult? {
|
override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult {
|
||||||
Log.i(TAG, "onGetEuiccProfileInfoList slotId=$slotId")
|
Log.i(TAG, "onGetEuiccProfileInfoList slotId=$slotId")
|
||||||
val channel = findChannel(slotId) ?: return null
|
val channel = findChannel(slotId)!!
|
||||||
val profiles = channel.lpa.profiles.operational.map {
|
val profiles = channel.lpa.profiles.operational.map {
|
||||||
EuiccProfileInfo.Builder(it.iccid).apply {
|
EuiccProfileInfo.Builder(it.iccid).apply {
|
||||||
setProfileName(it.name)
|
setProfileName(it.name)
|
||||||
|
@ -256,4 +256,4 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
// No-op -- we do not care
|
// No-op -- we do not care
|
||||||
return RESULT_FIRST_USER
|
return RESULT_FIRST_USER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,10 @@ class LuiActivity : AppCompatActivity() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
setContentView(R.layout.activity_lui)
|
setContentView(R.layout.activity_lui)
|
||||||
|
|
||||||
findViewById<View>(R.id.lui_skip).setOnClickListener { finish() }
|
requireViewById<View>(R.id.lui_skip).setOnClickListener { finish() }
|
||||||
// TODO: Deactivate LuiActivity if there is no eSIM found.
|
// TODO: Deactivate LuiActivity if there is no eSIM found.
|
||||||
// TODO: Support pre-filled download info (from carrier apps); UX
|
// TODO: Support pre-filled download info (from carrier apps); UX
|
||||||
findViewById<View>(R.id.lui_download).setOnClickListener {
|
requireViewById<View>(R.id.lui_download).setOnClickListener {
|
||||||
startActivity(Intent(this, DirectProfileDownloadActivity::class.java))
|
startActivity(Intent(this, DirectProfileDownloadActivity::class.java))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ class PrivilegedEuiccManagementFragment: EuiccManagementFragment() {
|
||||||
override suspend fun onCreateFooterViews(parent: ViewGroup): List<View> =
|
override suspend fun onCreateFooterViews(parent: ViewGroup): List<View> =
|
||||||
if (channel.isMEP) {
|
if (channel.isMEP) {
|
||||||
val view = layoutInflater.inflate(R.layout.footer_mep, parent, false)
|
val view = layoutInflater.inflate(R.layout.footer_mep, parent, false)
|
||||||
view.findViewById<Button>(R.id.footer_mep_slot_mapping).setOnClickListener {
|
view.requireViewById<Button>(R.id.footer_mep_slot_mapping).setOnClickListener {
|
||||||
(requireActivity() as PrivilegedMainActivity).showSlotMappingFragment()
|
(requireActivity() as PrivilegedMainActivity).showSlotMappingFragment()
|
||||||
}
|
}
|
||||||
listOf(view)
|
listOf(view)
|
||||||
|
|
|
@ -51,12 +51,12 @@ class SlotMappingFragment: BaseMaterialDialogFragment(),
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
val view = inflater.inflate(R.layout.fragment_slot_mapping, container, false)
|
val view = inflater.inflate(R.layout.fragment_slot_mapping, container, false)
|
||||||
toolbar = view.findViewById(R.id.toolbar)
|
toolbar = view.requireViewById(R.id.toolbar)
|
||||||
toolbar.inflateMenu(R.menu.fragment_slot_mapping)
|
toolbar.inflateMenu(R.menu.fragment_slot_mapping)
|
||||||
recyclerView = view.findViewById(R.id.mapping_list)
|
recyclerView = view.requireViewById(R.id.mapping_list)
|
||||||
recyclerView.layoutManager =
|
recyclerView.layoutManager =
|
||||||
LinearLayoutManager(view.context, LinearLayoutManager.VERTICAL, false)
|
LinearLayoutManager(view.context, LinearLayoutManager.VERTICAL, false)
|
||||||
helpTextView = view.findViewById(R.id.mapping_help)
|
helpTextView = view.requireViewById(R.id.mapping_help)
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +148,8 @@ class SlotMappingFragment: BaseMaterialDialogFragment(),
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(root: View): RecyclerView.ViewHolder(root), OnItemSelectedListener {
|
inner class ViewHolder(root: View): RecyclerView.ViewHolder(root), OnItemSelectedListener {
|
||||||
private val textViewLogicalSlot: TextView = root.findViewById(R.id.slot_mapping_logical_slot)
|
private val textViewLogicalSlot: TextView = root.requireViewById(R.id.slot_mapping_logical_slot)
|
||||||
private val spinnerPorts: Spinner = root.findViewById(R.id.slot_mapping_ports)
|
private val spinnerPorts: Spinner = root.requireViewById(R.id.slot_mapping_ports)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
spinnerPorts.adapter = ArrayAdapter(requireContext(), im.angry.openeuicc.common.R.layout.spinner_item, portsDesc)
|
spinnerPorts.adapter = ArrayAdapter(requireContext(), im.angry.openeuicc.common.R.layout.spinner_item, portsDesc)
|
||||||
|
|
|
@ -95,9 +95,9 @@ fun TelephonyManager.iccOpenLogicalChannelByPortCompat(
|
||||||
slotIndex: Int, portIndex: Int, aid: String?, p2: Int
|
slotIndex: Int, portIndex: Int, aid: String?, p2: Int
|
||||||
): IccOpenLogicalChannelResponse =
|
): IccOpenLogicalChannelResponse =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
iccOpenLogicalChannelByPort(slotIndex, portIndex, aid, p2)
|
iccOpenLogicalChannelByPort(slotIndex, portIndex, aid, p2)!!
|
||||||
} else {
|
} else {
|
||||||
iccOpenLogicalChannelBySlot(slotIndex, aid, p2)
|
iccOpenLogicalChannelBySlot(slotIndex, aid, p2)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TelephonyManager.iccCloseLogicalChannelByPortCompat(
|
fun TelephonyManager.iccCloseLogicalChannelByPortCompat(
|
||||||
|
@ -121,4 +121,4 @@ fun TelephonyManager.iccTransmitApduLogicalChannelByPortCompat(
|
||||||
iccTransmitApduLogicalChannelBySlot(
|
iccTransmitApduLogicalChannelBySlot(
|
||||||
slotIndex, channel, cla, inst, p1, p2, p3, data
|
slotIndex, channel, cla, inst, p1, p2, p3, data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue