diff options
Diffstat (limited to 'OpenKeychain')
13 files changed, 378 insertions, 206 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index c3320ed6a..964c535dd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -120,10 +120,12 @@ public class PgpImportExport { // If there aren't even any keys, do nothing here. if (entries == null || !entries.hasNext()) { return new ImportKeyResult( - ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0, 0); + ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0, 0, + new long[]{}); } int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0; + ArrayList<Long> importedMasterKeyIds = new ArrayList<Long>(); int position = 0; double progSteps = 100.0 / num; @@ -165,11 +167,13 @@ public class PgpImportExport { badKeys += 1; } else if (result.updated()) { oldKeys += 1; + importedMasterKeyIds.add(key.getMasterKeyId()); } else { newKeys += 1; if (key.isSecret()) { secret += 1; } + importedMasterKeyIds.add(key.getMasterKeyId()); } } catch (IOException e) { @@ -210,8 +214,14 @@ public class PgpImportExport { resultType |= ImportKeyResult.RESULT_CANCELLED; } - return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret); + // convert to long array + long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()]; + for (int i = 0; i < importedMasterKeyIds.size(); ++i) { + importedMasterKeyIdsArray[i] = importedMasterKeyIds.get(i); + } + return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret, + importedMasterKeyIdsArray); } public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds, diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index f5e6dedc2..485009f0d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -465,7 +465,7 @@ public class KeychainIntentService extends IntentService implements Progressable // actual import afterwards keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex())); } - } catch (Exception e) { + } catch (Keyserver.QueryFailedException e) { sendErrorToHandler(e); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/GetKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/GetKeyResult.java new file mode 100644 index 000000000..e76d1bd41 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/GetKeyResult.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.service.results; + +import android.os.Parcel; + +public class GetKeyResult extends OperationResult { + + public int mNonPgpPartsCount; + + public int getNonPgpPartsCount() { + return mNonPgpPartsCount; + } + + public void setNonPgpPartsCount(int nonPgpPartsCount) { + mNonPgpPartsCount = nonPgpPartsCount; + } + + public GetKeyResult(int result, OperationLog log) { + super(result, log); + } + + public static final int RESULT_ERROR_NO_VALID_KEYS = RESULT_ERROR + 8; + public static final int RESULT_ERROR_NO_PGP_PARTS = RESULT_ERROR + 16; + public static final int RESULT_ERROR_QUERY_TOO_SHORT = RESULT_ERROR + 32; + public static final int RESULT_ERROR_TOO_MANY_RESPONSES = RESULT_ERROR + 64; + public static final int RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES = RESULT_ERROR + 128; + public static final int RESULT_ERROR_QUERY_FAILED = RESULT_ERROR + 256; + + public GetKeyResult(Parcel source) { + super(source); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + } + + public static Creator<GetKeyResult> CREATOR = new Creator<GetKeyResult>() { + public GetKeyResult createFromParcel(final Parcel source) { + return new GetKeyResult(source); + } + + public GetKeyResult[] newArray(final int size) { + return new GetKeyResult[size]; + } + }; +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java index fa0dc6af3..0d71af876 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/ImportKeyResult.java @@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.ui.LogDisplayFragment; public class ImportKeyResult extends OperationResult { public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret; + public final long[] mImportedMasterKeyIds; // At least one new key public static final int RESULT_OK_NEWKEYS = 8; @@ -69,21 +70,28 @@ public class ImportKeyResult extends OperationResult { return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING; } + public long[] getImportedMasterKeyIds() { + return mImportedMasterKeyIds; + } + public ImportKeyResult(Parcel source) { super(source); mNewKeys = source.readInt(); mUpdatedKeys = source.readInt(); mBadKeys = source.readInt(); mSecret = source.readInt(); + mImportedMasterKeyIds = source.createLongArray(); } public ImportKeyResult(int result, OperationLog log, - int newKeys, int updatedKeys, int badKeys, int secret) { + int newKeys, int updatedKeys, int badKeys, int secret, + long[] importedMasterKeyIds) { super(result, log); mNewKeys = newKeys; mUpdatedKeys = updatedKeys; mBadKeys = badKeys; mSecret = secret; + mImportedMasterKeyIds = importedMasterKeyIds; } @Override @@ -93,6 +101,7 @@ public class ImportKeyResult extends OperationResult { dest.writeInt(mUpdatedKeys); dest.writeInt(mBadKeys); dest.writeInt(mSecret); + dest.writeLongArray(mImportedMasterKeyIds); } public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() { @@ -162,8 +171,8 @@ public class ImportKeyResult extends OperationResult { color = Style.RED; if (isFailNothing()) { str = activity.getString((resultType & ImportKeyResult.RESULT_CANCELLED) > 0 - ? R.string.import_error_nothing_cancelled - : R.string.import_error_nothing); + ? R.string.import_error_nothing_cancelled + : R.string.import_error_nothing); } else { str = activity.getResources().getQuantityString(R.plurals.import_error, mBadKeys); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java index 234b13ac2..d32e3a4a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/results/OperationResult.java @@ -537,8 +537,16 @@ public abstract class OperationResult implements Parcelable { MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg), - MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data) - + MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data), + + // get key + MSG_GET_SUCCESS(LogLevel.OK, R.string.msg_download_success), + MSG_GET_NO_VALID_KEYS(LogLevel.ERROR, R.string.msg_download_no_valid_keys), + MSG_GET_NO_PGP_PARTS(LogLevel.ERROR, R.string.msg_download_no_pgp_parts), + MSG_GET_QUERY_TOO_SHORT(LogLevel.ERROR, R.string.msg_download_query_too_short), + MSG_GET_TOO_MANY_RESPONSES(LogLevel.ERROR, R.string.msg_download_too_many_responses), + MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES(LogLevel.ERROR, R.string.msg_download_query_too_short_or_too_many_responses), + MSG_GET_QUERY_FAILED(LogLevel.ERROR, R.string.msg_download_query_failed) ; public final int mMsgId; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java index ecc72c24d..479a874d9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/AddKeysActivity.java @@ -31,32 +31,36 @@ import android.support.v7.app.ActionBarActivity; import android.view.View; import android.widget.ImageView; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; -import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.results.GetKeyResult; import org.sufficientlysecure.keychain.service.results.ImportKeyResult; import org.sufficientlysecure.keychain.service.results.OperationResult; import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper; -import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListCloudLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner; import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; +import org.sufficientlysecure.keychain.util.Preferences; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; +import java.util.Locale; import edu.cmu.cylab.starslinger.exchange.ExchangeActivity; import edu.cmu.cylab.starslinger.exchange.ExchangeConfig; @@ -68,6 +72,7 @@ public class AddKeysActivity extends ActionBarActivity implements View mActionSafeSlinger; ImageView mActionSafeSlingerIcon; View mActionQrCode; + View mActionNfc; View mActionSearchCloud; ProviderHelper mProviderHelper; @@ -75,14 +80,10 @@ public class AddKeysActivity extends ActionBarActivity implements long mExchangeMasterKeyId = Constants.key.none; byte[] mImportBytes; - private LongSparseArray<ParcelableKeyRing> mCachedKeyData; - private static final int REQUEST_CODE_SAFE_SLINGER = 1; - private static final int LOADER_ID_BYTES = 0; - private static final int LOADER_ID_CLOUD = 1; @Override public void onCreate(Bundle savedInstanceState) { @@ -90,7 +91,7 @@ public class AddKeysActivity extends ActionBarActivity implements mProviderHelper = new ProviderHelper(this); - setContentView(R.layout.add_key_activity); + setContentView(R.layout.add_keys_activity); mSafeSlingerKeySpinner = (ExchangeKeySpinner) findViewById(R.id.add_keys_safeslinger_key_spinner); mActionSafeSlinger = findViewById(R.id.add_keys_safeslinger); @@ -99,6 +100,7 @@ public class AddKeysActivity extends ActionBarActivity implements mActionSafeSlingerIcon.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); mActionQrCode = findViewById(R.id.add_keys_qr_code); + mActionNfc = findViewById(R.id.add_keys_nfc); mActionSearchCloud = findViewById(R.id.add_keys_search_cloud); mSafeSlingerKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() { @@ -122,6 +124,16 @@ public class AddKeysActivity extends ActionBarActivity implements } }); + mActionNfc.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // show nfc help + Intent intent = new Intent(AddKeysActivity.this, HelpActivity.class); + intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, HelpActivity.TAB_NFC); + startActivityForResult(intent, 0); + } + }); + mActionSearchCloud.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -153,10 +165,12 @@ public class AddKeysActivity extends ActionBarActivity implements } private void startQrCode() { - + // scan using xzing's Barcode Scanner + new IntentIntegrator(this).initiateScan(); } private void searchCloud() { + finish(); Intent importIntent = new Intent(this, ImportKeysActivity.class); startActivity(importIntent); } @@ -169,28 +183,57 @@ public class AddKeysActivity extends ActionBarActivity implements result.createNotify(this).show(); } else { switch (requestCode) { - case REQUEST_CODE_SAFE_SLINGER: + case REQUEST_CODE_SAFE_SLINGER: { switch (resultCode) { case ExchangeActivity.RESULT_EXCHANGE_OK: // import exchanged keys mImportBytes = getSlingedKeys(data); getSupportLoaderManager().restartLoader(LOADER_ID_BYTES, null, this); -// Intent importIntent = new Intent(this, ImportKeysActivity.class); -// importIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY); -// importIntent.putExtra(ImportKeysActivity.EXTRA_KEY_BYTES, getSlingedKeys(data)); -// startActivity(importIntent); break; case ExchangeActivity.RESULT_EXCHANGE_CANCELED: - // handle canceled result - // ... + // do nothing break; } break; + } + case IntentIntegratorSupportV4.REQUEST_CODE: { + IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode, + resultCode, data); + if (scanResult != null && scanResult.getFormatName() != null) { + String scannedContent = scanResult.getContents(); + + Log.d(Constants.TAG, "scannedContent: " + scannedContent); + + // look if it's fingerprint only + if (scannedContent.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { + importKeys(null, getFingerprintFromUri(Uri.parse(scanResult.getContents()))); + return; + } + + // is this a full key encoded as qr code? + if (scannedContent.startsWith("-----BEGIN PGP")) { + // TODO +// mImportActivity.loadCallback(new ImportKeysListFragment.BytesLoaderState(scannedContent.getBytes(), null)); + return; + } + + // fail... + Notify.showNotify(this, R.string.import_qr_code_wrong, Notify.Style.ERROR); + } + + break; + } } super.onActivityResult(requestCode, resultCode, data); } } + private String getFingerprintFromUri(Uri dataUri) { + String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH); + Log.d(Constants.TAG, "fingerprint: " + fingerprint); + return fingerprint; + } + private static byte[] getSlingedKeys(Intent data) { ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -221,10 +264,6 @@ public class AddKeysActivity extends ActionBarActivity implements InputData inputData = new InputData(new ByteArrayInputStream(mImportBytes), mImportBytes.length); return new ImportKeysListLoader(this, inputData); } - case LOADER_ID_CLOUD: { -// ImportKeysListFragment.CloudLoaderState ls = (ImportKeysListFragment.CloudLoaderState) mLoaderState; -// return new ImportKeysListCloudLoader(this, ls.mServerQuery, ls.mCloudPrefs); - } default: return null; @@ -232,74 +271,50 @@ public class AddKeysActivity extends ActionBarActivity implements } @Override - public void onLoadFinished(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader, AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) { - + public void onLoadFinished(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader, + AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) { Log.d(Constants.TAG, "data: " + data.getResult()); - // swap in the real data! -// mAdapter.setData(data.getResult()); -// mAdapter.notifyDataSetChanged(); -// -// setListAdapter(mAdapter); -// -// // The list should now be shown. -// if (isResumed()) { -// setListShown(true); -// } else { -// setListShownNoAnimation(true); -// } - - Exception error = data.getError(); - - // free old cached key data - mCachedKeyData = null; + GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult(); + LongSparseArray<ParcelableKeyRing> cachedKeyData = null; // TODO: Use parcels!!!!!!!!!!!!!!! switch (loader.getId()) { case LOADER_ID_BYTES: - if (error == null) { + if (getKeyResult.success()) { // No error - mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); - } else if (error instanceof ImportKeysListLoader.NoValidKeysException) { - Notify.showNotify(this, R.string.error_import_no_valid_keys, Notify.Style.ERROR); - } else if (error instanceof ImportKeysListLoader.NonPgpPartException) { - Notify.showNotify(this, - ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources(). - getQuantityString(R.plurals.error_import_non_pgp_part, - ((ImportKeysListLoader.NonPgpPartException) error).getCount()), - Notify.Style.OK - ); + cachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); } else { - Notify.showNotify(this, R.string.error_generic_report_bug, Notify.Style.ERROR); + getKeyResult.createNotify(this).show(); } - break; - - case LOADER_ID_CLOUD: - if (error == null) { - // No error - } else if (error instanceof Keyserver.QueryTooShortException) { - Notify.showNotify(this, R.string.error_query_too_short, Notify.Style.ERROR); - } else if (error instanceof Keyserver.TooManyResponsesException) { - Notify.showNotify(this, R.string.error_too_many_responses, Notify.Style.ERROR); - } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) { - Notify.showNotify(this, R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR); - } else if (error instanceof Keyserver.QueryFailedException) { - Log.d(Constants.TAG, - "Unrecoverable keyserver query error: " + error.getLocalizedMessage()); - String alert = this.getString(R.string.error_searching_keys); - alert = alert + " (" + error.getLocalizedMessage() + ")"; - Notify.showNotify(this, alert, Notify.Style.ERROR); - } +// if (error == null) { +// // No error +// cachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); +// Log.d(Constants.TAG, "no error!:" + cachedKeyData); +// +// } else if (error instanceof ImportKeysListLoader.NoValidKeysException) { +// Notify.showNotify(this, R.string.error_import_no_valid_keys, Notify.Style.ERROR); +// } else if (error instanceof ImportKeysListLoader.NonPgpPartException) { +// Notify.showNotify(this, +// ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources(). +// getQuantityString(R.plurals.error_import_non_pgp_part, +// ((ImportKeysListLoader.NonPgpPartException) error).getCount()), +// Notify.Style.OK +// ); +// } else { +// Notify.showNotify(this, R.string.error_generic_report_bug, Notify.Style.ERROR); +// } break; + default: break; } - importKeys(); + importKeys(cachedKeyData, null); } @Override @@ -309,44 +324,42 @@ public class AddKeysActivity extends ActionBarActivity implements // Clear the data in the adapter. // mAdapter.clear(); break; - case LOADER_ID_CLOUD: - // Clear the data in the adapter. -// mAdapter.clear(); - break; default: break; } } - public ParcelableFileCache.IteratorWithSize<ParcelableKeyRing> getSelectedData() { + public ParcelableFileCache.IteratorWithSize<ParcelableKeyRing> + getSelectedData(final LongSparseArray<ParcelableKeyRing> keyData) { return new ParcelableFileCache.IteratorWithSize<ParcelableKeyRing>() { int i = 0; @Override public int getSize() { - return mCachedKeyData.size(); + return keyData.size(); } @Override public boolean hasNext() { - return (mCachedKeyData.get(i + 1) != null); + return (i < getSize()); } @Override public ParcelableKeyRing next() { - ParcelableKeyRing key = mCachedKeyData.get(i); + // get the object by the key. + ParcelableKeyRing key = keyData.valueAt(i); i++; return key; } @Override public void remove() { - mCachedKeyData.remove(i); + keyData.remove(i); } }; } - public void importKeys() { + public void importKeys(final LongSparseArray<ParcelableKeyRing> keyData, String fingerprint) { // Message is received after importing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( this, @@ -370,27 +383,18 @@ public class AddKeysActivity extends ActionBarActivity implements return; } -// 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(); -// intent.putExtra(ImportKeyResult.EXTRA_RESULT, result); -// ImportKeysActivity.this.setResult(RESULT_OK, intent); -// ImportKeysActivity.this.finish(); -// return; -// } -// if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(getIntent().getAction())) { -// ImportKeysActivity.this.setResult(RESULT_OK, mPendingIntentData); -// ImportKeysActivity.this.finish(); -// return; -// } + finish(); + Intent certifyIntent = new Intent(); // TODO: certify + certifyIntent.putExtra(ImportKeyResult.EXTRA_RESULT, result); + certifyIntent.putExtra("key ids", result.getImportedMasterKeyIds()); // TODO: extra + startActivity(certifyIntent); result.createNotify(AddKeysActivity.this).show(); } } }; -// ImportKeysListFragment.LoaderState ls = mListFragment.getLoaderState(); -// if (importBytes != null) { + if (keyData != null) { Log.d(Constants.TAG, "importKeys started"); // Send all information needed to service to import key in other thread @@ -401,9 +405,6 @@ public class AddKeysActivity extends ActionBarActivity implements // fill values for this action Bundle data = new Bundle(); - // get DATA from selected key entries -// ParcelableFileCache.IteratorWithSize<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData(); - // instead of giving the entries by Intent extra, cache them into a // file to prevent Java Binder problems on heavy imports // read FileImportCache for more info. @@ -412,7 +413,7 @@ public class AddKeysActivity extends ActionBarActivity implements // display here, we should be able to import. ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl"); - cache.writeCache(getSelectedData()); + cache.writeCache(getSelectedData(keyData)); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -429,36 +430,44 @@ public class AddKeysActivity extends ActionBarActivity implements Log.e(Constants.TAG, "Problem writing cache file", e); Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR); } -// } else if (ls instanceof ImportKeysListFragment.CloudLoaderState) { -// ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls; -// -// // Send all information needed to service to query keys in other thread -// Intent intent = new Intent(this, KeychainIntentService.class); -// -// intent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS); -// -// // fill values for this action -// Bundle data = new Bundle(); -// -// data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, sls.mCloudPrefs.keyserver); -// -// // get selected key entries -// ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries(); -// data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries); -// -// intent.putExtra(KeychainIntentService.EXTRA_DATA, data); -// -// // Create a new Messenger for the communication back -// Messenger messenger = new Messenger(saveHandler); -// intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); -// -// // show progress dialog -// saveHandler.showProgressDialog(this); -// -// // start service with intent -// startService(intent); -// } else { -// Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR); -// } + } else if (fingerprint != null) { + + // search config + Preferences prefs = Preferences.getPreferences(this); + Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); + + // Send all information needed to service to query keys in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS); + + // fill values for this action + Bundle data = new Bundle(); + + data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, cloudPrefs.keyserver); + + final ImportKeysListEntry keyEntry = new ImportKeysListEntry(); + keyEntry.setFingerprintHex(fingerprint); + keyEntry.setBitStrength(1337); + keyEntry.addOrigin(cloudPrefs.keyserver); + ArrayList<ImportKeysListEntry> selectedEntries = new ArrayList<ImportKeysListEntry>(); + selectedEntries.add(keyEntry); + + data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } else { + Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR); + } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index 16ebd6aca..ad50ecd3d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -31,6 +31,7 @@ import android.widget.ListView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.results.GetKeyResult; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; @@ -285,48 +286,60 @@ public class ImportKeysListFragment extends ListFragment implements setListShownNoAnimation(true); } - Exception error = data.getError(); - // free old cached key data mCachedKeyData = null; + GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult(); switch (loader.getId()) { case LOADER_ID_BYTES: - if (error == null) { + if (getKeyResult.success()) { // No error mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); - } else if (error instanceof ImportKeysListLoader.NoValidKeysException) { - Notify.showNotify(getActivity(), R.string.error_import_no_valid_keys, Notify.Style.ERROR); - } else if (error instanceof ImportKeysListLoader.NonPgpPartException) { - Notify.showNotify(getActivity(), - ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources(). - getQuantityString(R.plurals.error_import_non_pgp_part, - ((ImportKeysListLoader.NonPgpPartException) error).getCount()), - Notify.Style.OK - ); } else { - Notify.showNotify(getActivity(), R.string.error_generic_report_bug, Notify.Style.ERROR); + getKeyResult.createNotify(getActivity()).show(); } +// if (error == null) { +// // No error +// mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); +// } else if (error instanceof ImportKeysListLoader.NoValidKeysException) { +// Notify.showNotify(getActivity(), R.string.error_import_no_valid_keys, Notify.Style.ERROR); +// } else if (error instanceof ImportKeysListLoader.NonPgpPartException) { +// Notify.showNotify(getActivity(), +// ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources(). +// getQuantityString(R.plurals.error_import_non_pgp_part, +// ((ImportKeysListLoader.NonPgpPartException) error).getCount()), +// Notify.Style.OK +// ); +// } else { +// Notify.showNotify(getActivity(), R.string.error_generic_report_bug, Notify.Style.ERROR); +// } break; case LOADER_ID_CLOUD: - if (error == null) { + if (getKeyResult.success()) { // No error - } else if (error instanceof Keyserver.QueryTooShortException) { - Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR); - } else if (error instanceof Keyserver.TooManyResponsesException) { - Notify.showNotify(getActivity(), R.string.error_too_many_responses, Notify.Style.ERROR); - } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) { - Notify.showNotify(getActivity(), R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR); - } else if (error instanceof Keyserver.QueryFailedException) { - Log.d(Constants.TAG, - "Unrecoverable keyserver query error: " + error.getLocalizedMessage()); - String alert = getActivity().getString(R.string.error_searching_keys); - alert = alert + " (" + error.getLocalizedMessage() + ")"; - Notify.showNotify(getActivity(), alert, Notify.Style.ERROR); + } else { + getKeyResult.createNotify(getActivity()).show(); } + + +// if (error == null) { +// // No error +// } else if (error instanceof Keyserver.QueryTooShortException) { +// Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR); +// } else if (error instanceof Keyserver.TooManyResponsesException) { +// Notify.showNotify(getActivity(), R.string.error_too_many_responses, Notify.Style.ERROR); +// } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) { +// Notify.showNotify(getActivity(), R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR); +// } else if (error instanceof Keyserver.QueryFailedException) { +// Log.d(Constants.TAG, +// "Unrecoverable keyserver query error: " + error.getLocalizedMessage()); +// String alert = getActivity().getString(R.string.error_searching_keys); +// alert = alert + " (" + error.getLocalizedMessage() + ")"; +// Notify.showNotify(getActivity(), alert, Notify.Style.ERROR); +// } break; default: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java index 03cd6c431..a835f3652 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MultiCertifyKeyFragment.java @@ -239,12 +239,6 @@ public class MultiCertifyKeyFragment extends LoaderFragment if (!subGrouped) { // 1. This name should NOT be grouped with the previous, so we flush the buffer - /*/ Special case: only a single user id (first && last) - if (data.isFirst()) { - lastMasterKeyId = masterKeyId; - uids.add(userId); - }*/ - Parcel p = Parcel.obtain(); p.writeStringList(uids); byte[] d = p.marshall(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java index 5f2aec4fe..dc0c5846a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java @@ -17,6 +17,8 @@ package org.sufficientlysecure.keychain.ui.adapter; +import org.sufficientlysecure.keychain.service.results.OperationResult; + /** * The AsyncTaskResultWrapper is used to wrap a result from a AsyncTask (for example: Loader). * You can pass the result and an exception in it if an error occurred. @@ -28,19 +30,19 @@ package org.sufficientlysecure.keychain.ui.adapter; public class AsyncTaskResultWrapper<T> { private final T mResult; - private final Exception mError; + private final OperationResult mOperationResult; - public AsyncTaskResultWrapper(T result, Exception error) { + public AsyncTaskResultWrapper(T result, OperationResult operationResult) { this.mResult = result; - this.mError = error; + this.mOperationResult = operationResult; } public T getResult() { return mResult; } - public Exception getError() { - return mError; + public OperationResult getOperationResult() { + return mOperationResult; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java index 0332e8882..cb51a15a9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java @@ -21,6 +21,9 @@ import android.content.Context; import android.support.v4.content.AsyncTaskLoader; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.keyimport.Keyserver; +import org.sufficientlysecure.keychain.service.results.GetKeyResult; +import org.sufficientlysecure.keychain.service.results.OperationResult; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.keyimport.CloudSearch; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; @@ -93,7 +96,8 @@ public class ImportKeysListCloudLoader */ private void queryServer(boolean enforceFingerprint) { try { - ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(mServerQuery, mCloudPrefs); + ArrayList<ImportKeysListEntry> searchResult + = CloudSearch.search(mServerQuery, mCloudPrefs); mEntryList.clear(); // add result to data @@ -114,9 +118,29 @@ public class ImportKeysListCloudLoader } else { mEntryList.addAll(searchResult); } - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); - } catch (Exception e) { - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); + GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult); + } catch (Keyserver.CloudSearchFailureException e) { + // convert exception to result parcel + int error = GetKeyResult.RESULT_ERROR; + OperationResult.LogType logType = null; + if (e instanceof Keyserver.QueryFailedException) { + error = GetKeyResult.RESULT_ERROR_QUERY_FAILED; + logType = OperationResult.LogType.MSG_GET_QUERY_FAILED; + } else if (e instanceof Keyserver.TooManyResponsesException) { + error = GetKeyResult.RESULT_ERROR_TOO_MANY_RESPONSES; + logType = OperationResult.LogType.MSG_GET_TOO_MANY_RESPONSES; + } else if (e instanceof Keyserver.QueryTooShortException) { + error = GetKeyResult.RESULT_ERROR_QUERY_TOO_SHORT; + logType = OperationResult.LogType.MSG_GET_QUERY_TOO_SHORT; + } else if (e instanceof Keyserver.QueryTooShortOrTooManyResponsesException) { + error = GetKeyResult.RESULT_ERROR_TOO_SHORT_OR_TOO_MANY_RESPONSES; + logType = OperationResult.LogType.MSG_GET_QUERY_TOO_SHORT_OR_TOO_MANY_RESPONSES; + } + OperationResult.OperationLog log = new OperationResult.OperationLog(); + log.add(logType, 0); + GetKeyResult getKeyResult = new GetKeyResult(error, log); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult); } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 04947da93..6664cb61a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -25,6 +25,8 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.service.results.GetKeyResult; +import org.sufficientlysecure.keychain.service.results.OperationResult; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.PositionAwareInputStream; @@ -37,9 +39,6 @@ import java.util.Iterator; public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { - public static class NoValidKeysException extends Exception { - } - public static class NonPgpPartException extends Exception { private int mCount; @@ -72,7 +71,8 @@ public class ImportKeysListLoader return mEntryListWrapper; } - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, null); + GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null); + mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, getKeyResult); if (mInputData == null) { Log.e(Constants.TAG, "Input data is null!"); @@ -136,13 +136,11 @@ public class ImportKeysListLoader } } catch (IOException e) { Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e); - - NoValidKeysException e1 = new NoValidKeysException(); + OperationResult.OperationLog log = new OperationResult.OperationLog(); + log.add(OperationResult.LogType.MSG_GET_NO_VALID_KEYS, 0); + GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_ERROR_NO_VALID_KEYS, log); mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> - (mData, e1); - } catch (Exception e) { - Log.e(Constants.TAG, "Other Exception on parsing key file!", e); - mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e); + (mData, getKeyResult); } } diff --git a/OpenKeychain/src/main/res/layout/add_key_activity.xml b/OpenKeychain/src/main/res/layout/add_keys_activity.xml index 624ef9ed4..b058c8268 100644 --- a/OpenKeychain/src/main/res/layout/add_key_activity.xml +++ b/OpenKeychain/src/main/res/layout/add_keys_activity.xml @@ -21,7 +21,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" - android:text="Secure Exchange" /> + android:text="@string/add_keys_section_secure_exchange" /> <LinearLayout android:layout_width="match_parent" @@ -35,7 +35,7 @@ android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textAppearance="?android:attr/textAppearanceMedium" - android:text="My key:" + android:text="@string/add_keys_my_key" android:paddingRight="8dp" /> <org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner @@ -61,7 +61,7 @@ android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="0dip" android:layout_height="match_parent" - android:text="Start exchange" + android:text="@string/add_keys_start_exchange" android:layout_weight="1" android:gravity="center_vertical" /> @@ -87,7 +87,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="32dp" - android:text="Secure add" /> + android:text="@string/add_keys_section_secure_add" /> <TextView android:id="@+id/add_keys_qr_code" @@ -99,12 +99,33 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:clickable="true" style="@style/SelectableItem" - android:text="QR Code" + android:text="@string/add_keys_qr_code" android:drawableRight="@drawable/ic_action_qr_code" android:drawablePadding="8dp" android:gravity="center_vertical" /> <View + android:id="@+id/view_key_action_certify_divider" + android:layout_width="match_parent" + android:layout_height="1dip" + android:background="?android:attr/listDivider" /> + + <TextView + android:id="@+id/add_keys_nfc" + android:paddingLeft="8dp" + android:paddingRight="8dp" + android:textAppearance="?android:attr/textAppearanceMedium" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?android:attr/listPreferredItemHeight" + android:clickable="true" + style="@style/SelectableItem" + android:text="@string/add_keys_nfc" + android:drawableRight="@drawable/ic_action_nfc" + android:drawablePadding="8dp" + android:gravity="center_vertical" /> + + <View android:layout_width="match_parent" android:layout_height="1dip" android:background="?android:attr/listDivider" @@ -115,7 +136,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="32dp" - android:text="Import (untrusted)" /> + android:text="@string/add_keys_section_import" /> <TextView android:id="@+id/add_keys_search_cloud" @@ -127,11 +148,17 @@ android:minHeight="?android:attr/listPreferredItemHeight" android:clickable="true" style="@style/SelectableItem" - android:text="Search cloud" + android:text="@string/add_keys_cloud" android:drawableRight="@drawable/ic_action_search" android:drawablePadding="8dp" android:gravity="center_vertical" /> + <View + android:layout_width="match_parent" + android:layout_height="1dip" + android:background="?android:attr/listDivider" + android:layout_marginBottom="8dp" /> + </LinearLayout> </LinearLayout>
\ 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 438f976e3..e61f4912a 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -261,18 +261,8 @@ <string name="error_jelly_bean_needed">"You need Android 4.1 to use Android's NFC Beam feature!"</string> <string name="error_nfc_needed">"NFC is not available on your device!"</string> <string name="error_nothing_import">"No keys found!"</string> - <string name="error_query_too_short">"Search query too short. Please refine your query!"</string> - <string name="error_searching_keys">"An error occurred when searching for keys."</string> - <string name="error_too_many_responses">"Key search query returned too many candidates. Please refine your query!"</string> - <string name="error_too_short_or_too_many_responses">"Either no keys or too many have been found. Please improve your query!"</string> <string name="error_contacts_key_id_missing">"Retrieving the key ID from contacts failed!"</string> - - <string name="error_import_no_valid_keys">"No valid keys found in File/Clipboard!"</string> <string name="error_generic_report_bug">"A generic error occurred, please create a new bug report for OpenKeychain."</string> - <plurals name="error_import_non_pgp_part"> - <item quantity="one">"part of the loaded file is a valid OpenPGP object but not a OpenPGP key"</item> - <item quantity="other">"parts of the loaded file are valid OpenPGP objects but not OpenPGP keys"</item> - </plurals> <!-- results shown after decryption/verification --> <string name="decrypt_result_invalid_signature">"Invalid signature!"</string> @@ -284,6 +274,17 @@ <string name="decrypt_result_decrypted_and_signature_uncertified">"Successfully decrypted and valid signature (uncertified)"</string> <string name="decrypt_result_decrypted_and_signature_certified">"Successfully decrypted and valid signature (certified)"</string> + <!-- Add keys --> + <string name="add_keys_section_secure_exchange">"Secure Exchange"</string> + <string name="add_keys_section_secure_add">"Secure Add"</string> + <string name="add_keys_section_import">"Import (untrusted)"</string> + <string name="add_keys_my_key">"My key:"</string> + <string name="add_keys_start_exchange">"Start exchange"</string> + <string name="add_keys_qr_code">"Scan QR Code"</string> + <string name="add_keys_nfc">"Receive via NFC"</string> + <string name="add_keys_cloud">"Search cloud"</string> + + <!-- progress dialogs, usually ending in '…' --> <string name="progress_done">"Done."</string> <string name="progress_cancel">"Cancel"</string> @@ -916,6 +917,19 @@ <string name="msg_acc_saved">"Account saved"</string> + <string name="msg_download_success">"Downloaded successfully!"</string> + <string name="msg_download_no_valid_keys">"No valid keys found in File/Clipboard!"</string> + <string name="msg_download_no_pgp_parts">"TODO: plurals!"</string> + <plurals name="error_import_non_pgp_part"> + <item quantity="one">"part of the loaded file is a valid OpenPGP object but not a OpenPGP key"</item> + <item quantity="other">"parts of the loaded file are valid OpenPGP objects but not OpenPGP keys"</item> + </plurals> + <string name="msg_download_query_too_short">"Search query too short. Please refine your query!"</string> + <string name="msg_download_too_many_responses">"Key search query returned too many candidates. Please refine your query!"</string> + <string name="msg_download_query_too_short_or_too_many_responses">"Either no keys or too many have been found. Please improve your query!"</string> + + <string name="msg_download_query_failed">"An error occurred when searching for keys."</string> + <!-- PassphraseCache --> <string name="passp_cache_notif_click_to_clear">"Click to clear cached passphrases"</string> <string name="passp_cache_notif_n_keys">"OpenKeychain has cached %d passphrases"</string> |