diff --git a/README.md b/README.md
new file mode 100644
index 0000000..369266f
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+## Keycard Connect
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a1b64a8..9c75396 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,7 @@
+
diff --git a/app/src/main/java/im/status/keycard/connect/card/CardScriptExecutor.kt b/app/src/main/java/im/status/keycard/connect/card/CardScriptExecutor.kt
index c2c29c8..fa52fae 100644
--- a/app/src/main/java/im/status/keycard/connect/card/CardScriptExecutor.kt
+++ b/app/src/main/java/im/status/keycard/connect/card/CardScriptExecutor.kt
@@ -8,7 +8,7 @@ import im.status.keycard.io.CardChannel
import im.status.keycard.io.CardListener
class CardScriptExecutor(private val listener: ScriptListener) : CardListener {
- data class ScriptContext(val activity: Activity, val cmdSet: KeycardCommandSet)
+ data class ScriptContext(val activity: Activity, val channel: CardChannel, val cmdSet: KeycardCommandSet)
enum class State {
READY, UX_ONGOING, RUNNING
@@ -21,7 +21,7 @@ class CardScriptExecutor(private val listener: ScriptListener) : CardListener {
private var waitingCmd: CardCommand? = null
override fun onConnected(channel: CardChannel) {
- val executionContext = ScriptContext(Registry.mainActivity, KeycardCommandSet(channel))
+ val executionContext = ScriptContext(Registry.mainActivity, channel, KeycardCommandSet(channel))
if (state == State.READY) {
startScript()
diff --git a/app/src/main/java/im/status/keycard/connect/card/ReinstallCommand.kt b/app/src/main/java/im/status/keycard/connect/card/ReinstallCommand.kt
new file mode 100644
index 0000000..d16d402
--- /dev/null
+++ b/app/src/main/java/im/status/keycard/connect/card/ReinstallCommand.kt
@@ -0,0 +1,24 @@
+package im.status.keycard.connect.card
+
+import android.net.Uri
+import im.status.keycard.connect.Registry
+import im.status.keycard.globalplatform.GlobalPlatformCommandSet
+
+class ReinstallCommand(private val applet: Uri, private val installWallet: Boolean, private val installCash: Boolean, private val installNDEF: Boolean, private val cashParams: ByteArray = ByteArray(0), private val ndefParams: ByteArray = ByteArray(0)) : CardCommand {
+ override fun run(context: CardScriptExecutor.ScriptContext): CardCommand.Result {
+ return runOnCard {
+ val gpCmd = GlobalPlatformCommandSet(context.channel)
+ gpCmd.select().checkOK()
+ gpCmd.openSecureChannel()
+ gpCmd.deleteKeycardInstancesAndPackage()
+
+ Registry.mainActivity.applicationContext.contentResolver.openInputStream(applet)?.use {
+ gpCmd.loadKeycardPackage(it) {_, _ -> }
+ }
+
+ if (installWallet) { gpCmd.installKeycardApplet().checkOK() }
+ if (installCash) { gpCmd.installCashApplet(cashParams).checkOK() }
+ if (installNDEF) { gpCmd.installNDEFApplet(ndefParams).checkOK() }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/im/status/keycard/connect/data/Constants.kt b/app/src/main/java/im/status/keycard/connect/data/Constants.kt
index ca5fe65..5d0afd0 100644
--- a/app/src/main/java/im/status/keycard/connect/data/Constants.kt
+++ b/app/src/main/java/im/status/keycard/connect/data/Constants.kt
@@ -30,6 +30,7 @@ const val MNEMONIC_PHRASE = "mnemonicPhrase"
const val REQ_INTERACTIVE_SCRIPT = 0x01
const val REQ_WALLETCONNECT = 0x02
const val REQ_LOADKEY = 0x03
+const val REQ_APPLET_FILE = 0x04
const val CACHE_VALIDITY = 15 * 60 * 1000
diff --git a/app/src/main/java/im/status/keycard/connect/ui/ChangePairingPasswordActivity.kt b/app/src/main/java/im/status/keycard/connect/ui/ChangePairingPasswordActivity.kt
index 08e19a3..24c69df 100644
--- a/app/src/main/java/im/status/keycard/connect/ui/ChangePairingPasswordActivity.kt
+++ b/app/src/main/java/im/status/keycard/connect/ui/ChangePairingPasswordActivity.kt
@@ -11,7 +11,7 @@ import im.status.keycard.connect.card.scriptWithAuthentication
class ChangePairingPasswordActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
- //TODO: puk validation and confirmation
+ //TODO: pairing password confirmation
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_change_pairing_password)
}
diff --git a/app/src/main/java/im/status/keycard/connect/ui/MainActivity.kt b/app/src/main/java/im/status/keycard/connect/ui/MainActivity.kt
index 7ab0179..9b7a932 100644
--- a/app/src/main/java/im/status/keycard/connect/ui/MainActivity.kt
+++ b/app/src/main/java/im/status/keycard/connect/ui/MainActivity.kt
@@ -106,6 +106,10 @@ class MainActivity : AppCompatActivity(), ScriptListener {
Registry.scriptExecutor.runScript(scriptWithAuthentication().plus(RemoveKeyCommand()))
}
+ fun reinstall(@Suppress("UNUSED_PARAMETER") view: View) {
+ startCommand(ReinstallActivity::class)
+ }
+
fun settings(@Suppress("UNUSED_PARAMETER") view: View) {
startCommand(SettingsActivity::class)
}
diff --git a/app/src/main/java/im/status/keycard/connect/ui/ReinstallActivity.kt b/app/src/main/java/im/status/keycard/connect/ui/ReinstallActivity.kt
new file mode 100644
index 0000000..df2f38e
--- /dev/null
+++ b/app/src/main/java/im/status/keycard/connect/ui/ReinstallActivity.kt
@@ -0,0 +1,47 @@
+package im.status.keycard.connect.ui
+
+import android.content.Intent
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+import android.view.View
+import android.widget.CheckBox
+import im.status.keycard.connect.R
+import im.status.keycard.connect.Registry
+import im.status.keycard.connect.card.ReinstallCommand
+import im.status.keycard.connect.data.REQ_APPLET_FILE
+
+class ReinstallActivity : AppCompatActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_reinstall)
+ }
+
+ fun reinstall(@Suppress("UNUSED_PARAMETER") view: View) {
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ type = "*/*"
+ }
+
+ startActivityForResult(intent, REQ_APPLET_FILE)
+ }
+
+ fun cancel(@Suppress("UNUSED_PARAMETER") view: View) {
+ finish()
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+
+ if (requestCode == REQ_APPLET_FILE && resultCode == RESULT_OK) {
+ val reinstallWallet = findViewById(R.id.reinstallWalletCheckbox).isChecked
+ val reinstallCash = findViewById(R.id.reinstallCashCheckbox).isChecked
+ val reinstallNDEF = findViewById(R.id.reinstallNDEFCheckbox).isChecked
+ data?.data?.also { uri ->
+ val script = listOf(ReinstallCommand(uri, reinstallWallet, reinstallCash, reinstallNDEF))
+ Registry.scriptExecutor.runScript(script)
+ }
+ }
+
+ finish()
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index fe3b893..fd574de 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -23,7 +23,6 @@
android:id="@+id/walletConnectButton"
android:layout_width="236dp"
android:layout_height="wrap_content"
- android:layout_marginTop="24dp"
android:onClick="connectWallet"
android:text="@string/connect_wallet"
app:layout_constraintEnd_toEndOf="parent"
@@ -96,6 +95,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/changeKeyButton" />
+
+
+ app:layout_constraintTop_toBottomOf="@+id/reinstall" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_reinstall.xml b/app/src/main/res/layout/activity_reinstall.xml
new file mode 100644
index 0000000..4ffc4ca
--- /dev/null
+++ b/app/src/main/res/layout/activity_reinstall.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 6feaf46..c7dde6b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -37,4 +37,11 @@
Wallet Path
m/44\'/...
Settings
+ (Re)install applet
+ Reinstall applet
+ WARNING: Reinstalling the applet completely destroys the currently loaded keys and resets all credentials. Make sure you have your seed phrase somewhere to restore from. Additionally, JCOP3 cards can be destroyed if the connection is lost during this process. Hold the card tightly to the phone!
+ Install Wallet applet
+ Install Cash applet
+ Install NDEF applet
+ Select cap file and install