mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-17 06:15:15 -05:00
Merge branch 'jacobshack-certify' of github.com:open-keychain/open-keychain into jacobshack-certify
This commit is contained in:
commit
2b436b9a19
@ -120,10 +120,12 @@ public class PgpImportExport {
|
|||||||
// If there aren't even any keys, do nothing here.
|
// If there aren't even any keys, do nothing here.
|
||||||
if (entries == null || !entries.hasNext()) {
|
if (entries == null || !entries.hasNext()) {
|
||||||
return new ImportKeyResult(
|
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;
|
int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0;
|
||||||
|
ArrayList<Long> importedMasterKeyIds = new ArrayList<Long>();
|
||||||
|
|
||||||
int position = 0;
|
int position = 0;
|
||||||
double progSteps = 100.0 / num;
|
double progSteps = 100.0 / num;
|
||||||
@ -165,11 +167,13 @@ public class PgpImportExport {
|
|||||||
badKeys += 1;
|
badKeys += 1;
|
||||||
} else if (result.updated()) {
|
} else if (result.updated()) {
|
||||||
oldKeys += 1;
|
oldKeys += 1;
|
||||||
|
importedMasterKeyIds.add(key.getMasterKeyId());
|
||||||
} else {
|
} else {
|
||||||
newKeys += 1;
|
newKeys += 1;
|
||||||
if (key.isSecret()) {
|
if (key.isSecret()) {
|
||||||
secret += 1;
|
secret += 1;
|
||||||
}
|
}
|
||||||
|
importedMasterKeyIds.add(key.getMasterKeyId());
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -210,8 +214,14 @@ public class PgpImportExport {
|
|||||||
resultType |= ImportKeyResult.RESULT_CANCELLED;
|
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,
|
public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds,
|
||||||
|
@ -465,7 +465,7 @@ public class KeychainIntentService extends IntentService implements Progressable
|
|||||||
// actual import afterwards
|
// actual import afterwards
|
||||||
keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
|
keyRings.add(new ParcelableKeyRing(downloadedKeyBytes, entry.getFingerprintHex()));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Keyserver.QueryFailedException e) {
|
||||||
sendErrorToHandler(e);
|
sendErrorToHandler(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
|
|||||||
public class ImportKeyResult extends OperationResult {
|
public class ImportKeyResult extends OperationResult {
|
||||||
|
|
||||||
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
|
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
|
||||||
|
public final long[] mImportedMasterKeyIds;
|
||||||
|
|
||||||
// At least one new key
|
// At least one new key
|
||||||
public static final int RESULT_OK_NEWKEYS = 8;
|
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;
|
return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long[] getImportedMasterKeyIds() {
|
||||||
|
return mImportedMasterKeyIds;
|
||||||
|
}
|
||||||
|
|
||||||
public ImportKeyResult(Parcel source) {
|
public ImportKeyResult(Parcel source) {
|
||||||
super(source);
|
super(source);
|
||||||
mNewKeys = source.readInt();
|
mNewKeys = source.readInt();
|
||||||
mUpdatedKeys = source.readInt();
|
mUpdatedKeys = source.readInt();
|
||||||
mBadKeys = source.readInt();
|
mBadKeys = source.readInt();
|
||||||
mSecret = source.readInt();
|
mSecret = source.readInt();
|
||||||
|
mImportedMasterKeyIds = source.createLongArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImportKeyResult(int result, OperationLog log,
|
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);
|
super(result, log);
|
||||||
mNewKeys = newKeys;
|
mNewKeys = newKeys;
|
||||||
mUpdatedKeys = updatedKeys;
|
mUpdatedKeys = updatedKeys;
|
||||||
mBadKeys = badKeys;
|
mBadKeys = badKeys;
|
||||||
mSecret = secret;
|
mSecret = secret;
|
||||||
|
mImportedMasterKeyIds = importedMasterKeyIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -93,6 +101,7 @@ public class ImportKeyResult extends OperationResult {
|
|||||||
dest.writeInt(mUpdatedKeys);
|
dest.writeInt(mUpdatedKeys);
|
||||||
dest.writeInt(mBadKeys);
|
dest.writeInt(mBadKeys);
|
||||||
dest.writeInt(mSecret);
|
dest.writeInt(mSecret);
|
||||||
|
dest.writeLongArray(mImportedMasterKeyIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() {
|
public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() {
|
||||||
|
@ -537,8 +537,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;
|
||||||
|
@ -31,32 +31,36 @@ import android.support.v7.app.ActionBarActivity;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
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.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||||
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
|
||||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
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;
|
||||||
import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
|
import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
|
||||||
import org.sufficientlysecure.keychain.util.InputData;
|
import org.sufficientlysecure.keychain.util.InputData;
|
||||||
|
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||||
|
import org.sufficientlysecure.keychain.util.Preferences;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
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 edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
|
import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
|
||||||
import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
|
import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
|
||||||
@ -68,6 +72,7 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
View mActionSafeSlinger;
|
View mActionSafeSlinger;
|
||||||
ImageView mActionSafeSlingerIcon;
|
ImageView mActionSafeSlingerIcon;
|
||||||
View mActionQrCode;
|
View mActionQrCode;
|
||||||
|
View mActionNfc;
|
||||||
View mActionSearchCloud;
|
View mActionSearchCloud;
|
||||||
|
|
||||||
ProviderHelper mProviderHelper;
|
ProviderHelper mProviderHelper;
|
||||||
@ -75,14 +80,10 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
long mExchangeMasterKeyId = Constants.key.none;
|
long mExchangeMasterKeyId = Constants.key.none;
|
||||||
|
|
||||||
byte[] mImportBytes;
|
byte[] mImportBytes;
|
||||||
private LongSparseArray<ParcelableKeyRing> mCachedKeyData;
|
|
||||||
|
|
||||||
|
|
||||||
private static final int REQUEST_CODE_SAFE_SLINGER = 1;
|
private static final int REQUEST_CODE_SAFE_SLINGER = 1;
|
||||||
|
|
||||||
|
|
||||||
private static final int LOADER_ID_BYTES = 0;
|
private static final int LOADER_ID_BYTES = 0;
|
||||||
private static final int LOADER_ID_CLOUD = 1;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@ -90,7 +91,7 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
|
|
||||||
mProviderHelper = new ProviderHelper(this);
|
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);
|
mSafeSlingerKeySpinner = (ExchangeKeySpinner) findViewById(R.id.add_keys_safeslinger_key_spinner);
|
||||||
mActionSafeSlinger = findViewById(R.id.add_keys_safeslinger);
|
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),
|
mActionSafeSlingerIcon.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
|
||||||
PorterDuff.Mode.SRC_IN);
|
PorterDuff.Mode.SRC_IN);
|
||||||
mActionQrCode = findViewById(R.id.add_keys_qr_code);
|
mActionQrCode = findViewById(R.id.add_keys_qr_code);
|
||||||
|
mActionNfc = findViewById(R.id.add_keys_nfc);
|
||||||
mActionSearchCloud = findViewById(R.id.add_keys_search_cloud);
|
mActionSearchCloud = findViewById(R.id.add_keys_search_cloud);
|
||||||
|
|
||||||
mSafeSlingerKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() {
|
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() {
|
mActionSearchCloud.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
@ -153,10 +165,12 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startQrCode() {
|
private void startQrCode() {
|
||||||
|
// scan using xzing's Barcode Scanner
|
||||||
|
new IntentIntegrator(this).initiateScan();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void searchCloud() {
|
private void searchCloud() {
|
||||||
|
finish();
|
||||||
Intent importIntent = new Intent(this, ImportKeysActivity.class);
|
Intent importIntent = new Intent(this, ImportKeysActivity.class);
|
||||||
startActivity(importIntent);
|
startActivity(importIntent);
|
||||||
}
|
}
|
||||||
@ -169,28 +183,57 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
result.createNotify(this).show();
|
result.createNotify(this).show();
|
||||||
} else {
|
} else {
|
||||||
switch (requestCode) {
|
switch (requestCode) {
|
||||||
case REQUEST_CODE_SAFE_SLINGER:
|
case REQUEST_CODE_SAFE_SLINGER: {
|
||||||
switch (resultCode) {
|
switch (resultCode) {
|
||||||
case ExchangeActivity.RESULT_EXCHANGE_OK:
|
case ExchangeActivity.RESULT_EXCHANGE_OK:
|
||||||
// import exchanged keys
|
// import exchanged keys
|
||||||
mImportBytes = getSlingedKeys(data);
|
mImportBytes = getSlingedKeys(data);
|
||||||
getSupportLoaderManager().restartLoader(LOADER_ID_BYTES, null, this);
|
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;
|
break;
|
||||||
case ExchangeActivity.RESULT_EXCHANGE_CANCELED:
|
case ExchangeActivity.RESULT_EXCHANGE_CANCELED:
|
||||||
// handle canceled result
|
// do nothing
|
||||||
// ...
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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);
|
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) {
|
private static byte[] getSlingedKeys(Intent data) {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|
||||||
@ -221,10 +264,6 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
InputData inputData = new InputData(new ByteArrayInputStream(mImportBytes), mImportBytes.length);
|
InputData inputData = new InputData(new ByteArrayInputStream(mImportBytes), mImportBytes.length);
|
||||||
return new ImportKeysListLoader(this, inputData);
|
return new ImportKeysListLoader(this, inputData);
|
||||||
}
|
}
|
||||||
case LOADER_ID_CLOUD: {
|
|
||||||
// ImportKeysListFragment.CloudLoaderState ls = (ImportKeysListFragment.CloudLoaderState) mLoaderState;
|
|
||||||
// return new ImportKeysListCloudLoader(this, ls.mServerQuery, ls.mCloudPrefs);
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
@ -232,74 +271,50 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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());
|
Log.d(Constants.TAG, "data: " + data.getResult());
|
||||||
|
|
||||||
// swap in the real data!
|
GetKeyResult getKeyResult = (GetKeyResult) data.getOperationResult();
|
||||||
// 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;
|
|
||||||
|
|
||||||
|
LongSparseArray<ParcelableKeyRing> cachedKeyData = null;
|
||||||
|
|
||||||
// TODO: Use parcels!!!!!!!!!!!!!!!
|
// TODO: Use parcels!!!!!!!!!!!!!!!
|
||||||
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();
|
cachedKeyData = ((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
|
|
||||||
);
|
|
||||||
} 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:
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
importKeys();
|
importKeys(cachedKeyData, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -307,10 +322,6 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
switch (loader.getId()) {
|
switch (loader.getId()) {
|
||||||
case LOADER_ID_BYTES:
|
case LOADER_ID_BYTES:
|
||||||
// Clear the data in the adapter.
|
// Clear the data in the adapter.
|
||||||
// mAdapter.clear();
|
|
||||||
break;
|
|
||||||
case LOADER_ID_CLOUD:
|
|
||||||
// Clear the data in the adapter.
|
|
||||||
// mAdapter.clear();
|
// mAdapter.clear();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -318,35 +329,37 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParcelableFileCache.IteratorWithSize<ParcelableKeyRing> getSelectedData() {
|
public ParcelableFileCache.IteratorWithSize<ParcelableKeyRing>
|
||||||
|
getSelectedData(final LongSparseArray<ParcelableKeyRing> keyData) {
|
||||||
return new ParcelableFileCache.IteratorWithSize<ParcelableKeyRing>() {
|
return new ParcelableFileCache.IteratorWithSize<ParcelableKeyRing>() {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return mCachedKeyData.size();
|
return keyData.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return (mCachedKeyData.get(i + 1) != null);
|
return (i < getSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParcelableKeyRing next() {
|
public ParcelableKeyRing next() {
|
||||||
ParcelableKeyRing key = mCachedKeyData.get(i);
|
// get the object by the key.
|
||||||
|
ParcelableKeyRing key = keyData.valueAt(i);
|
||||||
i++;
|
i++;
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
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
|
// Message is received after importing is done in KeychainIntentService
|
||||||
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
|
||||||
this,
|
this,
|
||||||
@ -370,27 +383,18 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())
|
finish();
|
||||||
// || ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) {
|
Intent certifyIntent = new Intent(); // TODO: certify
|
||||||
// Intent intent = new Intent();
|
certifyIntent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
|
||||||
// intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
|
certifyIntent.putExtra("key ids", result.getImportedMasterKeyIds()); // TODO: extra
|
||||||
// ImportKeysActivity.this.setResult(RESULT_OK, intent);
|
startActivity(certifyIntent);
|
||||||
// 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;
|
|
||||||
// }
|
|
||||||
|
|
||||||
result.createNotify(AddKeysActivity.this).show();
|
result.createNotify(AddKeysActivity.this).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ImportKeysListFragment.LoaderState ls = mListFragment.getLoaderState();
|
if (keyData != null) {
|
||||||
// if (importBytes != null) {
|
|
||||||
Log.d(Constants.TAG, "importKeys started");
|
Log.d(Constants.TAG, "importKeys started");
|
||||||
|
|
||||||
// Send all information needed to service to import key in other thread
|
// 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
|
// fill values for this action
|
||||||
Bundle data = new Bundle();
|
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
|
// instead of giving the entries by Intent extra, cache them into a
|
||||||
// file to prevent Java Binder problems on heavy imports
|
// file to prevent Java Binder problems on heavy imports
|
||||||
// read FileImportCache for more info.
|
// read FileImportCache for more info.
|
||||||
@ -412,7 +413,7 @@ public class AddKeysActivity extends ActionBarActivity implements
|
|||||||
// display here, we should be able to import.
|
// display here, we should be able to import.
|
||||||
ParcelableFileCache<ParcelableKeyRing> cache =
|
ParcelableFileCache<ParcelableKeyRing> cache =
|
||||||
new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl");
|
new ParcelableFileCache<ParcelableKeyRing>(this, "key_import.pcl");
|
||||||
cache.writeCache(getSelectedData());
|
cache.writeCache(getSelectedData(keyData));
|
||||||
|
|
||||||
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
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);
|
Log.e(Constants.TAG, "Problem writing cache file", e);
|
||||||
Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR);
|
Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR);
|
||||||
}
|
}
|
||||||
// } else if (ls instanceof ImportKeysListFragment.CloudLoaderState) {
|
} else if (fingerprint != null) {
|
||||||
// ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls;
|
|
||||||
//
|
// search config
|
||||||
// // Send all information needed to service to query keys in other thread
|
Preferences prefs = Preferences.getPreferences(this);
|
||||||
// Intent intent = new Intent(this, KeychainIntentService.class);
|
Preferences.CloudSearchPrefs cloudPrefs = new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
|
||||||
//
|
|
||||||
// intent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS);
|
// Send all information needed to service to query keys in other thread
|
||||||
//
|
Intent intent = new Intent(this, KeychainIntentService.class);
|
||||||
// // fill values for this action
|
|
||||||
// Bundle data = new Bundle();
|
intent.setAction(KeychainIntentService.ACTION_DOWNLOAD_AND_IMPORT_KEYS);
|
||||||
//
|
|
||||||
// data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, sls.mCloudPrefs.keyserver);
|
// fill values for this action
|
||||||
//
|
Bundle data = new Bundle();
|
||||||
// // get selected key entries
|
|
||||||
// ArrayList<ImportKeysListEntry> selectedEntries = mListFragment.getSelectedEntries();
|
data.putString(KeychainIntentService.DOWNLOAD_KEY_SERVER, cloudPrefs.keyserver);
|
||||||
// data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
|
|
||||||
//
|
final ImportKeysListEntry keyEntry = new ImportKeysListEntry();
|
||||||
// intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
keyEntry.setFingerprintHex(fingerprint);
|
||||||
//
|
keyEntry.setBitStrength(1337);
|
||||||
// // Create a new Messenger for the communication back
|
keyEntry.addOrigin(cloudPrefs.keyserver);
|
||||||
// Messenger messenger = new Messenger(saveHandler);
|
ArrayList<ImportKeysListEntry> selectedEntries = new ArrayList<ImportKeysListEntry>();
|
||||||
// intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
|
selectedEntries.add(keyEntry);
|
||||||
//
|
|
||||||
// // show progress dialog
|
data.putParcelableArrayList(KeychainIntentService.DOWNLOAD_KEY_LIST, selectedEntries);
|
||||||
// saveHandler.showProgressDialog(this);
|
|
||||||
//
|
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
|
||||||
// // start service with intent
|
|
||||||
// startService(intent);
|
// Create a new Messenger for the communication back
|
||||||
// } else {
|
Messenger messenger = new Messenger(saveHandler);
|
||||||
// Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:text="Secure Exchange" />
|
android:text="@string/add_keys_section_secure_exchange" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -35,7 +35,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:text="My key:"
|
android:text="@string/add_keys_my_key"
|
||||||
android:paddingRight="8dp" />
|
android:paddingRight="8dp" />
|
||||||
|
|
||||||
<org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner
|
<org.sufficientlysecure.keychain.ui.widget.ExchangeKeySpinner
|
||||||
@ -61,7 +61,7 @@
|
|||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:layout_width="0dip"
|
android:layout_width="0dip"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:text="Start exchange"
|
android:text="@string/add_keys_start_exchange"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:gravity="center_vertical" />
|
android:gravity="center_vertical" />
|
||||||
|
|
||||||
@ -87,7 +87,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:text="Secure add" />
|
android:text="@string/add_keys_section_secure_add" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/add_keys_qr_code"
|
android:id="@+id/add_keys_qr_code"
|
||||||
@ -99,11 +99,32 @@
|
|||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
style="@style/SelectableItem"
|
style="@style/SelectableItem"
|
||||||
android:text="QR Code"
|
android:text="@string/add_keys_qr_code"
|
||||||
android:drawableRight="@drawable/ic_action_qr_code"
|
android:drawableRight="@drawable/ic_action_qr_code"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:gravity="center_vertical" />
|
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
|
<View
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="1dip"
|
android:layout_height="1dip"
|
||||||
@ -115,7 +136,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:text="Import (untrusted)" />
|
android:text="@string/add_keys_section_import" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/add_keys_search_cloud"
|
android:id="@+id/add_keys_search_cloud"
|
||||||
@ -127,11 +148,17 @@
|
|||||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
style="@style/SelectableItem"
|
style="@style/SelectableItem"
|
||||||
android:text="Search cloud"
|
android:text="@string/add_keys_cloud"
|
||||||
android:drawableRight="@drawable/ic_action_search"
|
android:drawableRight="@drawable/ic_action_search"
|
||||||
android:drawablePadding="8dp"
|
android:drawablePadding="8dp"
|
||||||
android:gravity="center_vertical" />
|
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>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -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>
|
||||||
@ -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_uncertified">"Successfully decrypted and valid signature (uncertified)"</string>
|
||||||
<string name="decrypt_result_decrypted_and_signature_certified">"Successfully decrypted and valid signature (certified)"</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 '…' -->
|
<!-- progress dialogs, usually ending in '…' -->
|
||||||
<string name="progress_done">"Done."</string>
|
<string name="progress_done">"Done."</string>
|
||||||
<string name="progress_cancel">"Cancel"</string>
|
<string name="progress_cancel">"Cancel"</string>
|
||||||
@ -916,6 +917,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>
|
||||||
|
Loading…
Reference in New Issue
Block a user