Workings on new parcel for key downloads

This commit is contained in:
Dominik Schürmann 2014-10-04 21:09:32 +02:00
parent 9e1a0c2c0a
commit 0599f0dd4e
8 changed files with 211 additions and 85 deletions

View File

@ -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];
}
};
}

View File

@ -538,8 +538,16 @@ public abstract class OperationResult implements Parcelable {
MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg), 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; public final int mMsgId;

View File

@ -43,10 +43,10 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; 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.ImportKeyResult;
import org.sufficientlysecure.keychain.service.results.OperationResult; import org.sufficientlysecure.keychain.service.results.OperationResult;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper; 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.adapter.ImportKeysListLoader;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner; import org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner;
@ -61,7 +61,6 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale; import java.util.Locale;
import edu.cmu.cylab.starslinger.exchange.ExchangeActivity; import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
@ -306,7 +305,9 @@ public class AddKeysActivity extends ActionBarActivity implements
AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) { AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) {
Log.d(Constants.TAG, "data: " + data.getResult()); Log.d(Constants.TAG, "data: " + data.getResult());
Exception error = data.getError(); // Exception error = data.getError();
GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult();
LongSparseArray<ParcelableKeyRing> cachedKeyData = null; LongSparseArray<ParcelableKeyRing> cachedKeyData = null;
@ -314,42 +315,55 @@ public class AddKeysActivity extends ActionBarActivity implements
switch (loader.getId()) { switch (loader.getId()) {
case LOADER_ID_BYTES: case LOADER_ID_BYTES:
if (error == null) { if (getKeyResult.success()) {
// No error // No error
cachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); 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 { } else {
Notify.showNotify(this, R.string.error_generic_report_bug, Notify.Style.ERROR); getKeyResult.createNotify(this).show();
} }
// 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; break;
case LOADER_ID_CLOUD: case LOADER_ID_CLOUD:
if (error == null) { if (getKeyResult.success()) {
// No error // No error
} else if (error instanceof Keyserver.QueryTooShortException) { } else {
Notify.showNotify(this, R.string.error_query_too_short, Notify.Style.ERROR); getKeyResult.createNotify(this).show();
} 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
// } 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);
// }
break; break;
default: default:

View File

@ -31,6 +31,7 @@ import android.widget.ListView;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.results.GetKeyResult;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@ -285,48 +286,60 @@ public class ImportKeysListFragment extends ListFragment implements
setListShownNoAnimation(true); setListShownNoAnimation(true);
} }
Exception error = data.getError();
// free old cached key data // free old cached key data
mCachedKeyData = null; mCachedKeyData = null;
GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult();
switch (loader.getId()) { switch (loader.getId()) {
case LOADER_ID_BYTES: case LOADER_ID_BYTES:
if (error == null) { if (getKeyResult.success()) {
// No error // No error
mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings(); 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 { } 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; break;
case LOADER_ID_CLOUD: case LOADER_ID_CLOUD:
if (error == null) { if (getKeyResult.success()) {
// No error // No error
} else if (error instanceof Keyserver.QueryTooShortException) { } else {
Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR); getKeyResult.createNotify(getActivity()).show();
} 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);
} }
// 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; break;
default: default:

View File

@ -17,6 +17,8 @@
package org.sufficientlysecure.keychain.ui.adapter; 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). * 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. * 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> { public class AsyncTaskResultWrapper<T> {
private final T mResult; 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.mResult = result;
this.mError = error; this.mOperationResult = operationResult;
} }
public T getResult() { public T getResult() {
return mResult; return mResult;
} }
public Exception getError() { public OperationResult getOperationResult() {
return mError; return mOperationResult;
} }
} }

View File

@ -21,6 +21,9 @@ import android.content.Context;
import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.AsyncTaskLoader;
import org.sufficientlysecure.keychain.Constants; 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.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.CloudSearch; import org.sufficientlysecure.keychain.keyimport.CloudSearch;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
@ -93,7 +96,8 @@ public class ImportKeysListCloudLoader
*/ */
private void queryServer(boolean enforceFingerprint) { private void queryServer(boolean enforceFingerprint) {
try { try {
ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(mServerQuery, mCloudPrefs); ArrayList<ImportKeysListEntry> searchResult
= CloudSearch.search(mServerQuery, mCloudPrefs);
mEntryList.clear(); mEntryList.clear();
// add result to data // add result to data
@ -114,9 +118,29 @@ public class ImportKeysListCloudLoader
} else { } else {
mEntryList.addAll(searchResult); mEntryList.addAll(searchResult);
} }
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null); GetKeyResult getKeyResult = new GetKeyResult(GetKeyResult.RESULT_OK, null);
} catch (Exception e) { mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, getKeyResult);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, e); } 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);
} }
} }
} }

View File

@ -25,6 +25,8 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; 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.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream; import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
@ -37,9 +39,6 @@ import java.util.Iterator;
public class ImportKeysListLoader public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
public static class NoValidKeysException extends Exception {
}
public static class NonPgpPartException extends Exception { public static class NonPgpPartException extends Exception {
private int mCount; private int mCount;
@ -72,7 +71,8 @@ public class ImportKeysListLoader
return mEntryListWrapper; 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) { if (mInputData == null) {
Log.e(Constants.TAG, "Input data is null!"); Log.e(Constants.TAG, "Input data is null!");
@ -136,13 +136,11 @@ public class ImportKeysListLoader
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e); Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e);
OperationResult.OperationLog log = new OperationResult.OperationLog();
NoValidKeysException e1 = new NoValidKeysException(); 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>> mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
(mData, e1); (mData, getKeyResult);
} catch (Exception e) {
Log.e(Constants.TAG, "Other Exception on parsing key file!", e);
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
} }
} }

View File

@ -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_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_nfc_needed">"NFC is not available on your device!"</string>
<string name="error_nothing_import">"No keys found!"</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_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> <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 --> <!-- results shown after decryption/verification -->
<string name="decrypt_result_invalid_signature">"Invalid signature!"</string> <string name="decrypt_result_invalid_signature">"Invalid signature!"</string>
@ -928,6 +918,19 @@
<string name="msg_acc_saved">"Account saved"</string> <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 --> <!-- PassphraseCache -->
<string name="passp_cache_notif_click_to_clear">"Click to clear cached passphrases"</string> <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> <string name="passp_cache_notif_n_keys">"OpenKeychain has cached %d passphrases"</string>