Compare commits

..

No commits in common. "5ed4c84bf24bd29c859d8344313f385a8ec04c7d" and "98e0b032d028e8ad0f0468c484f0dea8dc2380fd" have entirely different histories.

48 changed files with 117 additions and 258 deletions

4
.idea/compiler.xml generated
View file

@ -3,9 +3,11 @@
<component name="CompilerConfiguration"> <component name="CompilerConfiguration">
<bytecodeTargetLevel target="1.7"> <bytecodeTargetLevel target="1.7">
<module name="OpenEUICC.app" target="17" /> <module name="OpenEUICC.app" target="17" />
<module name="OpenEUICC.app-common" target="17" />
<module name="OpenEUICC.libs.hidden-apis-shim" target="17" /> <module name="OpenEUICC.libs.hidden-apis-shim" target="17" />
<module name="OpenEUICC.libs.lpac-jni" target="17" /> <module name="OpenEUICC.libs.lpac-jni" target="17" />
<module name="OpenEUICC.libs.lpad-sm-dp-plus-connector" target="17" />
<module name="OpenEUICC.libs.lpad-sm-dp-plus-connector.main" target="17" />
<module name="OpenEUICC.libs.lpad-sm-dp-plus-connector.test" target="17" />
</bytecodeTargetLevel> </bytecodeTargetLevel>
</component> </component>
</project> </project>

1
.idea/gradle.xml generated
View file

@ -13,7 +13,6 @@
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" /> <option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/app-common" />
<option value="$PROJECT_DIR$/libs" /> <option value="$PROJECT_DIR$/libs" />
<option value="$PROJECT_DIR$/libs/hidden-apis-shim" /> <option value="$PROJECT_DIR$/libs/hidden-apis-shim" />
<option value="$PROJECT_DIR$/libs/hidden-apis-stub" /> <option value="$PROJECT_DIR$/libs/hidden-apis-stub" />

View file

@ -1 +0,0 @@
/build

View file

@ -1,47 +0,0 @@
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'im.angry.openeuicc.common'
compileSdk 34
defaultConfig {
minSdk 30
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
compileOnly project(':libs:hidden-apis-stub')
implementation project(':libs:hidden-apis-shim')
implementation project(":libs:lpac-jni")
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}

View file

@ -1,21 +0,0 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

@ -1,24 +0,0 @@
package im.angry.openeuicc.common
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("im.angry.openeuicc.common.test", appContext.packageName)
}
}

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
tools:ignore="ProtectedPermissions">
<uses-feature
android:name="android.hardware.telephony"
android:required="true" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION" />
<application
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="screenOrientation" />
</application>
</manifest>

View file

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="no_euicc">No eUICC found on this device.\nOn some devices, you may need to enable dual SIM first in the menu of this app.</string>
<string name="dsds">Dual SIM</string>
<string name="enabled">Enabled</string>
<string name="disabled">Disabled</string>
<string name="provider">Provider:</string>
<string name="iccid">ICCID:</string>
<string name="enable">Enable</string>
<string name="disable">Disable</string>
<string name="delete">Delete</string>
<string name="rename">Rename</string>
<string name="toast_profile_enabled">eSIM profile switched. Please wait for a while when the card is restarting.</string>
<string name="toast_profile_enable_failed">Cannot switch to new eSIM profile.</string>
<string name="toast_profile_name_too_long">Nickname cannot be longer than 64 characters</string>
<string name="toast_dsds_switched">DSDS state switched. Please wait until the modem restarts.</string>
<string name="profile_download">New eSIM</string>
<string name="profile_download_server">Server (RSP / SM-DP+)</string>
<string name="profile_download_code">Activation Code</string>
<string name="profile_download_confirmation_code">Confirmation Code (Optional)</string>
<string name="profile_download_imei">IMEI (Optional)</string>
<string name="profile_download_scan">Scan QR Code</string>
<string name="profile_download_ok">Download</string>
<string name="profile_download_failed">Failed to download eSIM. Check your activation / QR code.</string>
<string name="profile_rename_new_name">New nickname</string>
<string name="profile_delete_confirm">Are you sure you want to delete the profile %s? This operation is irreversible.</string>
</resources>

View file

@ -1,17 +0,0 @@
package im.angry.openeuicc.common
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View file

@ -85,9 +85,15 @@ android {
dependencies { dependencies {
compileOnly project(':libs:hidden-apis-stub') compileOnly project(':libs:hidden-apis-stub')
implementation project(':libs:hidden-apis-shim') implementation project(':libs:hidden-apis-shim')
implementation project(':libs:lpac-jni') implementation project(":libs:lpac-jni")
implementation project(":app-common") implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

View file

@ -4,14 +4,21 @@
tools:ignore="ProtectedPermissions" tools:ignore="ProtectedPermissions"
package="im.angry.openeuicc"> package="im.angry.openeuicc">
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED_OPERATION" />
<application <application
android:name=".PrivilegedOpenEuiccApplication" android:name=".OpenEuiccApplication"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.OpenEUICC"> android:theme="@style/Theme.OpenEUICC"
android:networkSecurityConfig="@xml/network_security_config">
<activity <activity
android:name=".ui.MainActivity" android:name=".ui.MainActivity"
android:exported="true"> android:exported="true">
@ -41,6 +48,11 @@
<category android:name="android.service.euicc.category.EUICC_UI" /> <category android:name="android.service.euicc.category.EUICC_UI" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="screenOrientation" />
</application> </application>
</manifest> </manifest>

View file

@ -3,16 +3,23 @@ package im.angry.openeuicc
import android.app.Application import android.app.Application
import android.telephony.SubscriptionManager import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import im.angry.openeuicc.core.BaseEuiccChannelManager import im.angry.openeuicc.core.EuiccChannelManager
abstract class BaseOpenEuiccApplication : Application() { class OpenEuiccApplication : Application() {
val telephonyManager by lazy { val telephonyManager by lazy {
getSystemService(TelephonyManager::class.java)!! getSystemService(TelephonyManager::class.java)!!
} }
abstract val euiccChannelManager: BaseEuiccChannelManager val euiccChannelManager by lazy {
EuiccChannelManager(this)
}
val subscriptionManager by lazy { val subscriptionManager by lazy {
getSystemService(SubscriptionManager::class.java)!! getSystemService(SubscriptionManager::class.java)!!
} }
override fun onCreate() {
super.onCreate()
euiccChannelManager.closeAllStaleChannels()
}
} }

View file

@ -1,15 +0,0 @@
package im.angry.openeuicc
import im.angry.openeuicc.core.BaseEuiccChannelManager
import im.angry.openeuicc.core.PrivilegedEuiccChannelManager
class PrivilegedOpenEuiccApplication: BaseOpenEuiccApplication() {
override val euiccChannelManager: BaseEuiccChannelManager by lazy {
PrivilegedEuiccChannelManager(this)
}
override fun onCreate() {
super.onCreate()
(euiccChannelManager as PrivilegedEuiccChannelManager).closeAllStaleChannels()
}
}

View file

@ -6,19 +6,21 @@ import android.os.HandlerThread
import android.se.omapi.SEService import android.se.omapi.SEService
import android.telephony.UiccCardInfo import android.telephony.UiccCardInfo
import android.util.Log import android.util.Log
import im.angry.openeuicc.BaseOpenEuiccApplication import im.angry.openeuicc.OpenEuiccApplication
import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
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
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.lang.Exception
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
abstract class BaseEuiccChannelManager(private val context: Context) { class EuiccChannelManager(private val context: Context) {
companion object { companion object {
const val TAG = "BaseEuiccChannelManager" const val TAG = "EuiccChannelManager"
} }
private val channels = mutableListOf<EuiccChannel>() private val channels = mutableListOf<EuiccChannel>()
@ -27,11 +29,11 @@ abstract class BaseEuiccChannelManager(private val context: Context) {
private val lock = Mutex() private val lock = Mutex()
protected val tm by lazy { private val tm by lazy {
(context.applicationContext as BaseOpenEuiccApplication).telephonyManager (context.applicationContext as OpenEuiccApplication).telephonyManager
} }
private val handler = Handler(HandlerThread("BaseEuiccChannelManager").also { it.start() }.looper) private val handler = Handler(HandlerThread("EuiccChannelManager").also { it.start() }.looper)
private suspend fun connectSEService(): SEService = suspendCoroutine { cont -> private suspend fun connectSEService(): SEService = suspendCoroutine { cont ->
handler.post { handler.post {
@ -48,8 +50,6 @@ abstract class BaseEuiccChannelManager(private val context: Context) {
} }
} }
abstract fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel?
private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfo): EuiccChannel? { private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfo): EuiccChannel? {
lock.withLock { lock.withLock {
ensureSEService() ensureSEService()
@ -71,7 +71,17 @@ abstract class BaseEuiccChannelManager(private val context: Context) {
uiccInfo.isRemovable uiccInfo.isRemovable
) )
var euiccChannel: EuiccChannel? = tryOpenEuiccChannelPrivileged(uiccInfo, channelInfo) var euiccChannel: EuiccChannel? = null
if (uiccInfo.isEuicc && !uiccInfo.isRemovable) {
Log.d(TAG, "Using TelephonyManager for slot ${uiccInfo.slotIndex}")
// TODO: On Tiramisu, we should also connect all available "ports" for MEP support
try {
euiccChannel = TelephonyManagerChannel(channelInfo, tm)
} catch (e: IllegalArgumentException) {
// Failed
}
}
if (euiccChannel == null) { if (euiccChannel == null) {
try { try {
@ -125,4 +135,19 @@ abstract class BaseEuiccChannelManager(private val context: Context) {
seService?.shutdown() seService?.shutdown()
seService = null seService = null
} }
// Clean up channels left open in TelephonyManager
// due to a (potentially) forced restart
// This should be called every time the application is restarted
fun closeAllStaleChannels() {
for (card in tm.uiccCardsInfo) {
for (channel in 0 until 10) {
try {
tm.iccCloseLogicalChannelBySlot(card.slotIndex, channel)
} catch (_: Exception) {
// We do not care
}
}
}
}
} }

View file

@ -1,38 +0,0 @@
package im.angry.openeuicc.core
import android.content.Context
import android.telephony.UiccCardInfo
import android.util.Log
import im.angry.openeuicc.util.*
import java.lang.Exception
import java.lang.IllegalArgumentException
class PrivilegedEuiccChannelManager(context: Context): BaseEuiccChannelManager(context) {
override fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
if (uiccInfo.isEuicc && !uiccInfo.isRemovable) {
Log.d(TAG, "Using TelephonyManager for slot ${uiccInfo.slotIndex}")
// TODO: On Tiramisu, we should also connect all available "ports" for MEP support
try {
return TelephonyManagerChannel(channelInfo, tm)
} catch (e: IllegalArgumentException) {
// Failed
}
}
return null
}
// Clean up channels left open in TelephonyManager
// due to a (potentially) forced restart
// This should be called every time the application is restarted
fun closeAllStaleChannels() {
for (card in tm.uiccCardsInfo) {
for (channel in 0 until 10) {
try {
tm.iccCloseLogicalChannelBySlot(card.slotIndex, channel)
} catch (_: Exception) {
// We do not care
}
}
}
}
}

View file

@ -4,13 +4,13 @@ import android.service.euicc.*
import android.telephony.euicc.DownloadableSubscription import android.telephony.euicc.DownloadableSubscription
import android.telephony.euicc.EuiccInfo import android.telephony.euicc.EuiccInfo
import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileInfo
import im.angry.openeuicc.BaseOpenEuiccApplication 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.*
class OpenEuiccService : EuiccService() { class OpenEuiccService : EuiccService() {
private val openEuiccApplication private val openEuiccApplication
get() = application as BaseOpenEuiccApplication get() = application as OpenEuiccApplication
private fun findChannel(slotId: Int): EuiccChannel? = private fun findChannel(slotId: Int): EuiccChannel? =
openEuiccApplication.euiccChannelManager openEuiccApplication.euiccChannelManager

View file

@ -2,8 +2,8 @@ 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.BaseEuiccChannelManager
import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.util.openEuiccApplication import im.angry.openeuicc.util.openEuiccApplication
interface EuiccFragmentMarker interface EuiccFragmentMarker
@ -19,7 +19,7 @@ fun <T> newInstanceEuicc(clazz: Class<T>, slotId: Int): T where T: Fragment, T:
val <T> T.slotId: Int where T: Fragment, T: EuiccFragmentMarker val <T> T.slotId: Int where T: Fragment, T: EuiccFragmentMarker
get() = requireArguments().getInt("slotId") get() = requireArguments().getInt("slotId")
val <T> T.euiccChannelManager: BaseEuiccChannelManager where T: Fragment, T: EuiccFragmentMarker val <T> T.euiccChannelManager: EuiccChannelManager where T: Fragment, T: EuiccFragmentMarker
get() = openEuiccApplication.euiccChannelManager get() = openEuiccApplication.euiccChannelManager
val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccFragmentMarker val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccFragmentMarker

View file

@ -19,7 +19,7 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileInfo
import im.angry.openeuicc.common.R import im.angry.openeuicc.R
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View file

@ -12,8 +12,8 @@ import android.widget.Spinner
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import im.angry.openeuicc.common.R import im.angry.openeuicc.R
import im.angry.openeuicc.core.BaseEuiccChannelManager import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -24,7 +24,7 @@ class MainActivity : AppCompatActivity() {
const val TAG = "MainActivity" const val TAG = "MainActivity"
} }
private lateinit var manager: BaseEuiccChannelManager private lateinit var manager: EuiccChannelManager
private lateinit var spinnerAdapter: ArrayAdapter<String> private lateinit var spinnerAdapter: ArrayAdapter<String>
private lateinit var spinner: Spinner private lateinit var spinner: Spinner

View file

@ -6,7 +6,7 @@ import android.util.Log
import androidx.appcompat.app.AlertDialog 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.R
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

@ -13,7 +13,7 @@ import androidx.lifecycle.lifecycleScope
import com.google.android.material.textfield.TextInputLayout 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.R
import im.angry.openeuicc.util.setWidthPercent import im.angry.openeuicc.util.setWidthPercent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -132,8 +132,6 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O
profileDownloadServer.editText!!.isEnabled = false profileDownloadServer.editText!!.isEnabled = false
profileDownloadCode.editText!!.isEnabled = false profileDownloadCode.editText!!.isEnabled = false
profileDownloadConfirmationCode.editText!!.isEnabled = false
profileDownloadIMEI.editText!!.isEnabled = false
progress.isIndeterminate = true progress.isIndeterminate = true
progress.visibility = View.VISIBLE progress.visibility = View.VISIBLE

View file

@ -13,7 +13,7 @@ import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
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.R
import im.angry.openeuicc.util.setWidthPercent import im.angry.openeuicc.util.setWidthPercent
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch

View file

@ -6,12 +6,12 @@ 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 androidx.fragment.app.Fragment
import im.angry.openeuicc.BaseOpenEuiccApplication import im.angry.openeuicc.OpenEuiccApplication
val Activity.openEuiccApplication: BaseOpenEuiccApplication val Activity.openEuiccApplication: OpenEuiccApplication
get() = application as BaseOpenEuiccApplication get() = application as OpenEuiccApplication
val Fragment.openEuiccApplication: BaseOpenEuiccApplication val Fragment.openEuiccApplication: OpenEuiccApplication
get() = requireActivity().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>

View file

@ -1,3 +1,35 @@
<resources> <resources>
<string name="app_name">OpenEUICC</string> <string name="app_name">OpenEUICC</string>
<string name="no_euicc">No eUICC found on this device.\nOn some devices, you may need to enable dual SIM first in the menu of this app.</string>
<string name="dsds">Dual SIM</string>
<string name="enabled">Enabled</string>
<string name="disabled">Disabled</string>
<string name="provider">Provider:</string>
<string name="iccid">ICCID:</string>
<string name="enable">Enable</string>
<string name="disable">Disable</string>
<string name="delete">Delete</string>
<string name="rename">Rename</string>
<string name="toast_profile_enabled">eSIM profile switched. Please wait for a while when the card is restarting.</string>
<string name="toast_profile_enable_failed">Cannot switch to new eSIM profile.</string>
<string name="toast_profile_name_too_long">Nickname cannot be longer than 64 characters</string>
<string name="toast_dsds_switched">DSDS state switched. Please wait until the modem restarts.</string>
<string name="profile_download">New eSIM</string>
<string name="profile_download_server">Server (RSP / SM-DP+)</string>
<string name="profile_download_code">Activation Code</string>
<string name="profile_download_confirmation_code">Confirmation Code (Optional)</string>
<string name="profile_download_imei">IMEI (Optional)</string>
<string name="profile_download_scan">Scan QR Code</string>
<string name="profile_download_ok">Download</string>
<string name="profile_download_failed">Failed to download eSIM. Check your activation / QR code.</string>
<string name="profile_rename_new_name">New nickname</string>
<string name="profile_delete_confirm">Are you sure you want to delete the profile %s? This operation is irreversible.</string>
</resources> </resources>

View file

@ -11,11 +11,9 @@
<item name="colorOnSecondary">@color/white</item> <item name="colorOnSecondary">@color/white</item>
<item name="colorAccent">?attr/colorSecondary</item> <item name="colorAccent">?attr/colorSecondary</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimary</item> <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<item name="android:windowLightStatusBar">true</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<item name="alertDialogTheme">@style/AlertDialogTheme</item> <item name="alertDialogTheme">@style/AlertDialogTheme</item>
<item name="android:navigationBarColor">?attr/colorSecondary</item>
</style> </style>
<style name="Theme.OpenEUICC.Input.Cursor" parent="ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox"> <style name="Theme.OpenEUICC.Input.Cursor" parent="ThemeOverlay.MaterialComponents.TextInputEditText.OutlinedBox">

View file

@ -17,4 +17,3 @@ include ':app'
include ':libs:hidden-apis-stub' include ':libs:hidden-apis-stub'
include ':libs:hidden-apis-shim' include ':libs:hidden-apis-shim'
include ':libs:lpac-jni' include ':libs:lpac-jni'
include ':app-common'