From d03595f64e264aed21a53c5d238125ce09a90f02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 3 Jul 2015 04:58:30 +0200 Subject: New NFC dialog design with indeterminate progress, do NFC stuff in background --- OpenKeychain/src/main/AndroidManifest.xml | 1 + .../keychain/ui/CreateKeyActivity.java | 41 +++++---- .../keychain/ui/CreateYubiKeyImportFragment.java | 7 +- .../keychain/ui/ImportKeysActivity.java | 8 +- .../keychain/ui/NfcOperationActivity.java | 56 ++++++++++-- .../keychain/ui/ViewKeyActivity.java | 33 ++++--- .../keychain/ui/base/BaseNfcActivity.java | 100 ++++++++++++++------- .../src/main/res/drawable/yubikey_phone.png | Bin 352625 -> 347902 bytes OpenKeychain/src/main/res/layout/nfc_activity.xml | 95 +++++++++++++++----- OpenKeychain/src/main/res/values/strings.xml | 4 +- OpenKeychain/src/main/res/values/themes.xml | 5 ++ 11 files changed, 254 insertions(+), 96 deletions(-) diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index b3754d360..699619e5c 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -692,6 +692,7 @@ --> diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java index 1db93d2c0..c6f02edb3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -63,6 +63,11 @@ public class CreateKeyActivity extends BaseNfcActivity { Fragment mCurrentFragment; + + byte[] mScannedFingerprints; + byte[] mNfcAid; + String mNfcUserId; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -72,8 +77,6 @@ public class CreateKeyActivity extends BaseNfcActivity { if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { try { handleTagDiscoveredIntent(getIntent()); - } catch (CardException e) { - handleNfcError(e); } catch (IOException e) { handleNfcError(e); } @@ -142,40 +145,47 @@ public class CreateKeyActivity extends BaseNfcActivity { } @Override - protected void onNfcPerform() throws IOException { + protected void doNfcInBackground() throws IOException { if (mCurrentFragment instanceof NfcListenerFragment) { - ((NfcListenerFragment) mCurrentFragment).onNfcPerform(); + ((NfcListenerFragment) mCurrentFragment).doNfcInBackground(); return; } - byte[] scannedFingerprints = nfcGetFingerprints(); - byte[] nfcAid = nfcGetAid(); - String userId = nfcGetUserId(); + mScannedFingerprints = nfcGetFingerprints(); + mNfcAid = nfcGetAid(); + mNfcUserId = nfcGetUserId(); + } + + @Override + protected void onNfcPostExecute() throws IOException { + if (mCurrentFragment instanceof NfcListenerFragment) { + ((NfcListenerFragment) mCurrentFragment).onNfcPostExecute(); + return; + } - if (containsKeys(scannedFingerprints)) { + if (containsKeys(mScannedFingerprints)) { try { - long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(scannedFingerprints); + long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mScannedFingerprints); CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId); ring.getMasterKeyId(); Intent intent = new Intent(this, ViewKeyActivity.class); intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, userId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, scannedFingerprints); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mScannedFingerprints); startActivity(intent); finish(); } catch (PgpKeyNotFoundException e) { Fragment frag = CreateYubiKeyImportFragment.newInstance( - scannedFingerprints, nfcAid, userId); + mScannedFingerprints, mNfcAid, mNfcUserId); loadFragment(frag, FragAction.TO_RIGHT); } } else { Fragment frag = CreateYubiKeyBlankFragment.newInstance(); loadFragment(frag, FragAction.TO_RIGHT); } - } private boolean containsKeys(byte[] scannedFingerprints) { @@ -246,7 +256,8 @@ public class CreateKeyActivity extends BaseNfcActivity { } interface NfcListenerFragment { - public void onNfcPerform() throws IOException; + public void doNfcInBackground() throws IOException; + public void onNfcPostExecute() throws IOException; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java index 9897fa237..c64f05687 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateYubiKeyImportFragment.java @@ -195,7 +195,7 @@ public class CreateYubiKeyImportFragment } @Override - public void onNfcPerform() throws IOException { + public void doNfcInBackground() throws IOException { mNfcFingerprints = mCreateKeyActivity.nfcGetFingerprints(); mNfcAid = mCreateKeyActivity.nfcGetAid(); @@ -204,10 +204,13 @@ public class CreateYubiKeyImportFragment byte[] fp = new byte[20]; ByteBuffer.wrap(fp).put(mNfcFingerprints, 0, 20); mNfcFingerprint = KeyFormattingUtils.convertFingerprintToHex(fp); + } + + @Override + public void onNfcPostExecute() throws IOException { setData(); refreshSearch(); - } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index ba4f759e6..bc83b05b0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -448,10 +448,8 @@ public class ImportKeysActivity extends BaseNfcActivity } @Override - protected void onNfcPerform() throws IOException { - // this displays the key or moves to the yubikey import dialogue. - super.onNfcPerform(); - // either way, finish afterwards + protected void onNfcPostExecute() throws IOException { + // either way, finish after NFC AsyncTask finish(); } @@ -463,7 +461,7 @@ public class ImportKeysActivity extends BaseNfcActivity } } - public void handleResult (ImportKeyResult result) { + public void handleResult(ImportKeyResult result) { if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction()) || ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) { Intent intent = new Intent(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java index 8a455bcec..f17d384a8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -7,8 +7,10 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; +import android.os.AsyncTask; import android.os.Bundle; import android.view.WindowManager; +import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -44,11 +46,15 @@ public class NfcOperationActivity extends BaseNfcActivity { public static final String RESULT_DATA = "result_data"; + public ViewAnimator vAnimator; + private RequiredInputParcel mRequiredInput; private Intent mServiceIntent; private static final byte[] BLANK_FINGERPRINT = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + private CryptoInputParcel mInputParcel; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -56,6 +62,11 @@ public class NfcOperationActivity extends BaseNfcActivity { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + setTitle(R.string.nfc_text); + + vAnimator = (ViewAnimator) findViewById(R.id.view_animator); + vAnimator.setDisplayedChild(0); + Intent intent = getIntent(); Bundle data = intent.getExtras(); @@ -74,16 +85,22 @@ public class NfcOperationActivity extends BaseNfcActivity { } @Override - protected void onNfcPerform() throws IOException { + public void onNfcPreExecute() { + // start with indeterminate progress + vAnimator.setDisplayedChild(1); + } + + @Override + protected void doNfcInBackground() throws IOException { - CryptoInputParcel inputParcel = new CryptoInputParcel(mRequiredInput.mSignatureTime); + mInputParcel = new CryptoInputParcel(mRequiredInput.mSignatureTime); switch (mRequiredInput.mType) { case NFC_DECRYPT: { for (int i = 0; i < mRequiredInput.mInputData.length; i++) { byte[] encryptedSessionKey = mRequiredInput.mInputData[i]; byte[] decryptedSessionKey = nfcDecryptSessionKey(encryptedSessionKey); - inputParcel.addCryptoData(encryptedSessionKey, decryptedSessionKey); + mInputParcel.addCryptoData(encryptedSessionKey, decryptedSessionKey); } break; } @@ -92,7 +109,7 @@ public class NfcOperationActivity extends BaseNfcActivity { byte[] hash = mRequiredInput.mInputData[i]; int algo = mRequiredInput.mSignAlgos[i]; byte[] signedHash = nfcCalculateSignature(hash, algo); - inputParcel.addCryptoData(hash, signedHash); + mInputParcel.addCryptoData(hash, signedHash); } break; } @@ -163,7 +180,7 @@ public class NfcOperationActivity extends BaseNfcActivity { } // TODO: Is this really needed? - inputParcel.addCryptoData(subkeyBytes, cardSerialNumber); + mInputParcel.addCryptoData(subkeyBytes, cardSerialNumber); } // change PINs afterwards @@ -177,16 +194,39 @@ public class NfcOperationActivity extends BaseNfcActivity { } } + } + + @Override + protected void onNfcPostExecute() throws IOException { if (mServiceIntent != null) { - CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, inputParcel); + CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, mInputParcel); setResult(RESULT_OK, mServiceIntent); } else { Intent result = new Intent(); - result.putExtra(NfcOperationActivity.RESULT_DATA, inputParcel); + result.putExtra(NfcOperationActivity.RESULT_DATA, mInputParcel); setResult(RESULT_OK, result); } - finish(); + // show finish + vAnimator.setDisplayedChild(2); + + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + // wait some 1000ms here, give the user time to appreciate the displayed finish + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // never mind + } + return null; + } + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + finish(); + } + }.execute(); } private boolean shouldPutKey(byte[] fingerprint, int idx) throws IOException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index d3849c892..27832b665 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -148,6 +148,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements private String mFingerprint; private long mMasterKeyId; + private byte[] mNfcFingerprints; + private String mNfcUserId; + private byte[] mNfcAid; + @SuppressLint("InflateParams") @Override protected void onCreate(Bundle savedInstanceState) { @@ -540,13 +544,17 @@ public class ViewKeyActivity extends BaseNfcActivity implements } @Override - protected void onNfcPerform() throws IOException { + protected void doNfcInBackground() throws IOException { - final byte[] nfcFingerprints = nfcGetFingerprints(); - final String nfcUserId = nfcGetUserId(); - final byte[] nfcAid = nfcGetAid(); + mNfcFingerprints = nfcGetFingerprints(); + mNfcUserId = nfcGetUserId(); + mNfcAid = nfcGetAid(); + } - long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); + @Override + protected void onNfcPostExecute() throws IOException { + + long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints); try { @@ -557,7 +565,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements // if the master key of that key matches this one, just show the yubikey dialog if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprint)) { - showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); + showYubiKeyFragment(mNfcFingerprints, mNfcUserId, mNfcAid); return; } @@ -570,9 +578,9 @@ public class ViewKeyActivity extends BaseNfcActivity implements Intent intent = new Intent( ViewKeyActivity.this, ViewKeyActivity.class); intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints); startActivity(intent); finish(); } @@ -586,15 +594,14 @@ public class ViewKeyActivity extends BaseNfcActivity implements public void onAction() { Intent intent = new Intent( ViewKeyActivity.this, CreateKeyActivity.class); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints); startActivity(intent); finish(); } }, R.string.snack_yubikey_import).show(); } - } public void showYubiKeyFragment( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index ba8dd3b55..5cbd202d3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -30,6 +30,7 @@ import android.content.IntentFilter; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.nfc.tech.IsoDep; +import android.os.AsyncTask; import android.os.Bundle; import android.widget.Toast; @@ -74,6 +75,10 @@ public abstract class BaseNfcActivity extends BaseActivity { private static final int TIMEOUT = 100000; + private byte[] mNfcFingerprints; + private String mNfcUserId; + private byte[] mNfcAid; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -91,28 +96,59 @@ public abstract class BaseNfcActivity extends BaseActivity { * All new NFC Intents which are delivered to this activity are handled here */ @Override - public void onNewIntent(Intent intent) { + public void onNewIntent(final Intent intent) { if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { - try { - handleTagDiscoveredIntent(intent); - } catch (CardException e) { - handleNfcError(e); - } catch (IOException e) { - handleNfcError(e); - } - } - } - public void handleNfcError(IOException e) { + // Actual NFC operations are executed in doInBackground to not block the UI thread + new AsyncTask() { + @Override + protected void onPreExecute() { + super.onPreExecute(); + onNfcPreExecute(); + } + + @Override + protected Exception doInBackground(Void... params) { + try { + handleTagDiscoveredIntent(intent); + } catch (CardException e) { + return e; + } catch (IOException e) { + return e; + } + + return null; + } - Log.e(Constants.TAG, "nfc error", e); - Notify.create(this, getString(R.string.error_nfc, e.getMessage()), Style.WARN).show(); + @Override + protected void onPostExecute(Exception exception) { + super.onPostExecute(exception); + + if (exception != null) { + handleNfcError(exception); + return; + } + + try { + onNfcPostExecute(); + } catch (IOException e) { + handleNfcError(e); + } + } + }.execute(); + + } } - public void handleNfcError(CardException e) { - Log.e(Constants.TAG, "card error", e); + public void handleNfcError(Exception e) { + Log.e(Constants.TAG, "nfc error", e); - short status = e.getResponseCode(); + short status; + if (e instanceof CardException) { + status = ((CardException) e).getResponseCode(); + } else { + status = -1; + } // When entering a PIN, a status of 63CX indicates X attempts remaining. if ((status & (short)0xFFF0) == 0x63C0) { Notify.create(this, getString(R.string.error_pin, status & 0x000F), Style.WARN).show(); @@ -313,20 +349,25 @@ public abstract class BaseNfcActivity extends BaseActivity { mPw1ValidatedForDecrypt = false; mPw3Validated = false; - onNfcPerform(); + doNfcInBackground(); mIsoDep.close(); mIsoDep = null; } - protected void onNfcPerform() throws IOException { + protected void onNfcPreExecute() { + } - final byte[] nfcFingerprints = nfcGetFingerprints(); - final String nfcUserId = nfcGetUserId(); - final byte[] nfcAid = nfcGetAid(); + protected void doNfcInBackground() throws IOException { + mNfcFingerprints = nfcGetFingerprints(); + mNfcUserId = nfcGetUserId(); + mNfcAid = nfcGetAid(); + } - final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); + protected void onNfcPostExecute() throws IOException { + + final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mNfcFingerprints); try { CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing( @@ -335,18 +376,17 @@ public abstract class BaseNfcActivity extends BaseActivity { Intent intent = new Intent(this, ViewKeyActivity.class); intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints); startActivity(intent); } catch (PgpKeyNotFoundException e) { Intent intent = new Intent(this, CreateKeyActivity.class); - intent.putExtra(CreateKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(CreateKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(CreateKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + intent.putExtra(CreateKeyActivity.EXTRA_NFC_AID, mNfcAid); + intent.putExtra(CreateKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); + intent.putExtra(CreateKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints); startActivity(intent); } - } /** Return the key id from application specific data stored on tag, or null @@ -608,7 +648,7 @@ public abstract class BaseNfcActivity extends BaseActivity { * conformance to the card's requirements for key length. * * @param pw For PW1, this is 0x81. For PW3 (Admin PIN), mode is 0x83. - * @param newPinString The new PW1 or PW3. + * @param newPin The new PW1 or PW3. */ public void nfcModifyPIN(int pw, byte[] newPin) throws IOException { final int MAX_PW1_LENGTH_INDEX = 1; diff --git a/OpenKeychain/src/main/res/drawable/yubikey_phone.png b/OpenKeychain/src/main/res/drawable/yubikey_phone.png index 6a03501c6..3cdfbba0f 100644 Binary files a/OpenKeychain/src/main/res/drawable/yubikey_phone.png and b/OpenKeychain/src/main/res/drawable/yubikey_phone.png differ diff --git a/OpenKeychain/src/main/res/layout/nfc_activity.xml b/OpenKeychain/src/main/res/layout/nfc_activity.xml index 9acd0676c..724b4606c 100644 --- a/OpenKeychain/src/main/res/layout/nfc_activity.xml +++ b/OpenKeychain/src/main/res/layout/nfc_activity.xml @@ -1,35 +1,86 @@ - + android:layout_height="wrap_content"> - - - + custom:initialView="0"> + + - + + + + + + android:orientation="vertical"> + + - + + + + + android:layout_below="@id/toolbar_include" + android:layout_gravity="center" + android:gravity="center" + android:orientation="vertical"> + + + + + + + - - \ No newline at end of file + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 18f642e3a..e76cb5b36 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -225,7 +225,9 @@ "Enter password for '%s'" "Enter PIN for '%s'" "Enter PIN to access YubiKey for '%s'" - "Hold YubiKey against the back of your device." + "Hold YubiKey against the NFC marker at the back of your device." + "Don't release the YubiKey yet!" + "Success" "Delete original files?" "The following files will be deleted:%s" "%1$d out of %2$d files have been deleted.%3$s" diff --git a/OpenKeychain/src/main/res/values/themes.xml b/OpenKeychain/src/main/res/values/themes.xml index 6ac09c5d7..1760ca5ec 100644 --- a/OpenKeychain/src/main/res/values/themes.xml +++ b/OpenKeychain/src/main/res/values/themes.xml @@ -35,4 +35,9 @@ + + \ No newline at end of file -- cgit v1.2.3