Better YubiKey UX flow for error handling
This commit is contained in:
@@ -9,7 +9,9 @@ package org.sufficientlysecure.keychain.ui;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.ViewAnimator;
|
import android.widget.ViewAnimator;
|
||||||
|
|
||||||
@@ -49,6 +51,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
|
|||||||
|
|
||||||
public ViewAnimator vAnimator;
|
public ViewAnimator vAnimator;
|
||||||
public TextView vErrorText;
|
public TextView vErrorText;
|
||||||
|
public Button vErrorTryAgainButton;
|
||||||
|
|
||||||
private RequiredInputParcel mRequiredInput;
|
private RequiredInputParcel mRequiredInput;
|
||||||
private Intent mServiceIntent;
|
private Intent mServiceIntent;
|
||||||
@@ -68,7 +71,20 @@ public class NfcOperationActivity extends BaseNfcActivity {
|
|||||||
|
|
||||||
vAnimator = (ViewAnimator) findViewById(R.id.view_animator);
|
vAnimator = (ViewAnimator) findViewById(R.id.view_animator);
|
||||||
vAnimator.setDisplayedChild(0);
|
vAnimator.setDisplayedChild(0);
|
||||||
vErrorText = (TextView) findViewById(R.id.nfc_activity_error_text);
|
vErrorText = (TextView) findViewById(R.id.nfc_activity_3_error_text);
|
||||||
|
vErrorTryAgainButton = (Button) findViewById(R.id.nfc_activity_3_error_try_again);
|
||||||
|
vErrorTryAgainButton.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
resumeTagHandling();
|
||||||
|
|
||||||
|
// obtain passphrase for this subkey
|
||||||
|
if (mRequiredInput.mType != RequiredInputParcel.RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
|
||||||
|
obtainYubiKeyPin(mRequiredInput);
|
||||||
|
}
|
||||||
|
vAnimator.setDisplayedChild(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
Bundle data = intent.getExtras();
|
Bundle data = intent.getExtras();
|
||||||
@@ -84,7 +100,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initLayout() {
|
protected void initLayout() {
|
||||||
setContentView(R.layout.nfc_activity);
|
setContentView(R.layout.nfc_operation_activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -221,8 +237,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
|
|||||||
if (isNfcConnected()) {
|
if (isNfcConnected()) {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException ignored) {
|
||||||
// never mind
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@@ -239,7 +254,9 @@ public class NfcOperationActivity extends BaseNfcActivity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onNfcError(String error) {
|
protected void onNfcError(String error) {
|
||||||
vErrorText.setText(error);
|
pauseTagHandling();
|
||||||
|
|
||||||
|
vErrorText.setText(error + "\n\n" + getString(R.string.nfc_try_again_text));
|
||||||
vAnimator.setDisplayedChild(3);
|
vAnimator.setDisplayedChild(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,8 +287,6 @@ public class NfcOperationActivity extends BaseNfcActivity {
|
|||||||
// clear (invalid) passphrase
|
// clear (invalid) passphrase
|
||||||
PassphraseCacheService.clearCachedPassphrase(
|
PassphraseCacheService.clearCachedPassphrase(
|
||||||
this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
|
this, mRequiredInput.getMasterKeyId(), mRequiredInput.getSubKeyId());
|
||||||
|
|
||||||
obtainYubiKeyPin(mRequiredInput);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.nfc.NfcAdapter;
|
import android.nfc.NfcAdapter;
|
||||||
import android.nfc.Tag;
|
import android.nfc.Tag;
|
||||||
|
import android.nfc.TagLostException;
|
||||||
import android.nfc.tech.IsoDep;
|
import android.nfc.tech.IsoDep;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -72,6 +73,7 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
protected boolean mPw3Validated;
|
protected boolean mPw3Validated;
|
||||||
private NfcAdapter mNfcAdapter;
|
private NfcAdapter mNfcAdapter;
|
||||||
private IsoDep mIsoDep;
|
private IsoDep mIsoDep;
|
||||||
|
private boolean mTagHandlingEnabled;
|
||||||
|
|
||||||
private static final int TIMEOUT = 100000;
|
private static final int TIMEOUT = 100000;
|
||||||
|
|
||||||
@@ -168,10 +170,20 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void pauseTagHandling() {
|
||||||
|
mTagHandlingEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void resumeTagHandling() {
|
||||||
|
mTagHandlingEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
mTagHandlingEnabled = true;
|
||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
|
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) {
|
||||||
@@ -186,7 +198,8 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onNewIntent(final Intent intent) {
|
public void onNewIntent(final Intent intent) {
|
||||||
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
|
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())
|
||||||
|
&& mTagHandlingEnabled) {
|
||||||
handleIntentInBackground(intent);
|
handleIntentInBackground(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,6 +207,11 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
private void handleNfcError(Exception e) {
|
private void handleNfcError(Exception e) {
|
||||||
Log.e(Constants.TAG, "nfc error", e);
|
Log.e(Constants.TAG, "nfc error", e);
|
||||||
|
|
||||||
|
if (e instanceof TagLostException) {
|
||||||
|
onNfcError(getString(R.string.error_nfc_tag_lost));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
short status;
|
short status;
|
||||||
if (e instanceof CardException) {
|
if (e instanceof CardException) {
|
||||||
status = ((CardException) e).getResponseCode();
|
status = ((CardException) e).getResponseCode();
|
||||||
@@ -202,7 +220,8 @@ public abstract class BaseNfcActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
// When entering a PIN, a status of 63CX indicates X attempts remaining.
|
// When entering a PIN, a status of 63CX indicates X attempts remaining.
|
||||||
if ((status & (short)0xFFF0) == 0x63C0) {
|
if ((status & (short)0xFFF0) == 0x63C0) {
|
||||||
onNfcError(getString(R.string.error_pin, status & 0x000F));
|
int tries = status & 0x000F;
|
||||||
|
onNfcError(getResources().getQuantityString(R.plurals.error_pin, tries, tries));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,10 +14,6 @@
|
|||||||
android:measureAllChildren="false"
|
android:measureAllChildren="false"
|
||||||
android:minHeight="?listPreferredItemHeightSmall"
|
android:minHeight="?listPreferredItemHeightSmall"
|
||||||
android:outAnimation="@anim/fade_out"
|
android:outAnimation="@anim/fade_out"
|
||||||
android:paddingBottom="16dp"
|
|
||||||
android:paddingLeft="24dp"
|
|
||||||
android:paddingRight="24dp"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
custom:initialView="3">
|
custom:initialView="3">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
@@ -25,20 +21,23 @@
|
|||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/nfc_activity_text"
|
android:id="@+id/nfc_activity_0_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="24dp"
|
||||||
|
android:layout_marginRight="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
android:text="@string/nfc_text"
|
android:text="@string/nfc_text"
|
||||||
android:textAppearance="@android:style/TextAppearance.Medium" />
|
android:textAppearance="@android:style/TextAppearance.Medium" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/nfc_image"
|
android:id="@+id/nfc_activity_0_image"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/nfc_activity_text"
|
android:layout_below="@+id/nfc_activity_0_text"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_margin="24dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:src="@drawable/yubikey_phone" />
|
android:src="@drawable/yubikey_phone" />
|
||||||
@@ -49,9 +48,12 @@
|
|||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/nfc_activity_text2"
|
android:id="@+id/nfc_activity_1_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="24dp"
|
||||||
|
android:layout_marginRight="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
android:text="@string/nfc_wait"
|
android:text="@string/nfc_wait"
|
||||||
android:textAppearance="@android:style/TextAppearance.Medium" />
|
android:textAppearance="@android:style/TextAppearance.Medium" />
|
||||||
|
|
||||||
@@ -65,13 +67,13 @@
|
|||||||
|
|
||||||
<!-- placeholder to retain dialog size -->
|
<!-- placeholder to retain dialog size -->
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/nfc_image_placeholder1"
|
android:id="@+id/nfc_activity_1_placeholder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/nfc_activity_text2"
|
android:layout_below="@+id/nfc_activity_1_text"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_margin="24dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:src="@drawable/yubikey_phone"
|
android:src="@drawable/yubikey_phone"
|
||||||
@@ -84,21 +86,24 @@
|
|||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/nfc_activity_text3"
|
android:id="@+id/nfc_activity_2_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="24dp"
|
||||||
|
android:layout_marginRight="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
android:text="@string/nfc_finished"
|
android:text="@string/nfc_finished"
|
||||||
android:textAppearance="@android:style/TextAppearance.Medium" />
|
android:textAppearance="@android:style/TextAppearance.Medium" />
|
||||||
|
|
||||||
<!-- placeholder to retain dialog size -->
|
<!-- placeholder to retain dialog size -->
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/nfc_image_placeholder2"
|
android:id="@+id/nfc_activity_2_placeholder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/nfc_activity_text3"
|
android:layout_below="@+id/nfc_activity_2_text"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_margin="24dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:src="@drawable/yubikey_phone"
|
android:src="@drawable/yubikey_phone"
|
||||||
@@ -120,36 +125,52 @@
|
|||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/nfc_activity_text_placeholder"
|
android:id="@+id/nfc_activity_3_text_placeholder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignEnd="@+id/nfc_activity_3_placeholder"
|
||||||
|
android:layout_alignRight="@+id/nfc_activity_3_placeholder"
|
||||||
|
android:layout_marginLeft="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
android:text=""
|
android:text=""
|
||||||
android:textAppearance="@android:style/TextAppearance.Medium" />
|
android:textAppearance="@android:style/TextAppearance.Medium" />
|
||||||
|
|
||||||
<!-- placeholder to retain dialog size -->
|
<!-- placeholder to retain dialog size -->
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/nfc_image_placeholder3"
|
android:id="@+id/nfc_activity_3_placeholder"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/nfc_activity_text_placeholder"
|
android:layout_below="@+id/nfc_activity_3_text_placeholder"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_margin="24dp"
|
||||||
android:adjustViewBounds="true"
|
android:adjustViewBounds="true"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:src="@drawable/yubikey_phone"
|
android:src="@drawable/yubikey_phone"
|
||||||
android:visibility="invisible" />
|
android:visibility="invisible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/nfc_activity_error_text"
|
android:id="@+id/nfc_activity_3_error_text"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_centerHorizontal="true"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_margin="24dp"
|
||||||
android:textAppearance="@android:style/TextAppearance.Medium"
|
android:textAppearance="@android:style/TextAppearance.Medium"
|
||||||
android:textColor="@color/android_red_dark"
|
android:textColor="@color/android_red_dark"
|
||||||
tools:text="Error text" />
|
tools:text="Error text" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/nfc_activity_3_error_try_again"
|
||||||
|
style="?android:attr/borderlessButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_below="@+id/nfc_activity_3_placeholder"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:text="@string/error_nfc_try_again"
|
||||||
|
android:textColor="@color/accent" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
@@ -263,7 +263,8 @@
|
|||||||
<string name="yubikey_pin_for">"Enter PIN to access YubiKey for '%s'"</string>
|
<string name="yubikey_pin_for">"Enter PIN to access YubiKey for '%s'"</string>
|
||||||
<string name="nfc_text">"Hold YubiKey against the NFC marker at the back of your device."</string>
|
<string name="nfc_text">"Hold YubiKey against the NFC marker at the back of your device."</string>
|
||||||
<string name="nfc_wait">"Keep the YubiKey at the back!"</string>
|
<string name="nfc_wait">"Keep the YubiKey at the back!"</string>
|
||||||
<string name="nfc_finished">"Please take away the YubiKey now."</string>
|
<string name="nfc_finished">"Take away the YubiKey now."</string>
|
||||||
|
<string name="nfc_try_again_text">"Take away the YubiKey now and press TRY AGAIN."</string>
|
||||||
<string name="file_delete_confirmation_title">"Delete original files?"</string>
|
<string name="file_delete_confirmation_title">"Delete original files?"</string>
|
||||||
<string name="file_delete_confirmation">"The following files will be deleted:%s"</string>
|
<string name="file_delete_confirmation">"The following files will be deleted:%s"</string>
|
||||||
<string name="file_delete_successful">"%1$d out of %2$d files have been deleted.%3$s"</string>
|
<string name="file_delete_successful">"%1$d out of %2$d files have been deleted.%3$s"</string>
|
||||||
@@ -1388,17 +1389,22 @@
|
|||||||
<string name="btn_import">"Import"</string>
|
<string name="btn_import">"Import"</string>
|
||||||
<string name="snack_yubi_other">Different key stored on YubiKey!</string>
|
<string name="snack_yubi_other">Different key stored on YubiKey!</string>
|
||||||
<string name="error_nfc">"NFC Error: %s"</string>
|
<string name="error_nfc">"NFC Error: %s"</string>
|
||||||
<string name="error_pin">"NFC: Incorrect PIN; %d tries remaining."</string>
|
<plurals name="error_pin">
|
||||||
<string name="error_nfc_terminated">"NFC: Smart card in termination state"</string>
|
<item quantity="one">"Incorrect PIN!\n%d try remaining."</item>
|
||||||
<string name="error_nfc_wrong_length">"NFC: Wrong length for sent / received data"</string>
|
<item quantity="other">"Incorrect PIN!\n%d tries remaining."</item>
|
||||||
<string name="error_nfc_conditions_not_satisfied">"NFC: Conditions of use not satisfied"</string>
|
</plurals>
|
||||||
<string name="error_nfc_security_not_satisfied">"NFC: Security status not satisfied"</string>
|
<string name="error_nfc_terminated">"YubiKey in termination state"</string>
|
||||||
<string name="error_nfc_authentication_blocked">"NFC: PIN blocked after too many attempts"</string>
|
<string name="error_nfc_wrong_length">"Wrong length for sent / received data"</string>
|
||||||
<string name="error_nfc_data_not_found">"NFC: Key or object not found"</string>
|
<string name="error_nfc_conditions_not_satisfied">"Conditions of use not satisfied"</string>
|
||||||
<string name="error_nfc_unknown">"NFC: Unknown Error"</string>
|
<string name="error_nfc_security_not_satisfied">"Security status not satisfied"</string>
|
||||||
<string name="error_nfc_bad_data">"NFC: Card reported invalid data"</string>
|
<string name="error_nfc_authentication_blocked">"PIN blocked after too many attempts"</string>
|
||||||
<string name="error_nfc_chaining_error">"NFC: Card expected last command in a chain"</string>
|
<string name="error_nfc_data_not_found">"Key or object not found"</string>
|
||||||
<string name="error_nfc_header">"NFC: Card reported invalid %s byte"</string>
|
<string name="error_nfc_unknown">"Unknown Error"</string>
|
||||||
|
<string name="error_nfc_bad_data">"YubiKey reported invalid data"</string>
|
||||||
|
<string name="error_nfc_chaining_error">"YubiKey expected last command in a chain"</string>
|
||||||
|
<string name="error_nfc_header">"YubiKey reported invalid %s byte"</string>
|
||||||
|
<string name="error_nfc_tag_lost">"YubiKey has been taken off too early. Keep the YubiKey at the back until the operation finishes."</string>
|
||||||
|
<string name="error_nfc_try_again">"Try again"</string>
|
||||||
<string name="error_pin_nodefault">Default PIN was rejected!</string>
|
<string name="error_pin_nodefault">Default PIN was rejected!</string>
|
||||||
<string name="error_temp_file">Error creating temporary file.</string>
|
<string name="error_temp_file">Error creating temporary file.</string>
|
||||||
<string name="btn_delete_original">Delete original file</string>
|
<string name="btn_delete_original">Delete original file</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user