forked from PeterCxy/OpenEUICC
Compare commits
9 commits
456754db5d
...
24076e8fb4
Author | SHA1 | Date | |
---|---|---|---|
24076e8fb4 | |||
f135a0da60 | |||
3b7bd8b31e | |||
74e946cc8f | |||
3430406603 | |||
24f04f54e4 | |||
0fbda7dd78 | |||
905d0c897e | |||
f395cee2e0 |
15 changed files with 110 additions and 78 deletions
|
@ -54,8 +54,9 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) :
|
|||
|
||||
override fun euiccMemoryReset() = lpa.euiccMemoryReset()
|
||||
|
||||
override fun setNickname(iccid: String, nickname: String): Boolean =
|
||||
override fun setNickname(iccid: String, nickname: String) {
|
||||
lpa.setNickname(iccid, nickname)
|
||||
}
|
||||
|
||||
override fun close() = lpa.close()
|
||||
|
||||
|
|
|
@ -414,16 +414,12 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
|||
getString(R.string.task_profile_rename_failure),
|
||||
R.drawable.ic_task_rename
|
||||
) {
|
||||
val res = euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
|
||||
euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
|
||||
channel.lpa.setNickname(
|
||||
iccid,
|
||||
name
|
||||
)
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
throw RuntimeException("Profile not renamed")
|
||||
}
|
||||
}
|
||||
|
||||
fun launchProfileDeleteTask(
|
||||
|
|
|
@ -14,6 +14,7 @@ import im.angry.openeuicc.common.R
|
|||
import im.angry.openeuicc.service.EuiccChannelManagerService.Companion.waitDone
|
||||
import im.angry.openeuicc.util.*
|
||||
import kotlinx.coroutines.launch
|
||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
||||
|
||||
class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragmentMarker {
|
||||
companion object {
|
||||
|
@ -81,13 +82,18 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
|
|||
}
|
||||
}
|
||||
|
||||
private fun rename() {
|
||||
val name = profileRenameNewName.editText!!.text.toString().trim()
|
||||
if (name.length >= 64) {
|
||||
Toast.makeText(context, R.string.toast_profile_name_too_long, Toast.LENGTH_LONG).show()
|
||||
return
|
||||
private fun showErrorAndCancel(errorStrRes: Int) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
errorStrRes,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
|
||||
renaming = false
|
||||
progress.visibility = View.GONE
|
||||
}
|
||||
|
||||
private fun rename() {
|
||||
renaming = true
|
||||
progress.isIndeterminate = true
|
||||
progress.visibility = View.VISIBLE
|
||||
|
@ -95,13 +101,27 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
|
|||
lifecycleScope.launch {
|
||||
ensureEuiccChannelManager()
|
||||
euiccChannelManagerService.waitForForegroundTask()
|
||||
euiccChannelManagerService.launchProfileRenameTask(
|
||||
val res = euiccChannelManagerService.launchProfileRenameTask(
|
||||
slotId,
|
||||
portId,
|
||||
requireArguments().getString("iccid")!!,
|
||||
name
|
||||
profileRenameNewName.editText!!.text.toString().trim()
|
||||
).waitDone()
|
||||
|
||||
when (res) {
|
||||
is LocalProfileAssistant.ProfileNameTooLongException -> {
|
||||
showErrorAndCancel(R.string.profile_rename_too_long)
|
||||
}
|
||||
|
||||
is LocalProfileAssistant.ProfileNameIsInvalidUTF8Exception -> {
|
||||
showErrorAndCancel(R.string.profile_rename_encoding_error)
|
||||
}
|
||||
|
||||
is Throwable -> {
|
||||
showErrorAndCancel(R.string.profile_rename_failure)
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (parentFragment is EuiccProfilesChangedListener) {
|
||||
(parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
|
||||
}
|
||||
|
@ -114,3 +134,5 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package im.angry.openeuicc.ui.wizard
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.content.ClipboardManager
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -8,6 +9,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
|
@ -68,6 +70,9 @@ class DownloadWizardMethodSelectFragment : DownloadWizardActivity.DownloadWizard
|
|||
DownloadMethod(R.drawable.ic_gallery_black, R.string.download_wizard_method_gallery) {
|
||||
gallerySelectorLauncher.launch("image/*")
|
||||
},
|
||||
DownloadMethod(R.drawable.ic_paste_go, R.string.download_wizard_method_clipboard) {
|
||||
handleLoadFromClipboard()
|
||||
},
|
||||
DownloadMethod(R.drawable.ic_edit, R.string.download_wizard_method_manual) {
|
||||
gotoNextFragment(DownloadWizardDetailsFragment())
|
||||
}
|
||||
|
@ -103,6 +108,22 @@ class DownloadWizardMethodSelectFragment : DownloadWizardActivity.DownloadWizard
|
|||
return view
|
||||
}
|
||||
|
||||
private fun handleLoadFromClipboard() {
|
||||
val clipboard = requireContext().getSystemService(ClipboardManager::class.java)
|
||||
val text = clipboard.primaryClip?.getItemAt(0)?.text
|
||||
|
||||
if (text == null) {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
R.string.profile_download_no_lpa_string,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return
|
||||
}
|
||||
|
||||
processLpaString(text.toString())
|
||||
}
|
||||
|
||||
private fun processLpaString(s: String) {
|
||||
val components = s.split("$")
|
||||
if (components.size < 3 || components[0] != "LPA:1") {
|
||||
|
|
|
@ -57,13 +57,15 @@ class DownloadWizardSlotSelectFragment : DownloadWizardActivity.DownloadWizardSt
|
|||
super.beforeNext()
|
||||
|
||||
if (adapter.selected.freeSpace < LOW_NVRAM_THRESHOLD) {
|
||||
val activity = requireActivity()
|
||||
|
||||
AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle(R.string.profile_download_low_nvram_title)
|
||||
setMessage(R.string.profile_download_low_nvram_message)
|
||||
setCancelable(true)
|
||||
setPositiveButton(android.R.string.ok, null)
|
||||
setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
requireActivity().finish()
|
||||
activity.finish()
|
||||
}
|
||||
show()
|
||||
}
|
||||
|
|
7
app-common/src/main/res/drawable/ic_paste_go.xml
Normal file
7
app-common/src/main/res/drawable/ic_paste_go.xml
Normal file
|
@ -0,0 +1,7 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="?attr/colorControlNormal" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M5,5h2v3h10V5h2v6h2V5c0,-1.1 -0.9,-2 -2,-2h-4.18C14.4,1.84 13.3,1 12,1S9.6,1.84 9.18,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h5v-2H5V5zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1s-1,-0.45 -1,-1S11.45,3 12,3z"/>
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18.01,13l-1.42,1.41l1.58,1.58l-6.17,0l0,2l6.17,0l-1.58,1.59l1.42,1.41l3.99,-4z"/>
|
||||
|
||||
</vector>
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintWidth_percent="1"
|
||||
app:navigationIcon="?homeAsUpIndicator" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="48dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/toolbar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/ok"
|
||||
android:icon="@drawable/ic_check_black"
|
||||
android:title="@string/slot_select_select"
|
||||
app:showAsAction="ifRoom"/>
|
||||
</menu>
|
|
@ -16,10 +16,7 @@
|
|||
<string name="enable_disable_timeout">eSIM チップがプロファイルの切り替えの待機中にタイムアウトしました。これはデバイスのモデムファームウェアのバグの可能性があります。機内モードに切り替えるかアプリを再起動、デバイスを再起動してください。</string>
|
||||
<string name="switch_did_not_refresh">操作は成功しましたが、デバイスのモデムが更新を拒否しました。新しいプロファイルを使用するには機内モードに切り替えるか、再起動する必要があります。</string>
|
||||
<string name="toast_profile_enable_failed">新しい eSIM プロファイルに切り替えることができません。</string>
|
||||
<string name="toast_profile_name_too_long">ニックネームは 64 文字以内にしてください</string>
|
||||
<string name="toast_iccid_copied">ICCID をクリップボードにコピーしました</string>
|
||||
<string name="slot_select">スロットを選択</string>
|
||||
<string name="slot_select_select">選択</string>
|
||||
<string name="usb_permission">USB の権限を許可</string>
|
||||
<string name="usb_permission_needed">USB スマートカードリーダーにアクセスするには許可が必要です。</string>
|
||||
<string name="usb_failed">USB スマートカードリーダー経由で eSIM に接続できません。</string>
|
||||
|
|
|
@ -17,10 +17,7 @@
|
|||
<string name="enable_disable_timeout">等待 eSIM 芯片切换配置文件时超时。这可能是您手机基带固件中的一个错误。请尝试切换飞行模式、重新启动应用程序或重新启动手机</string>
|
||||
<string name="switch_did_not_refresh">操作成功, 但是您手机的基带拒绝刷新。您可能需要切换飞行模式或重新启动,以便使用新的配置文件。</string>
|
||||
<string name="toast_profile_enable_failed">无法切换到新的 eSIM 配置文件。</string>
|
||||
<string name="toast_profile_name_too_long">昵称不能超过 64 个字符</string>
|
||||
<string name="toast_iccid_copied">已复制 ICCID 到剪贴板</string>
|
||||
<string name="slot_select">选择卡槽</string>
|
||||
<string name="slot_select_select">选择</string>
|
||||
<string name="usb_permission">授予 USB 权限</string>
|
||||
<string name="usb_permission_needed">需要获得访问 USB 智能卡读卡器的权限。</string>
|
||||
<string name="usb_failed">无法通过 USB 智能卡读卡器连接到 eSIM。</string>
|
||||
|
|
|
@ -28,14 +28,10 @@
|
|||
<string name="switch_did_not_refresh">The operation was successful, but your phone\'s modem refused to refresh. You might need to toggle airplane mode or reboot in order to use the new profile.</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_profile_delete_confirm_text_mismatched">Confirmation string mismatch</string>
|
||||
<string name="toast_iccid_copied">ICCID copied to clipboard</string>
|
||||
<string name="toast_eid_copied">EID copied to clipboard</string>
|
||||
|
||||
<string name="slot_select">Select Slot</string>
|
||||
<string name="slot_select_select">Select</string>
|
||||
|
||||
<string name="usb_permission">Grant USB permission</string>
|
||||
<string name="usb_permission_needed">Permission is needed to access the USB smart card reader.</string>
|
||||
<string name="usb_failed">Cannot connect to eSIM via a USB smart card reader.</string>
|
||||
|
@ -58,8 +54,9 @@
|
|||
|
||||
<string name="profile_download_low_nvram_title">This download may fail</string>
|
||||
<string name="profile_download_low_nvram_message">This download may fail due to low remaining capacity.</string>
|
||||
<string name="profile_download_no_lpa_string">No LPA string found in clipboard</string>
|
||||
<string name="profile_download_incorrect_lpa_string">Incorrect LPA String</string>
|
||||
<string name="profile_download_incorrect_lpa_string_message">The LPA string could not be parsed</string>
|
||||
<string name="profile_download_incorrect_lpa_string_message">Could not parse QR code or clipboard content as an LPA string for downloading eSIMs.</string>
|
||||
|
||||
<string name="download_wizard">Download Wizard</string>
|
||||
<string name="download_wizard_back">Back</string>
|
||||
|
@ -75,6 +72,7 @@
|
|||
<string name="download_wizard_method_select">How would you like to download the eSIM profile?</string>
|
||||
<string name="download_wizard_method_qr_code">Scan a QR code with camera</string>
|
||||
<string name="download_wizard_method_gallery">Load a QR code from gallery</string>
|
||||
<string name="download_wizard_method_clipboard">Load from Clipboard</string>
|
||||
<string name="download_wizard_method_manual">Enter manually</string>
|
||||
<string name="download_wizard_details">Input or confirm details for downloading your eSIM:</string>
|
||||
<string name="download_wizard_progress">Downloading your eSIM…</string>
|
||||
|
@ -98,6 +96,9 @@
|
|||
<string name="logs_saved_message">Logs have been saved to the selected path. Would you like to share the log through another app?</string>
|
||||
|
||||
<string name="profile_rename_new_name">New nickname</string>
|
||||
<string name="profile_rename_encoding_error">Failed to encode nickname as UTF-8</string>
|
||||
<string name="profile_rename_too_long">Nickname is too long</string>
|
||||
<string name="profile_rename_failure">Unknown failure when renaming profile</string>
|
||||
|
||||
<string name="profile_delete_confirm">Are you sure you want to delete the profile %s? This operation is irreversible.</string>
|
||||
<string name="profile_delete_confirm_input">Type \'%s\' here to confirm deletion</string>
|
||||
|
|
|
@ -12,6 +12,10 @@ interface LocalProfileAssistant {
|
|||
val lastApduException: Exception?,
|
||||
) : Exception("Failed to download profile")
|
||||
|
||||
class ProfileRenameException() : Exception("Failed to rename profile")
|
||||
class ProfileNameTooLongException() : Exception("Profile name too long")
|
||||
class ProfileNameIsInvalidUTF8Exception() : Exception("Profile name is invalid UTF-8")
|
||||
|
||||
val valid: Boolean
|
||||
val profiles: List<LocalProfileInfo>
|
||||
val notifications: List<LocalProfileNotification>
|
||||
|
@ -40,9 +44,14 @@ interface LocalProfileAssistant {
|
|||
|
||||
fun euiccMemoryReset()
|
||||
|
||||
/**
|
||||
* Nickname must be valid UTF-8 and shorter than 64 chars.
|
||||
*
|
||||
* May throw one of: ProfileRenameException, ProfileNameTooLongException, ProfileNameIsInvalidUTF8Exception
|
||||
*/
|
||||
fun setNickname(
|
||||
iccid: String, nickname: String
|
||||
): Boolean
|
||||
)
|
||||
|
||||
fun close()
|
||||
}
|
|
@ -19,7 +19,7 @@ internal object LpacJni {
|
|||
external fun es10cEnableProfile(handle: Long, iccid: String, refresh: Boolean): Int
|
||||
external fun es10cDisableProfile(handle: Long, iccid: String, refresh: Boolean): Int
|
||||
external fun es10cDeleteProfile(handle: Long, iccid: String): Int
|
||||
external fun es10cSetNickname(handle: Long, iccid: String, nick: String): Int
|
||||
external fun es10cSetNickname(handle: Long, iccid: String, nickNullTerminated: ByteArray): Int
|
||||
|
||||
// es10b
|
||||
external fun es10bListNotification(handle: Long): Long // A native pointer to a linked list. Handle with linked list-related methods below. May be 0 (null)
|
||||
|
|
|
@ -239,8 +239,23 @@ class LocalProfileAssistantImpl(
|
|||
} == 0
|
||||
|
||||
@Synchronized
|
||||
override fun setNickname(iccid: String, nickname: String): Boolean =
|
||||
LpacJni.es10cSetNickname(contextHandle, iccid, nickname) == 0
|
||||
override fun setNickname(iccid: String, nickname: String) {
|
||||
val encoded = try {
|
||||
Charsets.UTF_8.encode(nickname).array()
|
||||
} catch (e: CharacterCodingException) {
|
||||
throw LocalProfileAssistant.ProfileNameIsInvalidUTF8Exception()
|
||||
}
|
||||
|
||||
if (encoded.size >= 64) {
|
||||
throw LocalProfileAssistant.ProfileNameTooLongException()
|
||||
}
|
||||
|
||||
val encodedNullTerminated = encoded + byteArrayOf(0)
|
||||
|
||||
if (LpacJni.es10cSetNickname(contextHandle, iccid, encodedNullTerminated) != 0) {
|
||||
throw LocalProfileAssistant.ProfileRenameException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun euiccMemoryReset() {
|
||||
LpacJni.es10cEuiccMemoryReset(contextHandle)
|
||||
|
|
|
@ -205,16 +205,16 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cDisableProfile(JNIEnv *env, jobject thi
|
|||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_net_typeblog_lpac_1jni_LpacJni_es10cSetNickname(JNIEnv *env, jobject thiz, jlong handle,
|
||||
jstring iccid, jstring nick) {
|
||||
jstring iccid, jbyteArray nick) {
|
||||
struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
|
||||
const char *_iccid = NULL;
|
||||
const char *_nick = NULL;
|
||||
jbyte *_nick = NULL;
|
||||
int ret;
|
||||
|
||||
_iccid = (*env)->GetStringUTFChars(env, iccid, NULL);
|
||||
_nick = (*env)->GetStringUTFChars(env, nick, NULL);
|
||||
ret = es10c_set_nickname(ctx, _iccid, _nick);
|
||||
(*env)->ReleaseStringUTFChars(env, nick, _nick);
|
||||
_nick = (*env)->GetByteArrayElements(env, nick, NULL);
|
||||
ret = es10c_set_nickname(ctx, _iccid, (const char *) _nick);
|
||||
(*env)->ReleaseByteArrayElements(env, nick, _nick, JNI_ABORT);
|
||||
(*env)->ReleaseStringUTFChars(env, iccid, _iccid);
|
||||
return ret;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue