diff --git a/.forgejo/workflows/build-debug.yml b/.forgejo/workflows/build-debug.yml
index 51e802c..660dabc 100644
--- a/.forgejo/workflows/build-debug.yml
+++ b/.forgejo/workflows/build-debug.yml
@@ -1,7 +1,7 @@
on:
push:
branches:
- - 'master'
+ - '*'
jobs:
build-debug:
diff --git a/app-common/src/main/AndroidManifest.xml b/app-common/src/main/AndroidManifest.xml
index b0324dc..44c82c0 100644
--- a/app-common/src/main/AndroidManifest.xml
+++ b/app-common/src/main/AndroidManifest.xml
@@ -45,8 +45,9 @@
-
-
+
+
+
diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt
index 1d5f37f..4a34edd 100644
--- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt
+++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt
@@ -27,6 +27,13 @@ import kotlinx.coroutines.launch
import net.typeblog.lpac_jni.impl.PKID_GSMA_LIVE_CI
import net.typeblog.lpac_jni.impl.PKID_GSMA_TEST_CI
+// https://euicc-manual.osmocom.org/docs/pki/eum/accredited.json
+// ref:
+private val RE_SAS = Regex(
+ """^[A-Z]{2}-[A-Z]{2}(?:-UP)?-\d{4}T?(?:-\d+)?T?$""",
+ setOf(RegexOption.IGNORE_CASE),
+)
+
class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
companion object {
private val YES_NO = Pair(R.string.yes, R.string.no)
@@ -109,13 +116,14 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
vendorInfo.firmwareVersion?.let { add(Item(R.string.euicc_info_fw_ver, it)) }
vendorInfo.bootloaderVersion?.let { add(Item(R.string.euicc_info_bl_ver, it)) }
}
- channel.lpa.euiccInfo2.let { info ->
- add(Item(R.string.euicc_info_sgp22_version, info?.sgp22Version.toString()))
- add(Item(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion.toString()))
- add(Item(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion.toString()))
- add(Item(R.string.euicc_info_pp_version, info?.ppVersion.toString()))
- add(Item(R.string.euicc_info_sas_accreditation_number, info?.sasAccreditationNumber))
- add(Item(R.string.euicc_info_free_nvram, info?.freeNvram?.let(::formatFreeSpace)))
+ channel.lpa.euiccInfo2?.let { info ->
+ add(Item(R.string.euicc_info_sgp22_version, info.sgp22Version.toString()))
+ add(Item(R.string.euicc_info_firmware_version, info.euiccFirmwareVersion.toString()))
+ add(Item(R.string.euicc_info_globalplatform_version, info.globalPlatformVersion.toString()))
+ add(Item(R.string.euicc_info_pp_version, info.ppVersion.toString()))
+ info.sasAccreditationNumber.trim().takeIf(RE_SAS::matches)
+ ?.let { add(Item(R.string.euicc_info_sas_accreditation_number, it.uppercase())) }
+ add(Item(R.string.euicc_info_free_nvram, info.freeNvram.let(::formatFreeSpace)))
}
channel.lpa.euiccInfo2?.euiccCiPKIdListForSigning.orEmpty().let { signers ->
// SGP.28 v1.0, eSIM CI Registration Criteria (Page 5 of 9, 2019-10-24)
@@ -130,18 +138,13 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
}
add(Item(R.string.euicc_info_ci_type, getString(resId)))
}
- val atr = channel.atr?.encodeHex() ?: getString(R.string.information_unavailable)
+ val atr = channel.atr?.encodeHex() ?: getString(R.string.information_unavailable)
add(Item(R.string.euicc_info_atr, atr, copiedToastResId = R.string.toast_atr_copied))
}
+ @Suppress("SameParameterValue")
private fun formatByBoolean(b: Boolean, res: Pair): String =
- getString(
- if (b) {
- res.first
- } else {
- res.second
- }
- )
+ getString(if (b) res.first else res.second)
inner class EuiccInfoViewHolder(root: View) : ViewHolder(root) {
private val title: TextView = root.requireViewById(R.id.euicc_info_title)
diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt
index 12995ff..3c94c6c 100644
--- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt
+++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt
@@ -347,6 +347,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
private val profileClassLabel: TextView = root.requireViewById(R.id.profile_class_label)
private val profileClass: TextView = root.requireViewById(R.id.profile_class)
private val profileMenu: ImageButton = root.requireViewById(R.id.profile_menu)
+ private val profileSeqNumber: TextView = root.requireViewById(R.id.profile_sequence_number)
init {
iccid.setOnClickListener {
@@ -366,7 +367,9 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
true
}
- profileMenu.setOnClickListener { showOptionsMenu() }
+ profileMenu.setOnClickListener {
+ showOptionsMenu()
+ }
}
private lateinit var profile: LocalProfileInfo
@@ -396,6 +399,13 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
iccid.transformationMethod = PasswordTransformationMethod.getInstance()
}
+ fun setProfileSequenceNumber(index: Int) {
+ profileSeqNumber.text = root.context.getString(
+ R.string.profile_sequence_number_format,
+ index,
+ )
+ }
+
private fun showOptionsMenu() {
// Prevent users from doing multiple things at once
if (invalid || swipeRefresh.isRefreshing) return
@@ -461,6 +471,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
when (holder) {
is ProfileViewHolder -> {
holder.setProfile(profiles[position])
+ holder.setProfileSequenceNumber(position + 1)
}
is FooterViewHolder -> {
holder.attach(footerViews[position - profiles.size])
diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt
index 9e312d4..6574645 100644
--- a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt
+++ b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt
@@ -123,8 +123,8 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
// If we get an LPA string from deep-link intents, extract from there.
// Note that `onRestoreInstanceState` could override this with user input,
// but that _is_ the desired behavior.
- val uri = intent.data
- if (uri?.scheme == "lpa") {
+ val uri = intent.data ?: return
+ if (uri.scheme.contentEquals("lpa", ignoreCase = true)) {
val parsed = LPAString.parse(uri.schemeSpecificPart)
state.smdp = parsed.address
state.matchingId = parsed.matchingId
diff --git a/app-common/src/main/res/layout/euicc_profile.xml b/app-common/src/main/res/layout/euicc_profile.xml
index 58d55ab..74c1d7a 100644
--- a/app-common/src/main/res/layout/euicc_profile.xml
+++ b/app-common/src/main/res/layout/euicc_profile.xml
@@ -129,6 +129,14 @@
app:layout_constraintTop_toBottomOf="@id/profile_class"
app:layout_constraintBottom_toBottomOf="parent"/>
+
+
diff --git a/app-common/src/main/res/layout/fragment_euicc.xml b/app-common/src/main/res/layout/fragment_euicc.xml
index 4ae7523..c5fde7b 100644
--- a/app-common/src/main/res/layout/fragment_euicc.xml
+++ b/app-common/src/main/res/layout/fragment_euicc.xml
@@ -27,6 +27,7 @@
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
+ android:contentDescription="@string/profile_download"
android:src="@drawable/ic_add"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml
index 05ca15a..38bb976 100644
--- a/app-common/src/main/res/values/strings.xml
+++ b/app-common/src/main/res/values/strings.xml
@@ -19,6 +19,7 @@
Provisioning
Operational
ICCID:
+ #%d
Enable
Disable
diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c
index a61fc96..007e80d 100644
--- a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c
+++ b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c
@@ -80,7 +80,7 @@ apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, c
LPAC_JNI_EXCEPTION_RETURN;
*rx_len = (*env)->GetArrayLength(env, ret);
*rx = calloc(*rx_len, sizeof(uint8_t));
- (*env)->GetByteArrayRegion(env, ret, 0, *rx_len, *rx);
+ (*env)->GetByteArrayRegion(env, ret, 0, *rx_len, (jbyte *) *rx);
(*env)->DeleteLocalRef(env, txArr);
(*env)->DeleteLocalRef(env, ret);
return 0;
@@ -113,7 +113,7 @@ http_interface_transmit(struct euicc_ctx *ctx, const char *url, uint32_t *rcode,
jbyteArray rxArr = (jbyteArray) (*env)->GetObjectField(env, ret, field_resp_data);
*rx_len = (*env)->GetArrayLength(env, rxArr);
*rx = calloc(*rx_len, sizeof(uint8_t));
- (*env)->GetByteArrayRegion(env, rxArr, 0, *rx_len, *rx);
+ (*env)->GetByteArrayRegion(env, rxArr, 0, *rx_len, (jbyte *) *rx);
(*env)->DeleteLocalRef(env, txArr);
(*env)->DeleteLocalRef(env, rxArr);
(*env)->DeleteLocalRef(env, headersArr);