forked from PeterCxy/OpenEUICC
feat: copyable eid string #1
11 changed files with 85 additions and 47 deletions
|
@ -1,6 +1,7 @@
|
|||
package im.angry.openeuicc.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ClipData
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
|
@ -8,6 +9,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
@ -16,6 +18,7 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import im.angry.openeuicc.common.R
|
||||
import im.angry.openeuicc.core.EuiccChannel
|
||||
import im.angry.openeuicc.core.EuiccChannelManager
|
||||
import im.angry.openeuicc.util.*
|
||||
import kotlinx.coroutines.launch
|
||||
import net.typeblog.lpac_jni.impl.DEFAULT_PKID_GSMA_RSP2_ROOT_CI1
|
||||
|
@ -32,6 +35,13 @@ class EuiccInfoActivity : BaseEuiccAccessActivity() {
|
|||
|
||||
private var logicalSlotId: Int = -1
|
||||
|
||||
data class Item(
|
||||
@StringRes
|
||||
val titleResId: Int,
|
||||
val content: String?,
|
||||
val copyable: Boolean = false,
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -41,19 +51,21 @@ class EuiccInfoActivity : BaseEuiccAccessActivity() {
|
|||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
swipeRefresh = requireViewById(R.id.swipe_refresh)
|
||||
infoList = requireViewById(R.id.recycler_view)
|
||||
|
||||
infoList.layoutManager =
|
||||
LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
infoList.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
|
||||
infoList.adapter = EuiccInfoAdapter()
|
||||
infoList = requireViewById<RecyclerView>(R.id.recycler_view).also {
|
||||
it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
|
||||
it.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
|
||||
it.adapter = EuiccInfoAdapter()
|
||||
}
|
||||
|
||||
logicalSlotId = intent.getIntExtra("logicalSlotId", 0)
|
||||
|
||||
title = getString(
|
||||
R.string.euicc_info_activity_title,
|
||||
val channelTitle = if (logicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
|
||||
getString(R.string.usb)
|
||||
} else {
|
||||
getString(R.string.channel_name_format, logicalSlotId)
|
||||
)
|
||||
}
|
||||
|
||||
title = getString(R.string.euicc_info_activity_title, channelTitle)
|
||||
|
||||
swipeRefresh.setOnRefreshListener { refresh() }
|
||||
|
||||
|
@ -78,33 +90,31 @@ class EuiccInfoActivity : BaseEuiccAccessActivity() {
|
|||
|
||||
lifecycleScope.launch {
|
||||
(infoList.adapter!! as EuiccInfoAdapter).euiccInfoItems =
|
||||
euiccChannelManager.withEuiccChannel(logicalSlotId, ::buildPairs).map {
|
||||
Pair(getString(it.first), it.second ?: getString(R.string.unknown))
|
||||
}
|
||||
euiccChannelManager.withEuiccChannel(logicalSlotId, ::buildEuiccInfoItems)
|
||||
|
||||
swipeRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildPairs(channel: EuiccChannel) = buildList {
|
||||
add(Pair(R.string.euicc_info_access_mode, channel.type))
|
||||
private fun buildEuiccInfoItems(channel: EuiccChannel) = buildList {
|
||||
add(Item(R.string.euicc_info_access_mode, channel.type))
|
||||
add(
|
||||
Pair(
|
||||
Item(
|
||||
R.string.euicc_info_removable,
|
||||
formatByBoolean(channel.port.card.isRemovable, YES_NO)
|
||||
)
|
||||
)
|
||||
add(Pair(R.string.euicc_info_eid, channel.lpa.eID))
|
||||
add(Item(R.string.euicc_info_eid, channel.lpa.eID, copyable = true))
|
||||
channel.lpa.euiccInfo2.let { info ->
|
||||
add(Pair(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion))
|
||||
add(Pair(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion))
|
||||
add(Pair(R.string.euicc_info_pp_version, info?.ppVersion))
|
||||
add(Pair(R.string.euicc_info_sas_accreditation_number, info?.sasAccreditationNumber))
|
||||
add(Pair(R.string.euicc_info_free_nvram, info?.freeNvram?.let(::formatFreeSpace)))
|
||||
add(Item(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion))
|
||||
add(Item(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion))
|
||||
add(Item(R.string.euicc_info_pp_version, info?.ppVersion))
|
||||
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?.euiccCiPKIdListForSigning.orEmpty().let { signers ->
|
||||
add(
|
||||
Pair(
|
||||
Item(
|
||||
R.string.euicc_info_gsma_prod,
|
||||
formatByBoolean(
|
||||
signers.contains(DEFAULT_PKID_GSMA_RSP2_ROOT_CI1),
|
||||
|
@ -113,7 +123,7 @@ class EuiccInfoActivity : BaseEuiccAccessActivity() {
|
|||
)
|
||||
)
|
||||
add(
|
||||
Pair(
|
||||
Item(
|
||||
R.string.euicc_info_gsma_test,
|
||||
formatByBoolean(PKID_GSMA_TEST_CI.any(signers::contains), SUPPORTED_UNSUPPORTED)
|
||||
)
|
||||
|
@ -131,17 +141,28 @@ class EuiccInfoActivity : BaseEuiccAccessActivity() {
|
|||
)
|
||||
|
||||
inner class EuiccInfoViewHolder(root: View) : ViewHolder(root) {
|
||||
private val context = root.context
|
||||
private val title: TextView = root.requireViewById(R.id.euicc_info_title)
|
||||
private val content: TextView = root.requireViewById(R.id.euicc_info_content)
|
||||
private var copyable = false
|
||||
|
||||
fun bind(item: Pair<String, String>) {
|
||||
title.text = item.first
|
||||
content.text = item.second
|
||||
init {
|
||||
root.setOnClickListener {
|
||||
if (copyable) context.setClipboard(title.text.toString()) {
|
||||
ClipData.newPlainText(title.text, content.text.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(item: Item) {
|
||||
title.setText(item.titleResId)
|
||||
content.text = item.content ?: getString(R.string.unknown)
|
||||
copyable = item.copyable
|
||||
}
|
||||
}
|
||||
|
||||
inner class EuiccInfoAdapter : RecyclerView.Adapter<EuiccInfoViewHolder>() {
|
||||
var euiccInfoItems: List<Pair<String, String>> = listOf()
|
||||
var euiccInfoItems: List<Item> = listOf()
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
set(newVal) {
|
||||
field = newVal
|
||||
|
|
|
@ -2,7 +2,6 @@ package im.angry.openeuicc.ui
|
|||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.method.PasswordTransformationMethod
|
||||
|
@ -346,10 +345,9 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
|
|||
}
|
||||
|
||||
iccid.setOnLongClickListener {
|
||||
requireContext().getSystemService(ClipboardManager::class.java)!!
|
||||
.setPrimaryClip(ClipData.newPlainText("iccid", iccid.text))
|
||||
Toast.makeText(requireContext(), R.string.toast_iccid_copied, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
requireContext().setClipboard("ICCID") {
|
||||
ClipData.newPlainText("iccid", iccid.text)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package im.angry.openeuicc.ui.wizard
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -104,7 +105,16 @@ class DownloadWizardMethodSelectFragment : DownloadWizardActivity.DownloadWizard
|
|||
|
||||
private fun processLpaString(s: String) {
|
||||
val components = s.split("$")
|
||||
if (components.size < 3 || components[0] != "LPA:1") return
|
||||
if (components.size < 3 || components[0] != "LPA:1") {
|
||||
AlertDialog.Builder(requireContext()).apply {
|
||||
setTitle(R.string.profile_download_incorrect_lpa_string)
|
||||
setMessage(R.string.profile_download_incorrect_lpa_string_message)
|
||||
setCancelable(true)
|
||||
setNegativeButton(android.R.string.cancel, null)
|
||||
show()
|
||||
}
|
||||
return
|
||||
}
|
||||
state.smdp = components[1]
|
||||
state.matchingId = components[2]
|
||||
gotoNextFragment(DownloadWizardDetailsFragment())
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package im.angry.openeuicc.util
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import im.angry.openeuicc.common.R
|
||||
|
||||
fun Context.setClipboard(label: String, callback: () -> ClipData) {
|
||||
getSystemService(ClipboardManager::class.java)!!
|
||||
.setPrimaryClip(callback())
|
||||
Toast.makeText(this, getString(R.string.toast_copied, label), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
<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="toast_copied">%s をクリップボードにコピーしました</string>
|
||||
<string name="slot_select">スロットを選択</string>
|
||||
<string name="slot_select_select">選択</string>
|
||||
<string name="usb_permission">USB の権限を許可</string>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
<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="toast_copied">已复制 %s 到剪贴板</string>
|
||||
<string name="slot_select">选择卡槽</string>
|
||||
<string name="slot_select_select">选择</string>
|
||||
<string name="usb_permission">授予 USB 权限</string>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
<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_iccid_copied">ICCID copied to clipboard</string>
|
||||
<string name="toast_copied">%s copied to clipboard</string>
|
||||
|
||||
<string name="slot_select">Select Slot</string>
|
||||
<string name="slot_select_select">Select</string>
|
||||
|
@ -56,6 +56,8 @@
|
|||
|
||||
<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_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="download_wizard">Download Wizard</string>
|
||||
<string name="download_wizard_back">Back</string>
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
package im.angry.openeuicc.ui
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.preference.Preference
|
||||
import im.angry.easyeuicc.R
|
||||
import im.angry.openeuicc.util.encodeHex
|
||||
import im.angry.openeuicc.util.setClipboard
|
||||
import java.security.MessageDigest
|
||||
|
||||
class UnprivilegedSettingsFragment : SettingsFragment() {
|
||||
|
@ -33,10 +32,9 @@ class UnprivilegedSettingsFragment : SettingsFragment() {
|
|||
findPreference<Preference>("pref_info_ara_m")?.apply {
|
||||
summary = firstSigner.encodeHex()
|
||||
setOnPreferenceClickListener {
|
||||
requireContext().getSystemService(ClipboardManager::class.java)!!
|
||||
.setPrimaryClip(ClipData.newPlainText("ara-m", summary))
|
||||
Toast.makeText(requireContext(), R.string.toast_ara_m_copied, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
requireContext().setClipboard("ARA-M SHA-1") {
|
||||
ClipData.newPlainText("ara-m", summary)
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,5 +30,4 @@
|
|||
<string name="compatibility_check_verdict_unknown_likely_fail">挿入された取り外し可能な eSIM がデバイス上で管理できるかどうかは判断できません。デバイスが OMAPI のサポートを宣言していないため、このデバイス上で取り外し可能な eSIM を管理することはサポートされていない可能性があります。\n%s</string>
|
||||
<string name="compatibility_check_verdict_unknown">挿入された取り外し可能な eSIM がデバイス上で管理できるかどうかを確認できません。\n%s</string>
|
||||
<string name="compatibility_check_verdict_fail_shared">ただし、eSIM プロファイルがすでに読み込まれている場合、有効化されたプロファイル自体は引き続き機能します。また、プロファイルが管理できない場合は、このデバイスで USB カードリーダーを介してプロファイルを管理できる可能性があります。</string>
|
||||
<string name="toast_ara_m_copied">ARA-M SHA-1 をクリップボードにコピーしました</string>
|
||||
</resources>
|
||||
|
|
|
@ -28,5 +28,4 @@
|
|||
<string name="compatibility_check_verdict_unknown_likely_fail">我们无法确定是否可以在您的设备上管理可插拔 eSIM 卡。由于您的设备未声明支持OMAPI,因此更有可能不支持在此设备上管理可插拔 eSIM。\n%s</string>
|
||||
<string name="compatibility_check_verdict_unknown">我们无法确定是否可以在您的设备上管理可插拔 eSIM 卡。\n%s</string>
|
||||
<string name="compatibility_check_verdict_fail_shared">然而,已经加载了eSIM配置文件的可插拔 eSIM 卡仍然可以工作; 即使无法在装置上直接管理可插拔 eSIM 卡中的配置文件,您仍然可以使用 USB 卡读卡器来管理配置文件。</string>
|
||||
<string name="toast_ara_m_copied">ARA-M SHA-1 已拷贝到剪贴板</string>
|
||||
</resources>
|
|
@ -7,9 +7,6 @@
|
|||
<!-- Settings -->
|
||||
<string name="pref_developer_ara_m" translatable="false">ARA-M SHA-1</string>
|
||||
|
||||
<!-- Toast -->
|
||||
<string name="toast_ara_m_copied">ARA-M SHA-1 copied to clipboard</string>
|
||||
|
||||
<!-- Compatibility Check Descriptions -->
|
||||
<string name="compatibility_check_system_features">System Features</string>
|
||||
<string name="compatibility_check_system_features_desc">Whether your device has all the required features for managing removable eUICC cards. For example, basic telephony and OMAPI support.</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue