add base activities
This commit is contained in:
parent
d635bcf412
commit
6120d75964
|
@ -34,7 +34,7 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
implementation 'androidx.core:core-ktx:1.1.0'
|
implementation 'androidx.core:core-ktx:1.1.0'
|
||||||
implementation 'com.google.android.material:material:1.0.0'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="im.status.keycard.connect">
|
package="im.status.keycard.connect">
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.nfc.hce"
|
||||||
|
android:required="true" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
@ -9,6 +13,8 @@
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
|
<activity android:name=".PairingActivity"></activity>
|
||||||
|
<activity android:name=".PINActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
|
@ -20,5 +26,4 @@
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.nfc.hce" android:required="true" />
|
|
||||||
</manifest>
|
</manifest>
|
|
@ -1,6 +1,5 @@
|
||||||
package im.status.keycard.connect
|
package im.status.keycard.connect
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.nfc.NfcAdapter
|
import android.nfc.NfcAdapter
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package im.status.keycard.connect
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.View.INVISIBLE
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.TextView
|
||||||
|
import im.status.keycard.connect.data.PINCache
|
||||||
|
|
||||||
|
const val PIN_ACTIVITY_ATTEMPTS = "remainingAttempts"
|
||||||
|
const val PIN_ACTIVITY_CARD_UID = "cardUID"
|
||||||
|
|
||||||
|
class PINActivity : AppCompatActivity() {
|
||||||
|
private lateinit var cardUID: ByteArray
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
//TODO: validate PIN length == 6
|
||||||
|
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_pin)
|
||||||
|
val attempts = intent.getIntExtra(PIN_ACTIVITY_ATTEMPTS, -1)
|
||||||
|
cardUID = intent.getByteArrayExtra(PIN_ACTIVITY_CARD_UID)!!
|
||||||
|
|
||||||
|
val attemptLabel = findViewById<TextView>(R.id.attemptLabel)
|
||||||
|
|
||||||
|
if (attempts == -1) {
|
||||||
|
attemptLabel.text = ""
|
||||||
|
} else {
|
||||||
|
attemptLabel.text = getString(R.string.pin_attempts, attempts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ok(view: View) {
|
||||||
|
val pinText = findViewById<EditText>(R.id.pinText)
|
||||||
|
|
||||||
|
PINCache.putPIN(cardUID, pinText.text.toString())
|
||||||
|
setResult(Activity.RESULT_OK)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel(view: View) {
|
||||||
|
setResult(Activity.RESULT_CANCELED)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package im.status.keycard.connect
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.EditText
|
||||||
|
import im.status.keycard.connect.data.PINCache
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.core.app.ComponentActivity.ExtraData
|
||||||
|
import androidx.core.content.ContextCompat.getSystemService
|
||||||
|
|
||||||
|
const val PAIRING_ACTIVITY_PASSWORD = "pairingPassword"
|
||||||
|
|
||||||
|
class PairingActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_pairing)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ok(view: View) {
|
||||||
|
val intent = Intent()
|
||||||
|
intent.putExtra(PAIRING_ACTIVITY_PASSWORD, findViewById<EditText>(R.id.passwordText).text.toString())
|
||||||
|
setResult(Activity.RESULT_OK, intent)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel(view: View) {
|
||||||
|
setResult(Activity.RESULT_CANCELED)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
package im.status.keycard.connect.card
|
package im.status.keycard.connect.card
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import im.status.keycard.applet.KeycardCommandSet
|
import im.status.keycard.applet.KeycardCommandSet
|
||||||
|
import im.status.keycard.connect.*
|
||||||
import im.status.keycard.connect.data.PairingManager
|
import im.status.keycard.connect.data.PairingManager
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ class OpenSecureChannelCommand : CardCommand {
|
||||||
return CommandResult.OK
|
return CommandResult.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pair(cmdSet: KeycardCommandSet): CommandResult {
|
private fun pair(mainActivity: Activity, cmdSet: KeycardCommandSet): CommandResult {
|
||||||
if (pairingPassword != null) {
|
if (pairingPassword != null) {
|
||||||
try {
|
try {
|
||||||
//TODO: must distinguish real IOException from card exception (to fix in SDK)
|
//TODO: must distinguish real IOException from card exception (to fix in SDK)
|
||||||
|
@ -31,11 +33,13 @@ class OpenSecureChannelCommand : CardCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return promptPairingPassword()
|
return promptPairingPassword(mainActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun promptPairingPassword(): CommandResult {
|
private fun promptPairingPassword(mainActivity: Activity): CommandResult {
|
||||||
//TODO: start prompt activity
|
val intent = Intent(mainActivity, PairingActivity::class.java)
|
||||||
|
mainActivity.startActivityForResult(intent, REQ_INTERACTIVE_SCRIPT)
|
||||||
|
|
||||||
return CommandResult.UX_ONGOING
|
return CommandResult.UX_ONGOING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +57,10 @@ class OpenSecureChannelCommand : CardCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pair(cmdSet)
|
return pair(context.mainActivity, cmdSet)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDataReceived(data: Intent?) {
|
override fun onDataReceived(data: Intent?) {
|
||||||
pairingPassword = data?.getStringExtra("pairingPassword")
|
pairingPassword = data?.getStringExtra(PAIRING_ACTIVITY_PASSWORD)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,10 @@
|
||||||
package im.status.keycard.connect.card
|
package im.status.keycard.connect.card
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
|
import im.status.keycard.connect.PINActivity
|
||||||
|
import im.status.keycard.connect.PIN_ACTIVITY_ATTEMPTS
|
||||||
|
import im.status.keycard.connect.PIN_ACTIVITY_CARD_UID
|
||||||
import im.status.keycard.connect.data.PINCache
|
import im.status.keycard.connect.data.PINCache
|
||||||
import im.status.keycard.io.APDUException
|
import im.status.keycard.io.APDUException
|
||||||
import im.status.keycard.io.WrongPINException
|
import im.status.keycard.io.WrongPINException
|
||||||
|
@ -8,8 +13,14 @@ import java.io.IOException
|
||||||
class VerifyPINCommand : CardCommand {
|
class VerifyPINCommand : CardCommand {
|
||||||
private var retries = -1
|
private var retries = -1
|
||||||
|
|
||||||
private fun promptPIN(): CommandResult {
|
private fun promptPIN(mainActivity: Activity, instanceUID: ByteArray): CommandResult {
|
||||||
//TODO: start prompt activity
|
val intent = Intent(mainActivity, PINActivity::class.java).apply {
|
||||||
|
putExtra(PIN_ACTIVITY_ATTEMPTS, retries)
|
||||||
|
putExtra(PIN_ACTIVITY_CARD_UID, instanceUID)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mainActivity.startActivityForResult(intent, REQ_INTERACTIVE_SCRIPT)
|
||||||
return CommandResult.UX_ONGOING
|
return CommandResult.UX_ONGOING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +36,7 @@ class VerifyPINCommand : CardCommand {
|
||||||
cmdSet.verifyPIN(pin).checkAuthOK()
|
cmdSet.verifyPIN(pin).checkAuthOK()
|
||||||
return CommandResult.OK
|
return CommandResult.OK
|
||||||
} catch (e: WrongPINException) {
|
} catch (e: WrongPINException) {
|
||||||
|
PINCache.removePIN(cmdSet.applicationInfo.instanceUID)
|
||||||
retries = e.retryAttempts
|
retries = e.retryAttempts
|
||||||
} catch(e: IOException) {
|
} catch(e: IOException) {
|
||||||
return CommandResult.RETRY
|
return CommandResult.RETRY
|
||||||
|
@ -33,6 +45,6 @@ class VerifyPINCommand : CardCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return promptPIN()
|
return promptPIN(context.mainActivity, cmdSet.applicationInfo.instanceUID)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".PairingActivity">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/passwordPrompt"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="TextView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:layout_editor_absoluteY="91dp"
|
||||||
|
tools:text="@string/pairing_password_prompt" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/passwordText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:layout_editor_absoluteY="155dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="ok"
|
||||||
|
android:text="Button"
|
||||||
|
tools:layout_editor_absoluteX="278dp"
|
||||||
|
tools:layout_editor_absoluteY="274dp"
|
||||||
|
tools:text="@android:string/ok" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Button"
|
||||||
|
tools:layout_editor_absoluteX="44dp"
|
||||||
|
tools:layout_editor_absoluteY="273dp"
|
||||||
|
tools:text="@android:string/cancel" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".PINActivity">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/pinText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:inputType="numberPassword"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:layout_editor_absoluteY="296dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/cancelButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="cancel"
|
||||||
|
android:text="Button"
|
||||||
|
tools:layout_editor_absoluteX="39dp"
|
||||||
|
tools:layout_editor_absoluteY="625dp"
|
||||||
|
tools:text="@android:string/cancel" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/okButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:onClick="ok"
|
||||||
|
android:text="Button"
|
||||||
|
tools:layout_editor_absoluteX="287dp"
|
||||||
|
tools:layout_editor_absoluteY="625dp"
|
||||||
|
tools:text="@android:string/ok" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/pinPrompt"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="TextView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:layout_editor_absoluteY="222dp"
|
||||||
|
tools:text="@string/pin_prompt" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/attemptLabel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="TextView"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
tools:layout_editor_absoluteY="420dp"
|
||||||
|
tools:text="@string/pin_attempts" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -3,4 +3,7 @@
|
||||||
<string name="title_home">Home</string>
|
<string name="title_home">Home</string>
|
||||||
<string name="title_dashboard">Dashboard</string>
|
<string name="title_dashboard">Dashboard</string>
|
||||||
<string name="title_notifications">Notifications</string>
|
<string name="title_notifications">Notifications</string>
|
||||||
|
<string name="pin_prompt">Insert your PIN</string>
|
||||||
|
<string name="pin_attempts">%1$d retries left</string>
|
||||||
|
<string name="pairing_password_prompt" >Insert your pairing password</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue