diff options
Diffstat (limited to 'OpenKeychain/src')
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> | 
