diff options
Diffstat (limited to 'OpenKeychain/src')
42 files changed, 982 insertions, 495 deletions
diff --git a/OpenKeychain/src/androidTest/java/tests/SomeTest.java b/OpenKeychain/src/androidTest/java/tests/SomeTest.java deleted file mode 100644 index edf5f7bcc..000000000 --- a/OpenKeychain/src/androidTest/java/tests/SomeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package tests; - -import org.junit.Assert; -import org.junit.Test; - -public class SomeTest { -    @Test -    public void willFail() { -        // stub -        // Assert.assertThat(); -    } -}
\ No newline at end of file diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 48677431c..c6e528f4d 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -85,6 +85,11 @@              android:label="@string/title_edit_key"              android:windowSoftInputMode="stateHidden" />          <activity +            android:name=".ui.EditKeyActivityNew" +            android:configChanges="orientation|screenSize|keyboardHidden|keyboard" +            android:label="@string/title_edit_key" +            android:windowSoftInputMode="stateHidden" /> +        <activity              android:name=".ui.ViewKeyActivity"              android:configChanges="orientation|screenSize|keyboardHidden|keyboard"              android:label="@string/title_key_details" diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index 47265c3aa..0a49cb629 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -46,6 +46,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable {      private String mExtraData;      private String mQuery;      private String mOrigin; +    private Integer mHashCode = null;      private boolean mSelected; @@ -98,6 +99,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable {          }      }; +    public int hashCode() { +        if (mHashCode != null) { +            return mHashCode; +        } +        return super.hashCode(); +    } +      public String getKeyIdHex() {          return mKeyIdHex;      } @@ -240,6 +248,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {          mSecretKey = ring.isSecret();          UncachedPublicKey key = ring.getPublicKey(); +        mHashCode = key.hashCode(); +          mPrimaryUserId = key.getPrimaryUserId();          mUserIds = key.getUnorderedUserIds(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index 2f14d77a8..29ab06264 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -17,18 +17,17 @@  package org.sufficientlysecure.keychain.keyimport; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; +import com.textuality.keybase.lib.KeybaseException; +import com.textuality.keybase.lib.Match; +import com.textuality.keybase.lib.Search; +import com.textuality.keybase.lib.User; +  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.util.JWalk;  import org.sufficientlysecure.keychain.util.Log; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder;  import java.util.ArrayList; +import java.util.List;  public class KeybaseKeyserver extends Keyserver {      public static final String ORIGIN = "keybase:keybase.io"; @@ -43,29 +42,14 @@ public class KeybaseKeyserver extends Keyserver {              // cut off "0x" if a user is searching for a key id              query = query.substring(2);          } +        mQuery = query; -        JSONObject fromQuery = getFromKeybase("_/api/1.0/user/autocomplete.json?q=", query);          try { - -            JSONArray matches = JWalk.getArray(fromQuery, "completions"); -            for (int i = 0; i < matches.length(); i++) { -                JSONObject match = matches.getJSONObject(i); - -                // only list them if they have a key -                if (JWalk.optObject(match, "components", "key_fingerprint") != null) { -                    // TODO: needed anymore? -//                    String keybaseId = JWalk.getString(match, "components", "username", "val"); -//                    String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); -//                    fingerprint = fingerprint.replace(" ", "").toUpperCase(); -//                    if (keybaseId.equals(query) || fingerprint.startsWith(query.toUpperCase())) { -//                        results.add(makeEntry(match)); -//                    } else { -//                        results.add(makeEntry(match)); -//                    } -                    results.add(makeEntry(match)); -                } +            Iterable<Match> matches = Search.search(query); +            for (Match match : matches) { +                results.add(makeEntry(match));              } -        } catch (Exception e) { +        } catch (KeybaseException e) {              Log.e(Constants.TAG, "keybase result parsing error", e);              throw new QueryFailedException("Unexpected structure in keybase search result: " + e.getMessage());          } @@ -73,103 +57,43 @@ public class KeybaseKeyserver extends Keyserver {          return results;      } -    private JSONObject getUser(String keybaseId) throws QueryFailedException { -        try { -            return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseId); -        } catch (Exception e) { -            String detail = ""; -            if (keybaseId != null) { -                detail = ". Query was for user '" + keybaseId + "'"; -            } -            throw new QueryFailedException(e.getMessage() + detail); -        } -    } - -    private ImportKeysListEntry makeEntry(JSONObject match) throws QueryFailedException, JSONException { - +    private ImportKeysListEntry makeEntry(Match match) throws KeybaseException {          final ImportKeysListEntry entry = new ImportKeysListEntry();          entry.setQuery(mQuery);          entry.setOrigin(ORIGIN); -        String keybaseId = JWalk.getString(match, "components", "username", "val"); -        String fullName = JWalk.getString(match, "components", "full_name", "val"); -        String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); -        fingerprint = fingerprint.replace(" ", "").toUpperCase(); // not strictly necessary but doesn't hurt +        String username = match.getUsername(); +        String fullName = match.getFullName(); +        String fingerprint = match.getFingerprint();          entry.setFingerprintHex(fingerprint); -        entry.setKeyIdHex("0x" + fingerprint.substring(Math.max(0, fingerprint.length() - 16))); +        entry.setKeyIdHex("0x" + match.getKeyID());          // store extra info, so we can query for the keybase id directly -        entry.setExtraData(keybaseId); +        entry.setExtraData(username); -        final int algorithmId = JWalk.getInt(match, "components", "key_fingerprint", "algo"); +        final int algorithmId = match.getAlgorithmId();          entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); -        final int bitStrength = JWalk.getInt(match, "components", "key_fingerprint", "nbits"); +        final int bitStrength = match.getBitStrength();          entry.setBitStrength(bitStrength);          ArrayList<String> userIds = new ArrayList<String>(); -        String name = fullName + " <keybase.io/" + keybaseId + ">"; +        String name = fullName + " <keybase.io/" + username + ">";          userIds.add(name); -        try { -            userIds.add("github.com/" + JWalk.getString(match, "components", "github", "val")); -        } catch (JSONException e) { -            // ignore -        } -        try { -            userIds.add("twitter.com/" + JWalk.getString(match, "components", "twitter", "val")); -        } catch (JSONException e) { -            // ignore -        } -        try { -            JSONArray array = JWalk.getArray(match, "components", "websites"); -            JSONObject website = array.getJSONObject(0); -            userIds.add(JWalk.getString(website, "val")); -        } catch (JSONException e) { -            // ignore + +        List<String> proofLabels = match.getProofLabels(); +        for (String proofLabel : proofLabels) { +            userIds.add(proofLabel);          }          entry.setUserIds(userIds);          entry.setPrimaryUserId(name);          return entry;      } -    private JSONObject getFromKeybase(String path, String query) throws QueryFailedException { -        try { -            String url = "https://keybase.io/" + path + URLEncoder.encode(query, "utf8"); -            Log.d(Constants.TAG, "keybase query: " + url); - -            URL realUrl = new URL(url); -            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); -            conn.setConnectTimeout(5000); // TODO: Reasonable values for keybase -            conn.setReadTimeout(25000); -            conn.connect(); -            int response = conn.getResponseCode(); -            if (response >= 200 && response < 300) { -                String text = readAll(conn.getInputStream(), conn.getContentEncoding()); -                try { -                    JSONObject json = new JSONObject(text); -                    if (JWalk.getInt(json, "status", "code") != 0) { -                        throw new QueryFailedException("Keybase.io query failed: " + path + "?" + -                                query); -                    } -                    return json; -                } catch (JSONException e) { -                    throw new QueryFailedException("Keybase.io query returned broken JSON"); -                } -            } else { -                String message = readAll(conn.getErrorStream(), conn.getContentEncoding()); -                throw new QueryFailedException("Keybase.io query error (status=" + response + -                        "): " + message); -            } -        } catch (Exception e) { -            throw new QueryFailedException("Keybase.io query error"); -        } -    } -      @Override      public String get(String id) throws QueryFailedException {          try { -            JSONObject user = getUser(id); -            return JWalk.getString(user, "them", "public_keys", "primary", "bundle"); -        } catch (Exception e) { +            return User.keyForUsername(id); +        } catch (KeybaseException e) {              throw new QueryFailedException(e.getMessage());          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java index fdf561aaf..066c51a13 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -27,7 +27,9 @@ public class ParcelableKeyRing implements Parcelable {      public static final Creator<ParcelableKeyRing> CREATOR = new Creator<ParcelableKeyRing>() {          public ParcelableKeyRing createFromParcel(final Parcel source) { -            return new ParcelableKeyRing(source.createByteArray()); +            byte[] bytes = source.createByteArray(); +            String expectedFingerprint = source.readString(); +            return new ParcelableKeyRing(bytes, expectedFingerprint);          }          public ParcelableKeyRing[] newArray(final int size) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java new file mode 100644 index 000000000..68312dca3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java @@ -0,0 +1,19 @@ +package org.sufficientlysecure.keychain.pgp; + +/** + * No-op implementation of Progressable + */ +public class NullProgressable implements Progressable { + +    @Override +    public void setProgress(String message, int current, int total) { +    } + +    @Override +    public void setProgress(int resourceId, int current, int total) { +    } + +    @Override +    public void setProgress(int current, int total) { +    } +} 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 7544f7b86..c50d92f7a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -139,6 +139,8 @@ public class PgpImportExport {                  String expectedFp = entry.getExpectedFingerprint();                  if(expectedFp != null) {                      if(!PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()).equals(expectedFp)) { +                        Log.d(Constants.TAG, "fingerprint: " + PgpKeyHelper.convertFingerprintToHex(key.getFingerprint())); +                        Log.d(Constants.TAG, "expected fingerprint: " + expectedFp);                          Log.e(Constants.TAG, "Actual key fingerprint is not the same as expected!");                          badKeys += 1;                          continue; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 291b07639..1ba028006 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -104,7 +104,7 @@ public class PgpKeyHelper {       * @return       */      public static String convertFingerprintToHex(byte[] fingerprint) { -        String hexString = Hex.toHexString(fingerprint); +        String hexString = Hex.toHexString(fingerprint).toLowerCase(Locale.US);          return hexString;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index c590200ee..9b3e5bc54 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -481,58 +481,4 @@ public class PgpKeyOperation {      } - -    /** -     * Certify the given pubkeyid with the given masterkeyid. -     * -     * @param certificationKey Certifying key -     * @param publicKey        public key to certify -     * @param userIds          User IDs to certify, must not be null or empty -     * @param passphrase       Passphrase of the secret key -     * @return A keyring with added certifications -     */ -    public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey, -                                   List<String> userIds, String passphrase) -            throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, -            PGPException, SignatureException { - -        // create a signatureGenerator from the supplied masterKeyId and passphrase -        PGPSignatureGenerator signatureGenerator; -        { - -            if (certificationKey == null) { -                throw new PgpGeneralMsgIdException(R.string.error_no_signature_key); -            } - -            PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( -                    Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); -            PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor); -            if (signaturePrivateKey == null) { -                throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key); -            } - -            // TODO: SHA256 fixed? -            JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( -                    certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) -                    .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - -            signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); -            signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey); -        } - -        { // supply signatureGenerator with a SubpacketVector -            PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); -            PGPSignatureSubpacketVector packetVector = spGen.generate(); -            signatureGenerator.setHashedSubpackets(packetVector); -        } - -        // fetch public key ring, add the certification and return it -        for (String userId : new IterableIterator<String>(userIds.iterator())) { -            PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); -            publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); -        } - -        return publicKey; -    } -  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 955fb90ba..f4e19d21a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,6 +29,7 @@ import android.support.v4.util.LongSparseArray;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.NullProgressable;  import org.sufficientlysecure.keychain.pgp.Progressable;  import org.sufficientlysecure.keychain.pgp.WrappedPublicKey;  import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; @@ -497,13 +498,12 @@ public class ProviderHelper {                  }              } -            mIndent -= 1; -          } catch (IOException e) {              log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC);              Log.e(Constants.TAG, "IOException during import", e); -            mIndent -= 1;              return SaveKeyringResult.RESULT_ERROR; +        } finally { +            mIndent -= 1;          }          try { @@ -522,19 +522,16 @@ public class ProviderHelper {              mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);              log(LogLevel.OK, LogType.MSG_IP_SUCCESS); -            mIndent -= 1;              progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 90, 100);              return result;          } catch (RemoteException e) {              log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX);              Log.e(Constants.TAG, "RemoteException during import", e); -            mIndent -= 1;              return SaveKeyringResult.RESULT_ERROR;          } catch (OperationApplicationException e) {              log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EXC);              Log.e(Constants.TAG, "OperationApplicationException during import", e); -            mIndent -= 1;              return SaveKeyringResult.RESULT_ERROR;          } @@ -580,94 +577,87 @@ public class ProviderHelper {          log(LogLevel.START, LogType.MSG_IS,                  new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) });          mIndent += 1; +        try { -        // Canonicalize this key, to assert a number of assumptions made about it. -        keyRing = keyRing.canonicalize(mLog, mIndent); -        if (keyRing == null) { -            return SaveKeyringResult.RESULT_ERROR; -        } +            // Canonicalize this key, to assert a number of assumptions made about it. +            keyRing = keyRing.canonicalize(mLog, mIndent); +            if (keyRing == null) { +                return SaveKeyringResult.RESULT_ERROR; +            } -        // IF this is successful, it's a secret key -        int result = SaveKeyringResult.SAVED_SECRET; +            // IF this is successful, it's a secret key +            int result = SaveKeyringResult.SAVED_SECRET; -        // save secret keyring -        try { -            ContentValues values = new ContentValues(); -            values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); -            values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); -            // insert new version of this keyRing -            Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); -            if (mContentResolver.insert(uri, values) == null) { -                log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); +            // save secret keyring +            try { +                ContentValues values = new ContentValues(); +                values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); +                values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); +                // insert new version of this keyRing +                Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); +                if (mContentResolver.insert(uri, values) == null) { +                    log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); +                    return SaveKeyringResult.RESULT_ERROR; +                } +            } catch (IOException e) { +                Log.e(Constants.TAG, "Failed to encode key!", e); +                log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC);                  return SaveKeyringResult.RESULT_ERROR;              } -        } catch (IOException e) { -            Log.e(Constants.TAG, "Failed to encode key!", e); -            log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); -            return SaveKeyringResult.RESULT_ERROR; -        } -        { -            Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); +            { +                Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); -            // first, mark all keys as not available -            ContentValues values = new ContentValues(); -            values.put(Keys.HAS_SECRET, 0); -            mContentResolver.update(uri, values, null, null); +                // first, mark all keys as not available +                ContentValues values = new ContentValues(); +                values.put(Keys.HAS_SECRET, 0); +                mContentResolver.update(uri, values, null, null); -            values.put(Keys.HAS_SECRET, 1); -            // then, mark exactly the keys we have available -            log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); -            mIndent += 1; -            Set<Long> available = keyRing.getAvailableSubkeys(); -            for (UncachedPublicKey sub : -                    new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) { -                long id = sub.getKeyId(); -                if(available.contains(id)) { -                    int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", -                            new String[] { Long.toString(id) }); -                    if (upd == 1) { -                        log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[]{ -                                PgpKeyHelper.convertKeyIdToHex(id) -                        }); +                values.put(Keys.HAS_SECRET, 1); +                // then, mark exactly the keys we have available +                log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); +                mIndent += 1; +                Set<Long> available = keyRing.getAvailableSubkeys(); +                for (UncachedPublicKey sub : +                        new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) { +                    long id = sub.getKeyId(); +                    if (available.contains(id)) { +                        int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", +                                new String[]{Long.toString(id)}); +                        if (upd == 1) { +                            log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[]{ +                                    PgpKeyHelper.convertKeyIdToHex(id) +                            }); +                        } else { +                            log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[]{ +                                    PgpKeyHelper.convertKeyIdToHex(id) +                            }); +                        }                      } else { -                        log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[]{ +                        log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, new String[]{                                  PgpKeyHelper.convertKeyIdToHex(id)                          });                      } -                } else { -                    log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, new String[]{ -                            PgpKeyHelper.convertKeyIdToHex(id) -                    });                  } +                mIndent -= 1; + +                // this implicitly leaves all keys which were not in the secret key ring +                // with has_secret = 0              } -            mIndent -= 1; -            // this implicitly leaves all keys which were not in the secret key ring -            // with has_secret = 0 -        } +            log(LogLevel.OK, LogType.MSG_IS_SUCCESS); +            return result; -        log(LogLevel.OK, LogType.MSG_IS_SUCCESS); -        return result; +        } finally { +            mIndent -= 1; +        }      }      @Deprecated      public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { -        return savePublicKeyRing(keyRing, new Progressable() { -            @Override -            public void setProgress(String message, int current, int total) { -            } - -            @Override -            public void setProgress(int resourceId, int current, int total) { -            } - -            @Override -            public void setProgress(int current, int total) { -            } -        }); +        return savePublicKeyRing(keyRing, new NullProgressable());      }      /** Save a public keyring into the database. @@ -749,12 +739,13 @@ public class ProviderHelper {                  }              } -            mIndent -= 1;              return new SaveKeyringResult(result, mLog);          } catch (IOException e) {              log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC);              return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); +        } finally { +            mIndent -= 1;          }      } @@ -844,6 +835,8 @@ public class ProviderHelper {          } catch (IOException e) {              log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC, null);              return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); +        } finally { +            mIndent -= 1;          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index c68b7c189..47a6cab1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -4,7 +4,7 @@ import android.os.Parcel;  import android.os.Parcelable;  import java.io.Serializable; -import java.util.HashMap; +import java.util.ArrayList;  /** This class is a a transferable representation for a collection of changes   * to be done on a keyring. @@ -29,14 +29,14 @@ public class SaveKeyringParcel implements Parcelable {      public String newPassphrase; -    public String[] addUserIds; -    public SubkeyAdd[] addSubKeys; +    public ArrayList<String> addUserIds; +    public ArrayList<SubkeyAdd> addSubKeys; -    public SubkeyChange[] changeSubKeys; +    public ArrayList<SubkeyChange> changeSubKeys;      public String changePrimaryUserId; -    public String[] revokeUserIds; -    public long[] revokeSubKeys; +    public ArrayList<String> revokeUserIds; +    public ArrayList<Long> revokeSubKeys;      public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) {          mMasterKeyId = masterKeyId; @@ -73,14 +73,14 @@ public class SaveKeyringParcel implements Parcelable {          mMasterKeyId = source.readLong();          mFingerprint = source.createByteArray(); -        addUserIds = source.createStringArray(); -        addSubKeys = (SubkeyAdd[]) source.readSerializable(); +        addUserIds = source.createStringArrayList(); +        addSubKeys = (ArrayList<SubkeyAdd>) source.readSerializable(); -        changeSubKeys = (SubkeyChange[]) source.readSerializable(); +        changeSubKeys = (ArrayList<SubkeyChange>) source.readSerializable();          changePrimaryUserId = source.readString(); -        revokeUserIds = source.createStringArray(); -        revokeSubKeys = source.createLongArray(); +        revokeUserIds = source.createStringArrayList(); +        revokeSubKeys = (ArrayList<Long>) source.readSerializable();      }      @Override @@ -88,14 +88,14 @@ public class SaveKeyringParcel implements Parcelable {          destination.writeLong(mMasterKeyId);          destination.writeByteArray(mFingerprint); -        destination.writeStringArray(addUserIds); +        destination.writeStringList(addUserIds);          destination.writeSerializable(addSubKeys);          destination.writeSerializable(changeSubKeys);          destination.writeString(changePrimaryUserId); -        destination.writeStringArray(revokeUserIds); -        destination.writeLongArray(revokeSubKeys); +        destination.writeStringList(revokeUserIds); +        destination.writeSerializable(revokeSubKeys);      }      public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java new file mode 100644 index 000000000..d4bc6c541 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java @@ -0,0 +1,56 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; + +import org.sufficientlysecure.keychain.pgp.NullProgressable; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults; + +/** + * Helper for tests of the Keyring import in ProviderHelper. + */ +public class KeyringTestingHelper { + +    private final Context context; + +    public KeyringTestingHelper(Context robolectricContext) { +        this.context = robolectricContext; +    } + +    public boolean addKeyring() throws Exception { + +        ProviderHelper providerHelper = new ProviderHelper(context); + +//        providerHelper.insertApiApp(new AppSettings("robo-test-package", new byte[]{5, 4, 3, 2, 1})); + +        byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); +        UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data); +        long masterKeyId = ring.getMasterKeyId(); + +        // Should throw an exception; key is not yet saved +        retrieveKeyAndExpectNotFound(providerHelper, masterKeyId); + +        OperationResults.SaveKeyringResult saveKeyringResult = providerHelper.savePublicKeyRing(ring, new NullProgressable()); + +        boolean saveSuccess = saveKeyringResult.success(); + +        // Now re-retrieve the saved key. Should not throw an exception. +        providerHelper.getWrappedPublicKeyRing(masterKeyId); + +        // A different ID should still fail +        retrieveKeyAndExpectNotFound(providerHelper, masterKeyId - 1); + +        return saveSuccess; +    } + +    private void retrieveKeyAndExpectNotFound(ProviderHelper providerHelper, long masterKeyId) { +        try { +            providerHelper.getWrappedPublicKeyRing(masterKeyId); +            throw new AssertionError("Was expecting the previous call to fail!"); +        } catch (ProviderHelper.NotFoundException expectedException) { +            // good +        } +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java new file mode 100644 index 000000000..1ab5878cc --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java @@ -0,0 +1,49 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; + +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.InputData; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * For functional tests of PgpDecryptVerify + */ +public class PgpVerifyTestingHelper { + +    private final Context context; + +    public PgpVerifyTestingHelper(Context robolectricContext) { +        this.context = robolectricContext; +    } + +    public int doTestFile(String testFileName) throws Exception { +        ProviderHelper providerHelper = new ProviderHelperStub(context); + +        PgpDecryptVerify.PassphraseCache passphraseCache = new PgpDecryptVerify.PassphraseCache() { +            public String getCachedPassphrase(long masterKeyId) { +                return "I am a passphrase"; +            } +        }; + +        byte[] sampleInputBytes = TestDataUtil.readFully(getClass().getResourceAsStream(testFileName)); + +        InputStream sampleInput = new ByteArrayInputStream(sampleInputBytes); + +        InputData data = new InputData(sampleInput, sampleInputBytes.length); +        OutputStream outStream = new ByteArrayOutputStream(); + +        PgpDecryptVerify verify = new PgpDecryptVerify.Builder(providerHelper, passphraseCache, data, outStream).build(); +        PgpDecryptVerifyResult result = verify.execute(); + +        return result.getSignatureResult().getStatus(); +    } + + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java new file mode 100644 index 000000000..c6d834bf9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java @@ -0,0 +1,22 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; +import android.net.Uri; + +import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; + +/** + * Created by art on 21/06/14. + */ +class ProviderHelperStub extends ProviderHelper { +    public ProviderHelperStub(Context context) { +        super(context); +    } + +    @Override +    public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException { +        byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); +        return new WrappedPublicKeyRing(data, false, 0); +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java new file mode 100644 index 000000000..06dc08eab --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java @@ -0,0 +1,25 @@ +package org.sufficientlysecure.keychain.testsupport; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Misc support functions. Would just use Guava / Apache Commons but + * avoiding extra dependencies. + */ +public class TestDataUtil { +    public static byte[] readFully(InputStream input) { +        byte[] buffer = new byte[8192]; +        int bytesRead; +        ByteArrayOutputStream output = new ByteArrayOutputStream(); +        try { +            while ((bytesRead = input.read(buffer)) != -1) { +                output.write(buffer, 0, bytesRead); +            } +        } catch (IOException e) { +            throw new RuntimeException(e); +        } +        return output.toByteArray(); +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java new file mode 100644 index 000000000..1cc0f9a95 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java @@ -0,0 +1,7 @@ +/** + * Test support classes. + * This is only in main code because of gradle-Android Studio-robolectric issues. Having + * classes in main code means IDE autocomplete, class detection, etc., all works. + * TODO Move into test package when possible + */ +package org.sufficientlysecure.keychain.testsupport; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityNew.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityNew.java new file mode 100644 index 000000000..f2b5e68eb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityNew.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.ui; + +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.View; +import android.view.View.OnClickListener; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.remote.ui.AccountsListFragment; +import org.sufficientlysecure.keychain.util.Log; + +public class EditKeyActivityNew extends ActionBarActivity { + +    private Uri mDataUri; + +    private EditKeyFragment mEditKeyFragment; + +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); + +        setContentView(R.layout.edit_key_activity_new); + +//        // Inflate a "Done"/"Cancel" custom action bar view +//        ActionBarHelper.setTwoButtonView(getSupportActionBar(), +//                R.string.btn_save, R.drawable.ic_action_save, +//                new OnClickListener() { +//                    @Override +//                    public void onClick(View v) { +//                        // Save +// +//                    } +//                }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, +//                new OnClickListener() { +//                    @Override +//                    public void onClick(View v) { +//                        // Cancel +// +//                    } +//                } +//        ); + +        Uri dataUri = getIntent().getData(); +        if (dataUri == null) { +            Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); +            finish(); +            return; +        } + +        loadFragment(savedInstanceState, dataUri); +    } + +    private void loadFragment(Bundle savedInstanceState, Uri dataUri) { +        // However, if we're being restored from a previous state, +        // then we don't need to do anything and should return or else +        // we could end up with overlapping fragments. +        if (savedInstanceState != null) { +            return; +        } + +        // Create an instance of the fragment +        mEditKeyFragment = EditKeyFragment.newInstance(dataUri); + +        // Add the fragment to the 'fragment_container' FrameLayout +        // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! +        getSupportFragmentManager().beginTransaction() +                .replace(R.id.edit_key_fragment_container, mEditKeyFragment) +                .commitAllowingStateLoss(); +        // do it immediately! +        getSupportFragmentManager().executePendingTransactions(); +    } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java new file mode 100644 index 000000000..9231ce142 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.ui; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v7.app.ActionBarActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ListView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter; +import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter; +import org.sufficientlysecure.keychain.util.Log; + +public class EditKeyFragment extends LoaderFragment implements +        LoaderManager.LoaderCallbacks<Cursor> { + +    public static final String ARG_DATA_URI = "uri"; + +    private ListView mUserIds; +    private ListView mKeys; + +    private static final int LOADER_ID_USER_IDS = 0; +    private static final int LOADER_ID_KEYS = 1; + +    private ViewKeyUserIdsAdapter mUserIdsAdapter; +    private ViewKeyKeysAdapter mKeysAdapter; + +    private Uri mDataUri; + +    /** +     * Creates new instance of this fragment +     */ +    public static EditKeyFragment newInstance(Uri dataUri) { +        EditKeyFragment frag = new EditKeyFragment(); + +        Bundle args = new Bundle(); +        args.putParcelable(ARG_DATA_URI, dataUri); + +        frag.setArguments(args); + +        return frag; +    } + +    @Override +    public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { +        View root = super.onCreateView(inflater, superContainer, savedInstanceState); +        View view = inflater.inflate(R.layout.edit_key_fragment, getContainer()); + +        mUserIds = (ListView) view.findViewById(R.id.edit_key_user_ids); +        mKeys = (ListView) view.findViewById(R.id.edit_key_keys); +//        mActionEdit = view.findViewById(R.id.view_key_action_edit); + +        return root; +    } + +    @Override +    public void onActivityCreated(Bundle savedInstanceState) { +        super.onActivityCreated(savedInstanceState); + + +        // Inflate a "Done"/"Cancel" custom action bar view +        ActionBarHelper.setTwoButtonView(((ActionBarActivity) getActivity()).getSupportActionBar(), +                R.string.btn_save, R.drawable.ic_action_save, +                new OnClickListener() { +                    @Override +                    public void onClick(View v) { +                        // Save +                        save(); +                    } +                }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, +                new OnClickListener() { +                    @Override +                    public void onClick(View v) { +                        // Cancel +                        getActivity().finish(); +                    } +                } +        ); + +        Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); +        if (dataUri == null) { +            Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); +            getActivity().finish(); +            return; +        } + +        loadData(dataUri); +    } + +    private void loadData(Uri dataUri) { +        mDataUri = dataUri; + +        Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + +//        mActionEncrypt.setOnClickListener(new View.OnClickListener() { +//            @Override +//            public void onClick(View v) { +//                encrypt(mDataUri); +//            } +//        }); + + +        mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0); +        mUserIds.setAdapter(mUserIdsAdapter); +        mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0); +        mKeys.setAdapter(mKeysAdapter); + +        // Prepare the loaders. Either re-connect with an existing ones, +        // or start new ones. +        getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); +        getLoaderManager().initLoader(LOADER_ID_KEYS, null, this); +    } + +    public Loader<Cursor> onCreateLoader(int id, Bundle args) { +        setContentShown(false); + +        switch (id) { +            case LOADER_ID_USER_IDS: { +                Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri); +                return new CursorLoader(getActivity(), baseUri, +                        ViewKeyUserIdsAdapter.USER_IDS_PROJECTION, null, null, null); +            } + +            case LOADER_ID_KEYS: { +                Uri baseUri = KeychainContract.Keys.buildKeysUri(mDataUri); +                return new CursorLoader(getActivity(), baseUri, +                        ViewKeyKeysAdapter.KEYS_PROJECTION, null, null, null); +            } + +            default: +                return null; +        } +    } + +    public void onLoadFinished(Loader<Cursor> loader, Cursor data) { +        // Swap the new cursor in. (The framework will take care of closing the +        // old cursor once we return.) +        switch (loader.getId()) { +            case LOADER_ID_USER_IDS: +                mUserIdsAdapter.swapCursor(data); +                break; + +            case LOADER_ID_KEYS: +                mKeysAdapter.swapCursor(data); +                break; + +        } +        setContentShown(true); +    } + +    /** +     * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. +     * We need to make sure we are no longer using it. +     */ +    public void onLoaderReset(Loader<Cursor> loader) { +        switch (loader.getId()) { +            case LOADER_ID_USER_IDS: +                mUserIdsAdapter.swapCursor(null); +                break; +            case LOADER_ID_KEYS: +                mKeysAdapter.swapCursor(null); +                break; +        } +    } + +    private void save() { +        getActivity().finish(); +        // TODO +    } + + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index e829df7a0..f7455905d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -44,6 +44,7 @@ import com.github.johnpersano.supertoasts.util.Style;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.Preferences;  import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;  import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;  import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -88,8 +89,6 @@ public class ImportKeysActivity extends ActionBarActivity {      // view      private ImportKeysListFragment mListFragment; -    private String[] mNavigationStrings; -    private Fragment mCurrentFragment;      private View mImportButton;      private ViewPager mViewPager;      private SlidingTabLayout mSlidingTabLayout; @@ -97,12 +96,12 @@ public class ImportKeysActivity extends ActionBarActivity {      public static final int VIEW_PAGER_HEIGHT = 64; // dp -    private static final int NAV_SERVER = 0; -    private static final int NAV_QR_CODE = 1; -    private static final int NAV_FILE = 2; -    private static final int NAV_KEYBASE = 3; +    private static final int TAB_KEYSERVER = 0; +    private static final int TAB_QR_CODE = 1; +    private static final int TAB_FILE = 2; +    private static final int TAB_KEYBASE = 3; -    private int mSwitchToTab = NAV_SERVER; +    private int mSwitchToTab = TAB_KEYSERVER;      @Override      protected void onCreate(Bundle savedInstanceState) { @@ -121,12 +120,6 @@ public class ImportKeysActivity extends ActionBarActivity {              }          }); -        mNavigationStrings = getResources().getStringArray(R.array.import_action_list); - -        // TODO: add actionbar button for this action? -//        if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { -//        } -          handleActions(savedInstanceState, getIntent());      } @@ -142,26 +135,31 @@ public class ImportKeysActivity extends ActionBarActivity {          if (Intent.ACTION_VIEW.equals(action)) {              // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) -            // override action to delegate it to Keychain's ACTION_IMPORT_KEY +            // delegate action to ACTION_IMPORT_KEY              action = ACTION_IMPORT_KEY;          } -        Bundle serverBundle = null; -        boolean serverOnly = false;          if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {              /* Scanning a fingerprint directly with Barcode Scanner */ -            loadFromFingerprintUri(savedInstanceState, dataUri); -        } else if (ACTION_IMPORT_KEY.equals(action)) { +            // delegate action to ACTION_IMPORT_KEY_FROM_KEYSERVER +            String fingerprint = getFingerprintFromUri(dataUri); +            action = ACTION_IMPORT_KEY_FROM_KEYSERVER; +            extras.putString(EXTRA_FINGERPRINT, fingerprint); +        } + +        Bundle serverBundle = null; +        boolean serverOnly = false; +        if (ACTION_IMPORT_KEY.equals(action)) {              /* Keychain's own Actions */              // display file fragment -            mViewPager.setCurrentItem(NAV_FILE); +            mViewPager.setCurrentItem(TAB_FILE);              if (dataUri != null) {                  // action: directly load data                  startListFragment(savedInstanceState, null, dataUri, null);              } else if (extras.containsKey(EXTRA_KEY_BYTES)) { -                byte[] importData = intent.getByteArrayExtra(EXTRA_KEY_BYTES); +                byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES);                  // action: directly load data                  startListFragment(savedInstanceState, importData, null, null); @@ -180,7 +178,7 @@ public class ImportKeysActivity extends ActionBarActivity {                  if (extras.containsKey(EXTRA_QUERY)) {                      query = extras.getString(EXTRA_QUERY);                  } else if (extras.containsKey(EXTRA_KEY_ID)) { -                    long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); +                    long keyId = extras.getLong(EXTRA_KEY_ID, 0);                      if (keyId != 0) {                          query = PgpKeyHelper.convertKeyIdToHex(keyId);                      } @@ -190,7 +188,7 @@ public class ImportKeysActivity extends ActionBarActivity {                      // display keyserver fragment with query                      serverBundle = new Bundle();                      serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); -                    mSwitchToTab = NAV_SERVER; +                    mSwitchToTab = TAB_KEYSERVER;                      // action: search immediately                      startListFragment(savedInstanceState, null, null, query); @@ -204,7 +202,7 @@ public class ImportKeysActivity extends ActionBarActivity {                   * if the right key has been downloaded                   */ -                String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT); +                String fingerprint = extras.getString(EXTRA_FINGERPRINT);                  if (isFingerprintValid(fingerprint)) {                      String query = "0x" + fingerprint; @@ -212,8 +210,9 @@ public class ImportKeysActivity extends ActionBarActivity {                      serverBundle = new Bundle();                      serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query);                      serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); +                    // display server tab only                      serverOnly = true; -                    mSwitchToTab = NAV_SERVER; +                    mSwitchToTab = TAB_KEYSERVER;                      // action: search immediately                      startListFragment(savedInstanceState, null, null, query); @@ -227,7 +226,7 @@ public class ImportKeysActivity extends ActionBarActivity {              }          } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {              // NOTE: this only displays the appropriate fragment, no actions are taken -            mSwitchToTab = NAV_FILE; +            mSwitchToTab = TAB_FILE;              // no immediate actions!              startListFragment(savedInstanceState, null, null, null); @@ -235,20 +234,20 @@ public class ImportKeysActivity extends ActionBarActivity {              // also exposed in AndroidManifest              // NOTE: this only displays the appropriate fragment, no actions are taken -            mSwitchToTab = NAV_QR_CODE; +            mSwitchToTab = TAB_QR_CODE;              // no immediate actions!              startListFragment(savedInstanceState, null, null, null);          } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) {              // NOTE: this only displays the appropriate fragment, no actions are taken -            mSwitchToTab = NAV_QR_CODE; +            mSwitchToTab = TAB_QR_CODE;              // no immediate actions!              startListFragment(savedInstanceState, null, null, null);          } else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) {              // NOTE: this only displays the appropriate fragment, no actions are taken -            mSwitchToTab = NAV_KEYBASE; +            mSwitchToTab = TAB_KEYBASE;              // no immediate actions!              startListFragment(savedInstanceState, null, null, null); @@ -273,6 +272,8 @@ public class ImportKeysActivity extends ActionBarActivity {              @Override              public void onPageSelected(int position) { +                // cancel loader and clear list +                mListFragment.destroyLoader();              }              @Override @@ -329,23 +330,45 @@ public class ImportKeysActivity extends ActionBarActivity {          return OtherHelper.pxToDp(this, params.height);      } -    public void loadFromFingerprintUri(Bundle savedInstanceState, Uri dataUri) { +    private String getFingerprintFromUri(Uri dataUri) {          String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH); -          Log.d(Constants.TAG, "fingerprint: " + fingerprint); +        return fingerprint; +    } + +    public void loadFromFingerprintUri(Uri dataUri) { +        String query = "0x" + getFingerprintFromUri(dataUri); -        // TODO: reload fragment when coming from qr code! -//        loadFromFingerprint(savedInstanceState, fingerprint); +        // setCurrentItem does not work directly after onResume (from qr code scanner) +        // see http://stackoverflow.com/q/19316729 +        // so, reset adapter completely! +        if (mViewPager.getAdapter() != null) +            mViewPager.setAdapter(null); +        mViewPager.setAdapter(mTabsAdapter); +        mViewPager.setCurrentItem(TAB_KEYSERVER); +        ImportKeysServerFragment f = (ImportKeysServerFragment) +                getActiveFragment(mViewPager, TAB_KEYSERVER); -//        String query = "0x" + fingerprint; -// -//        // display keyserver fragment with query -//        Bundle serverBundle = new Bundle(); -//        serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); -//        serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); -// -//        return serverBundle; +        // TODO: Currently it simply uses keyserver nr 0 +        String keyserver = Preferences.getPreferences(ImportKeysActivity.this) +                .getKeyServers()[0]; + +        // set fields of ImportKeysServerFragment +        f.setQueryAndKeyserver(query, keyserver); +        // search directly +        loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); +    } + +    // http://stackoverflow.com/a/9293207 +    public Fragment getActiveFragment(ViewPager container, int position) { +        String name = makeFragmentName(container.getId(), position); +        return getSupportFragmentManager().findFragmentByTag(name); +    } + +    // http://stackoverflow.com/a/9293207 +    private static String makeFragmentName(int viewId, int index) { +        return "android:switcher:" + viewId + ":" + index;      }      private boolean isFingerprintValid(String fingerprint) { @@ -372,7 +395,13 @@ public class ImportKeysActivity extends ActionBarActivity {          boolean result = super.onTouchEvent(event);          if (!result) { -            mViewPager.onTouchEvent(event); +            try { +                mViewPager.onTouchEvent(event); +            } catch (IllegalArgumentException e) { +                // workaround for Android bug? +                // http://stackoverflow.com/q/16459196 +                Log.d(Constants.TAG, "Workaround: Catched IllegalArgumentException"); +            }          }          return result; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index f24cb379e..cb53647f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -81,7 +81,7 @@ public class ImportKeysFileFragment extends Fragment {                  if (clipboardText != null) {                      sendText = clipboardText.toString();                      if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { -                        mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); +                        mImportActivity.loadFromFingerprintUri(Uri.parse(sendText));                          return;                      }                  } 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 cba6dd78f..84fd513a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -24,7 +24,9 @@ import android.support.v4.app.ListFragment;  import android.support.v4.app.LoaderManager;  import android.support.v4.content.Loader;  import android.support.v4.util.LongSparseArray; +import android.view.MotionEvent;  import android.view.View; +import android.view.View.OnTouchListener;  import android.widget.ListView;  import org.sufficientlysecure.keychain.Constants; @@ -77,7 +79,7 @@ public class ImportKeysListFragment extends ListFragment implements      public ArrayList<ParcelableKeyRing> getSelectedData() {          ArrayList<ParcelableKeyRing> result = new ArrayList<ParcelableKeyRing>();          for (ImportKeysListEntry entry : getSelectedEntries()) { -            result.add(mCachedKeyData.get(entry.getKeyId())); +            result.add(mCachedKeyData.get(entry.hashCode()));          }          return result;      } @@ -149,19 +151,30 @@ public class ImportKeysListFragment extends ListFragment implements          mAdapter = new ImportKeysAdapter(mActivity);          setListAdapter(mAdapter); -        if (getArguments().containsKey(ARG_DATA_URI) || getArguments().containsKey(ARG_BYTES)) { -            Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); -            byte[] bytes = getArguments().getByteArray(ARG_BYTES); +        Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); +        byte[] bytes = getArguments().getByteArray(ARG_BYTES); +        String query = getArguments().getString(ARG_SERVER_QUERY); + +        if (dataUri != null || bytes != null) {              mLoaderState = new BytesLoaderState(bytes, dataUri); -        } else if (getArguments().containsKey(ARG_SERVER_QUERY)) { -            String query = getArguments().getString(ARG_SERVER_QUERY); -            // TODO: this is used when scanning QR Code or updating a key. +        } else if (query != null) { +            // TODO: this is used when updating a key.              // Currently it simply uses keyserver nr 0              String keyserver = Preferences.getPreferences(getActivity())                      .getKeyServers()[0];              mLoaderState = new KeyserverLoaderState(query, keyserver);          } +        getListView().setOnTouchListener(new OnTouchListener() { +            @Override +            public boolean onTouch(View v, MotionEvent event) { +                if (!mAdapter.isEmpty()) { +                    mActivity.onTouchEvent(event); +                } +                return false; +            } +        }); +          restartLoaders();      } @@ -184,27 +197,34 @@ public class ImportKeysListFragment extends ListFragment implements          restartLoaders();      } +    public void destroyLoader() { +        if (getLoaderManager().getLoader(LOADER_ID_BYTES) != null) { +            getLoaderManager().destroyLoader(LOADER_ID_BYTES); +        } +        if (getLoaderManager().getLoader(LOADER_ID_SERVER_QUERY) != null) { +            getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); +        } +        if (getLoaderManager().getLoader(LOADER_ID_KEYBASE) != null) { +            getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); +        } +        setListShown(true); +    } +      private void restartLoaders() {          if (mLoaderState instanceof BytesLoaderState) {              // Start out with a progress indicator.              setListShown(false);              getLoaderManager().restartLoader(LOADER_ID_BYTES, null, this); -            getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); -            getLoaderManager().destroyLoader(LOADER_ID_KEYBASE);          } else if (mLoaderState instanceof KeyserverLoaderState) {              // Start out with a progress indicator.              setListShown(false); -            getLoaderManager().destroyLoader(LOADER_ID_BYTES);              getLoaderManager().restartLoader(LOADER_ID_SERVER_QUERY, null, this); -            getLoaderManager().destroyLoader(LOADER_ID_KEYBASE);          } else if (mLoaderState instanceof KeybaseLoaderState) {              // Start out with a progress indicator.              setListShown(false); -            getLoaderManager().destroyLoader(LOADER_ID_BYTES); -            getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY);              getLoaderManager().restartLoader(LOADER_ID_KEYBASE, null, this);          }      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java index 5766fc189..5f54daa3c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -132,7 +132,7 @@ public class ImportKeysQrCodeFragment extends Fragment {      }      public void importFingerprint(Uri dataUri) { -        mImportActivity.loadFromFingerprintUri(null, dataUri); +        mImportActivity.loadFromFingerprintUri(dataUri);      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index c7467d789..d339bc132 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -40,7 +40,7 @@ import org.sufficientlysecure.keychain.util.Log;  public class ImportKeysServerFragment extends Fragment {      public static final String ARG_QUERY = "query"; -    public static final String ARG_KEY_SERVER = "key_server"; +    public static final String ARG_KEYSERVER = "keyserver";      public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";      private ImportKeysActivity mImportActivity; @@ -55,12 +55,12 @@ public class ImportKeysServerFragment extends Fragment {      /**       * Creates new instance of this fragment       */ -    public static ImportKeysServerFragment newInstance(String query, String keyServer) { +    public static ImportKeysServerFragment newInstance(String query, String keyserver) {          ImportKeysServerFragment frag = new ImportKeysServerFragment();          Bundle args = new Bundle();          args.putString(ARG_QUERY, query); -        args.putString(ARG_KEY_SERVER, keyServer); +        args.putString(ARG_KEYSERVER, keyserver);          frag.setArguments(args); @@ -149,12 +149,12 @@ public class ImportKeysServerFragment extends Fragment {                  Log.d(Constants.TAG, "query: " + query);              } -            if (getArguments().containsKey(ARG_KEY_SERVER)) { -                String keyServer = getArguments().getString(ARG_KEY_SERVER); -                int keyServerPos = mServerAdapter.getPosition(keyServer); -                mServerSpinner.setSelection(keyServerPos); +            if (getArguments().containsKey(ARG_KEYSERVER)) { +                String keyserver = getArguments().getString(ARG_KEYSERVER); +                int keyserverPos = mServerAdapter.getPosition(keyserver); +                mServerSpinner.setSelection(keyserverPos); -                Log.d(Constants.TAG, "keyServer: " + keyServer); +                Log.d(Constants.TAG, "keyserver: " + keyserver);              }              if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) { @@ -174,4 +174,10 @@ public class ImportKeysServerFragment extends Fragment {          mImportActivity.loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver));      } +    public void setQueryAndKeyserver(String query, String keyserver) { +        mQueryEditText.setText(query, TextView.BufferType.EDITABLE); +        int keyServerPos = mServerAdapter.getPosition(keyserver); +        mServerSpinner.setSelection(keyServerPos); +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index a0d449195..b8386b144 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -1,13 +1,28 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.ui;  import android.os.Bundle; -import android.support.v4.view.GestureDetectorCompat;  import android.support.v7.app.ActionBarActivity; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.MotionEvent; +import android.view.View;  import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper;  public class LogDisplayActivity extends ActionBarActivity { @@ -15,6 +30,18 @@ public class LogDisplayActivity extends ActionBarActivity {      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState); +        // Inflate a "Done" custom action bar +        ActionBarHelper.setOneButtonView(getSupportActionBar(), +                R.string.btn_okay, R.drawable.ic_action_done, +                new View.OnClickListener() { +                    @Override +                    public void onClick(View v) { +                        // "Done" +                        finish(); +                    } +                } +        ); +          setContentView(R.layout.log_display_activity);      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 496e98c18..e42d9d00b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.ui;  import android.content.Context; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java index f4a44f526..e01a0140c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java @@ -83,17 +83,11 @@ public class ViewKeyKeysFragment extends LoaderFragment implements          getLoaderManager().initLoader(0, null, this);      } -    static final String[] KEYS_PROJECTION = new String[] { -            Keys._ID, -            Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.HAS_SECRET, -            Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED, -            Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT -    }; -      public Loader<Cursor> onCreateLoader(int id, Bundle args) {          setContentShown(false);          Uri baseUri = Keys.buildKeysUri(mDataUri); -        return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, null); +        return new CursorLoader(getActivity(), baseUri, +                ViewKeyKeysAdapter.KEYS_PROJECTION, null, null, null);      }      public void onLoadFinished(Loader<Cursor> loader, Cursor data) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index ae7cef70c..bd29f3820 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -49,6 +49,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements      public static final String ARG_DATA_URI = "uri";      private View mActionEdit; +    private View mActionEditNew;      private View mActionEditDivider;      private View mActionEncrypt;      private View mActionCertify; @@ -73,6 +74,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements          mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids);          mActionEdit = view.findViewById(R.id.view_key_action_edit); +        mActionEditNew = view.findViewById(R.id.view_key_action_edit_new);          mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider);          mActionEncrypt = view.findViewById(R.id.view_key_action_encrypt);          mActionCertify = view.findViewById(R.id.view_key_action_certify); @@ -116,6 +118,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements                  editKey(mDataUri);              }          }); +        mActionEditNew.setOnClickListener(new View.OnClickListener() { +            public void onClick(View view) { +                editKeyNew(mDataUri); +            } +        });          mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0);          mUserIds.setAdapter(mUserIdsAdapter); @@ -256,4 +263,12 @@ public class ViewKeyMainFragment extends LoaderFragment implements          startActivityForResult(editIntent, 0);      } +    private void editKeyNew(Uri dataUri) { +        Intent editIntent = new Intent(getActivity(), EditKeyActivityNew.class); +//        editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); +        editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); +        editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); +        startActivityForResult(editIntent, 0); +    } +  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 233b1fca8..a44d32e5b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -170,7 +170,7 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {          } else {              holder.userIdsList.setVisibility(View.VISIBLE); -            // clear view from holder +            // destroyLoader view from holder              holder.userIdsList.removeAllViews();              Iterator<String> it = entry.getUserIds().iterator(); 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 c2712e89e..99f959035 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 @@ -138,7 +138,7 @@ public class ImportKeysListLoader                  for(UncachedKeyRing key : rings) {                      ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key);                      mData.add(item); -                    mParcelableRings.put(key.getMasterKeyId(), new ParcelableKeyRing(key.getEncoded())); +                    mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(key.getEncoded()));                      isEmpty = false;                  }              } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java index 4eb6f158b..d62c2db43 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java @@ -47,7 +47,6 @@ public class ImportKeysListServerLoader      @Override      public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { -          mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);          if (mServerQuery == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index f4942a2a0..dae287bbc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -53,6 +53,22 @@ public class ViewKeyKeysAdapter extends CursorAdapter {      private ColorStateList mDefaultTextColor; +    public static final String[] KEYS_PROJECTION = new String[] { +            Keys._ID, +            Keys.KEY_ID, +            Keys.RANK, +            Keys.ALGORITHM, +            Keys.KEY_SIZE, +            Keys.HAS_SECRET, +            Keys.CAN_CERTIFY, +            Keys.CAN_ENCRYPT, +            Keys.CAN_SIGN, +            Keys.IS_REVOKED, +            Keys.CREATION, +            Keys.EXPIRY, +            Keys.FINGERPRINT +    }; +      public ViewKeyKeysAdapter(Context context, Cursor c, int flags) {          super(context, c, flags); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java deleted file mode 100644 index 76797811d..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2014 Tim Bray <tbray@textuality.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.util; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Minimal hierarchy selector - * - * This is for picking out an item in a large multilevel JSON object, for example look at - * the Keybase.io User object, documentation at https://keybase.io/__/api-docs/1.0#user-objects - * an example available via - *   curl https://keybase.io/_/api/1.0/user/lookup.json?username=timbray - * - * If you want to retrieve the ascii-armored key, you'd say - * String key = JWalk.getString(match,"them", "public_keys", "primary", "bundle"); - */ -public class JWalk { - -    /** -     * Returns an int member value from the JSON sub-object addressed by the path -     * -     * @param json The object -     * @param path list of string object member selectors -     * @return the int addressed by the path, assuming such a thing exists -     * @throws JSONException if any step in the path doesn’t work -     */ -    public static int getInt(JSONObject json, String... path) throws JSONException { -        json = walk(json, path); -        return json.getInt(path[path.length - 1]); -    } - -    /** -     * Returns a long member value from the JSON sub-object addressed by the path -     * -     * @param json The object -     * @param path list of string object member selectors -     * @return the int addressed by the path, assuming such a thing exists -     * @throws JSONException if any step in the path doesn’t work -     */ -    public static long getLong(JSONObject json, String... path) throws JSONException { -        json = walk(json, path); -        return json.getLong(path[path.length - 1]); -    } - -    /** -     * Returns a String member value from the JSON sub-object addressed by the path -     * -     * @param json The object -     * @param path list of string object member selectors -     * @return the int addressed by the path, assuming such a thing exists -     * @throws JSONException if any step in the path doesn’t work -     */ -    public static String getString(JSONObject json, String... path) throws JSONException { -        json = walk(json, path); -        return json.getString(path[path.length - 1]); -    } - -    /** -     * Returns a JSONArray member value from the JSON sub-object addressed by the path -     * -     * @param json The object -     * @param path list of string object member selectors -     * @return the int addressed by the path, assuming such a thing exists -     * @throws JSONException if any step in the path doesn’t work -     */ -    public static JSONArray getArray(JSONObject json, String... path) throws JSONException { -        json = walk(json, path); -        return json.getJSONArray(path[path.length - 1]); -    } - -    /** -     * Returns a JSONObject member value from the JSON sub-object addressed by the path, or null -     * -     * @param json The object -     * @param path list of string object member selectors -     * @return the int addressed by the path, assuming such a thing exists -     * @throws JSONException if any step in the path, except for the last, doesn’t work -     */ -    public static JSONObject optObject(JSONObject json, String... path) throws JSONException { -        json = walk(json, path); -        return json.optJSONObject(path[path.length - 1]); -    } - -    private static JSONObject walk(JSONObject json, String... path) throws JSONException { -        int len = path.length - 1; -        int pathIndex = 0; -        try { -            while (pathIndex < len) { -                json = json.getJSONObject(path[pathIndex]); -                pathIndex++; -            } -        } catch (JSONException e) { -            // try to give ’em a nice-looking error -            StringBuilder sb = new StringBuilder(); -            for (int i = 0; i < len; i++) { -                sb.append(path[i]).append('.'); -            } -            sb.append(path[len]); -            throw new JSONException("JWalk error at step " + pathIndex + " of " + sb); -        } -        return json; -    } -} diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml b/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml new file mode 100644 index 000000000..f96b993c5 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" +    android:layout_width="match_parent" +    android:layout_height="match_parent"> + +    <FrameLayout +        android:id="@+id/edit_key_fragment_container" +        android:layout_width="match_parent" +        android:layout_height="match_parent" +        android:orientation="vertical" /> + +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/edit_key_fragment.xml b/OpenKeychain/src/main/res/layout/edit_key_fragment.xml new file mode 100644 index 000000000..dbc0c3941 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/edit_key_fragment.xml @@ -0,0 +1,44 @@ +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" +    android:layout_width="match_parent" +    android:layout_height="match_parent"> + +    <LinearLayout +        android:layout_width="match_parent" +        android:layout_height="wrap_content" +        android:orientation="vertical" +        android:paddingLeft="16dp" +        android:paddingRight="16dp"> + +        <TextView +            style="@style/SectionHeader" +            android:layout_width="wrap_content" +            android:layout_height="0dp" +            android:layout_marginBottom="4dp" +            android:layout_marginTop="8dp" +            android:text="@string/section_user_ids" +            android:layout_weight="1" /> + +        <org.sufficientlysecure.keychain.ui.widget.FixedListView +            android:id="@+id/edit_key_user_ids" +            android:layout_width="match_parent" +            android:layout_height="0dp" +            android:layout_marginBottom="4dp" +            android:layout_weight="1" /> + +        <TextView +            style="@style/SectionHeader" +            android:layout_width="wrap_content" +            android:layout_height="wrap_content" +            android:layout_marginBottom="4dp" +            android:layout_marginTop="8dp" +            android:text="@string/section_keys" /> + +        <org.sufficientlysecure.keychain.ui.widget.FixedListView +            android:id="@+id/edit_key_keys" +            android:layout_width="match_parent" +            android:layout_height="wrap_content" +            android:layout_marginBottom="8dp" /> + +    </LinearLayout> + +</ScrollView> diff --git a/OpenKeychain/src/main/res/layout/log_display_activity.xml b/OpenKeychain/src/main/res/layout/log_display_activity.xml index 591e2650c..1dc45a9a8 100644 --- a/OpenKeychain/src/main/res/layout/log_display_activity.xml +++ b/OpenKeychain/src/main/res/layout/log_display_activity.xml @@ -13,34 +13,4 @@          android:layout_marginRight="8dp"          android:layout_marginLeft="8dp" /> -    <LinearLayout -        android:id="@+id/import_footer" -        android:layout_width="match_parent" -        android:layout_height="wrap_content" -        android:orientation="vertical" -        android:paddingTop="8dp" -        android:paddingLeft="16dp" -        android:paddingRight="16dp"> - -        <View -            android:layout_width="match_parent" -            android:layout_height="1dip" -            android:background="?android:attr/listDivider" /> - -        <TextView -            android:id="@+id/import_import" -            android:paddingLeft="8dp" -            android:paddingRight="8dp" -            android:layout_marginBottom="8dp" -            android:textAppearance="?android:attr/textAppearanceMedium" -            android:layout_width="match_parent" -            android:layout_height="match_parent" -            android:text="Close" -            android:minHeight="?android:attr/listPreferredItemHeight" -            android:gravity="center_vertical" -            android:clickable="true" -            style="@style/SelectableItem" /> - -    </LinearLayout> -  </LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml index d93420a99..96871aacf 100644 --- a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml @@ -74,6 +74,22 @@              android:drawablePadding="8dp"              android:gravity="center_vertical" /> +        <TextView +            android:id="@+id/view_key_action_edit_new" +            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="NEW EDIT" +            android:layout_weight="1" +            android:drawableRight="@drawable/ic_action_edit" +            android:drawablePadding="8dp" +            android:gravity="center_vertical" /> +          <View              android:id="@+id/view_key_action_edit_divider"              android:layout_width="match_parent" diff --git a/OpenKeychain/src/main/res/values/arrays.xml b/OpenKeychain/src/main/res/values/arrays.xml index 699c02aff..44bbe00cc 100644 --- a/OpenKeychain/src/main/res/values/arrays.xml +++ b/OpenKeychain/src/main/res/values/arrays.xml @@ -48,13 +48,5 @@          <item>@string/key_size_1024</item>          <item>@string/key_size_custom</item>      </string-array> -    <string-array name="import_action_list" translatable="false"> -        <item>@string/menu_import_from_key_server</item> -        <item>@string/menu_import_from_file</item> -        <item>@string/menu_import_from_qr_code</item> -        <item>@string/import_from_clipboard</item> -        <item>@string/menu_import_from_nfc</item> -        <item>@string/menu_import_from_keybase</item> -    </string-array>  </resources> diff --git a/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java b/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java new file mode 100644 index 000000000..346a1f9df --- /dev/null +++ b/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java @@ -0,0 +1,37 @@ +package tests; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.*; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class PgpDecryptVerifyTest { + +    @Test +    public void testVerifySuccess() throws Exception { + +        String testFileName = "/sample.txt"; +        int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED; + +        int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); + +        Assert.assertEquals(expectedSignatureResult, status); +    } + + +    @Test +    public void testVerifyFailure() throws Exception { + +        String testFileName = "/sample-altered.txt"; +        int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_ERROR; + +        int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); + +        Assert.assertEquals(expectedSignatureResult, status); +    } + +} diff --git a/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java b/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java new file mode 100644 index 000000000..265e01170 --- /dev/null +++ b/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java @@ -0,0 +1,20 @@ +package tests; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.*; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.testsupport.KeyringTestingHelper; +import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class ProviderHelperKeyringTest { + +    @Test +    public void testSavePublicKeyring() throws Exception { +        Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring()); +    } + +} diff --git a/OpenKeychain/src/test/resources/public-key-for-sample.blob b/OpenKeychain/src/test/resources/public-key-for-sample.blob Binary files differnew file mode 100644 index 000000000..4aa91510b --- /dev/null +++ b/OpenKeychain/src/test/resources/public-key-for-sample.blob diff --git a/OpenKeychain/src/test/resources/sample-altered.txt b/OpenKeychain/src/test/resources/sample-altered.txt new file mode 100644 index 000000000..458821f81 --- /dev/null +++ b/OpenKeychain/src/test/resources/sample-altered.txt @@ -0,0 +1,26 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +This is a simple text document, which is used to illustrate +the concept of signing simple text files.  There are no +control characters or special formatting commands in this +text, just simple printable ASCII characters. +MALICIOUS TEXT +To make this a slightly less uninteresting document, there +follows a short shopping list. + +    eggs, 1 doz +    milk, 1 gal +    bacon, 1 lb +    olive oil +    bread, 1 loaf + +That's all there is to this document. + +-----BEGIN PGP SIGNATURE----- +Version: PGPfreeware 5.5.5 for non-commercial use <http://www.nai.com> + +iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC +CDxKAFyaaGa835l1vpbFkAJk +=3r/N +-----END PGP SIGNATURE----- diff --git a/OpenKeychain/src/test/resources/sample.txt b/OpenKeychain/src/test/resources/sample.txt new file mode 100644 index 000000000..c0065f78d --- /dev/null +++ b/OpenKeychain/src/test/resources/sample.txt @@ -0,0 +1,26 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +This is a simple text document, which is used to illustrate +the concept of signing simple text files.  There are no +control characters or special formatting commands in this +text, just simple printable ASCII characters. + +To make this a slightly less uninteresting document, there +follows a short shopping list. + +    eggs, 1 doz +    milk, 1 gal +    bacon, 1 lb +    olive oil +    bread, 1 loaf + +That's all there is to this document. + +-----BEGIN PGP SIGNATURE----- +Version: PGPfreeware 5.5.5 for non-commercial use <http://www.nai.com> + +iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC +CDxKAFyaaGa835l1vpbFkAJk +=3r/N +-----END PGP SIGNATURE-----  | 
