Implement profile renaming

This commit is contained in:
Peter Cai 2022-04-30 21:57:02 -04:00
parent 45dcb1d282
commit 715f19166f
10 changed files with 275 additions and 6 deletions

View file

@ -8,8 +8,10 @@
<entry key="app/src/main/res/layout/euicc_profile.xml" value="0.19375" />
<entry key="app/src/main/res/layout/fragment_euicc.xml" value="0.19375" />
<entry key="app/src/main/res/layout/fragment_profile_download.xml" value="0.19375" />
<entry key="app/src/main/res/layout/fragment_profile_rename.xml" value="0.19375" />
<entry key="app/src/main/res/menu/activity_main_slot_spinner.xml" value="0.19375" />
<entry key="app/src/main/res/menu/fragment_profile_download.xml" value="0.19375" />
<entry key="app/src/main/res/menu/fragment_profile_rename.xml" value="0.19375" />
<entry key="app/src/main/res/menu/profile_options.xml" value="0.19375" />
</map>
</option>

View file

@ -128,12 +128,8 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
fun setProfile(profile: Map<String, String>) {
this.profile = profile
binding.name.text =
if (profile[NICKNAME.name].isNullOrEmpty()) {
profile[NAME.name]
} else {
profile[NICKNAME.name]
}
binding.name.text = getName()
binding.state.setText(
if (isEnabled()) {
R.string.enabled
@ -149,6 +145,13 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
private fun isEnabled(): Boolean =
profile[STATE.name]?.lowercase() == "enabled"
private fun getName(): String =
if (profile[NICKNAME.name].isNullOrEmpty()) {
profile[NAME.name]
} else {
profile[NICKNAME.name]
}!!
private fun showOptionsMenu() {
PopupMenu(binding.root.context, binding.profileMenu).apply {
setOnMenuItemClickListener(::onMenuItemClicked)
@ -166,6 +169,11 @@ class EuiccManagementFragment : Fragment(), EuiccFragmentMarker, EuiccProfilesCh
enableProfile(profile[ICCID.name]!!)
true
}
R.id.rename -> {
ProfileRenameFragment.newInstance(slotId, profile[ICCID.name]!!, getName())
.show(childFragmentManager, ProfileRenameFragment.TAG)
true
}
else -> false
}
}

View file

@ -0,0 +1,109 @@
package im.angry.openeuicc.ui
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.Window
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import im.angry.openeuicc.R
import im.angry.openeuicc.databinding.FragmentProfileRenameBinding
import im.angry.openeuicc.util.setWidthPercent
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.lang.Exception
import java.lang.RuntimeException
class ProfileRenameFragment : DialogFragment(), EuiccFragmentMarker {
companion object {
const val TAG = "ProfileRenameFragment"
fun newInstance(slotId: Int, iccid: String, currentName: String): ProfileRenameFragment {
val instance = newInstanceEuicc(ProfileRenameFragment::class.java, slotId)
instance.requireArguments().apply {
putString("iccid", iccid)
putString("currentName", currentName)
}
return instance
}
}
private var _binding: FragmentProfileRenameBinding? = null
private val binding get() = _binding!!
private var renaming = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentProfileRenameBinding.inflate(inflater, container, false)
binding.toolbar.inflateMenu(R.menu.fragment_profile_rename)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.apply {
setTitle(R.string.rename)
setNavigationOnClickListener {
if (!renaming) dismiss()
}
setOnMenuItemClickListener {
if (!renaming) rename()
true
}
}
}
override fun onStart() {
super.onStart()
binding.profileRenameNewName.editText!!.setText(requireArguments().getString("currentName"))
}
override fun onResume() {
super.onResume()
setWidthPercent(95)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).also {
it.window?.requestFeature(Window.FEATURE_NO_TITLE)
it.setCanceledOnTouchOutside(false)
}
}
private fun rename() {
val name = binding.profileRenameNewName.editText!!.text.toString().trim()
renaming = true
binding.progress.isIndeterminate = true
binding.progress.visibility = View.VISIBLE
lifecycleScope.launch {
try {
doRename(name)
} catch (e: Exception) {
Log.d(TAG, "Failed to rename profile")
Log.d(TAG, Log.getStackTraceString(e))
} finally {
if (parentFragment is EuiccProfilesChangedListener) {
(parentFragment as EuiccProfilesChangedListener).onEuiccProfilesChanged()
}
dismiss()
}
}
}
private suspend fun doRename(name: String) = withContext(Dispatchers.IO) {
if (!channel.lpa.setNickname(requireArguments().getString("iccid"), name)) {
throw RuntimeException("Profile nickname not changed")
}
}
}

View file

@ -0,0 +1,60 @@
<?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">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:theme="@style/Theme.OpenEUICC"
android:background="?attr/colorPrimary"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintWidth_percent="1"
app:navigationIcon="?homeAsUpIndicator" />
<View
android:id="@+id/guideline"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@id/toolbar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ProgressBar
android:id="@+id/progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/guideline"
style="@style/Widget.AppCompat.ProgressBar.Horizontal" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/profile_rename_new_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="15dp"
android:hint="@string/profile_rename_new_name"
style="@style/Widget.OpenEUICC.Input"
app:layout_constraintTop_toBottomOf="@id/toolbar"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintWidth_percent=".8">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/Theme.OpenEUICC.Input.Cursor"/>
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,9 @@
<?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/rename"
app:showAsAction="ifRoom"/>
</menu>

View file

@ -4,6 +4,10 @@
android:id="@+id/enable"
android:title="@string/enable"/>
<item
android:id="@+id/rename"
android:title="@string/rename"/>
<item
android:id="@+id/delete"
android:title="@string/delete"/>

View file

@ -10,6 +10,7 @@
<string name="enable">Enable</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>
@ -20,4 +21,6 @@
<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>
</resources>

View file

@ -37,4 +37,6 @@ public interface LocalProfileAssistant {
String allocateProfile(String mcc);
void processPendingNotifications();
boolean setNickname(String iccid, String nickname);
}

View file

@ -136,6 +136,11 @@ public class LocalProfileAssistantImpl implements LocalProfileAssistant {
}
@Override
public boolean setNickname(String iccid, String nickname) {
return new SetNicknameWorker(iccid, nickname, apduChannel).run();
}
public void smdsRetrieveEvents(Progress progress) {
// return new SmdsRetrieveEvents();
}

View file

@ -0,0 +1,67 @@
package com.truphone.lpa.impl;
import com.truphone.lpa.ApduChannel;
import com.truphone.lpa.apdu.ApduUtils;
import com.truphone.rsp.dto.asn1.rspdefinitions.SetNicknameResponse;
import com.truphone.util.LogStub;
import com.truphone.util.Util;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SetNicknameWorker {
private static final Logger LOG = Logger.getLogger(ListProfilesWorker.class.getName());
private final String iccid;
private final String nickname;
private final ApduChannel apduChannel;
SetNicknameWorker(String iccid, String nickname, ApduChannel apduChannel) {
this.apduChannel = apduChannel;
this.iccid = iccid;
this.nickname = nickname;
}
boolean run() {
if (LogStub.getInstance().isDebugEnabled()) {
LogStub.getInstance().logDebug(LOG, LogStub.getInstance().getTag() + " - Renaming profile: " + iccid);
}
String apdu = ApduUtils.setNicknameApdu(iccid, Util.byteArrayToHexString(nickname.getBytes(), ""));
String eResponse = apduChannel.transmitAPDU(apdu);
try {
InputStream is = new ByteArrayInputStream(Hex.decodeHex(eResponse.toCharArray()));
SetNicknameResponse response = new SetNicknameResponse();
response.decode(is);
if ("0".equals(response.getSetNicknameResult().toString())) {
if (LogStub.getInstance().isDebugEnabled()) {
LogStub.getInstance().logDebug(LOG, LogStub.getInstance().getTag() + " - Profile renamed: " + iccid);
}
return true;
} else {
if (LogStub.getInstance().isDebugEnabled()) {
LogStub.getInstance().logDebug(LOG, LogStub.getInstance().getTag() + " - Profile not renamed: " + iccid);
}
return false;
}
} catch (IOException ioe) {
LOG.log(Level.SEVERE, LogStub.getInstance().getTag() + " - iccid: " + iccid + " profile failed to be renamed");
throw new RuntimeException("Unable to rename profile: " + iccid + ", response: " + eResponse);
} catch (DecoderException e) {
LOG.log(Level.SEVERE, LogStub.getInstance().getTag() + " - " + e.getMessage(), e);
LOG.log(Level.SEVERE, LogStub.getInstance().getTag() + " - iccid: " + iccid + " profile failed to be renamed. Exception in Decoder:" + e.getMessage());
throw new RuntimeException("Unable to rename profile: " + iccid + ", response: " + eResponse);
}
}
}