aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OpenPGP-Keychain/src/main/AndroidManifest.xml14
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java39
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java71
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java746
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java202
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java91
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java22
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java112
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/import_keys_clipboard_fragment.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/import_keys_keyserver_fragment.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/import_keys_nfc_fragment.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/import_keys_qr_code_fragment.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_server_export.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml4
-rw-r--r--README.md10
19 files changed, 656 insertions, 680 deletions
diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml
index 1863d2b62..b8ee23514 100644
--- a/OpenPGP-Keychain/src/main/AndroidManifest.xml
+++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml
@@ -266,6 +266,20 @@
android:launchMode="singleTop"
android:windowSoftInputMode="stateHidden" >
+ <!-- Handle URIs with fingerprints when scanning directly from Barcode Scanner -->
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+
+ <!-- Android's scheme matcher is case-sensitive, so include most likely variations -->
+ <data android:scheme="openpgp4fpr" />
+ <data android:scheme="OPENPGP4FPR" />
+ <data android:scheme="OpenPGP4FPR" />
+ <data android:scheme="OpenPGP4Fpr" />
+ <data android:scheme="OpenPGP4fpr" />
+ </intent-filter>
<!-- Handle NFC tags detected from outside our application -->
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index 7ab7aae3f..3fc63cda1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -42,17 +42,6 @@ import android.content.Context;
public class PgpKeyHelper {
- /**
- * Returns the last 9 chars of a fingerprint
- *
- * @param fingerprint
- * String containing short or long fingerprint
- * @return
- */
- public static String shortifyFingerprint(String fingerprint) {
- return fingerprint.substring(41);
- }
-
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
@@ -175,10 +164,6 @@ public class PgpKeyHelper {
return true;
}
- public static boolean isExpired(PGPSecretKey key) {
- return isExpired(key.getPublicKey());
- }
-
public static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
@@ -460,12 +445,12 @@ public class PgpKeyHelper {
* @param fp
* @return
*/
- public static String convertFingerprintToHex(byte[] fp) {
+ public static String convertFingerprintToHex(byte[] fp, boolean chunked) {
String fingerPrint = "";
for (int i = 0; i < fp.length; ++i) {
- if (i != 0 && i % 10 == 0) {
+ if (chunked && i != 0 && i % 10 == 0) {
fingerPrint += " ";
- } else if (i != 0 && i % 2 == 0) {
+ } else if (chunked && i != 0 && i % 2 == 0) {
fingerPrint += " ";
}
String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase(Locale.US);
@@ -491,21 +476,21 @@ public class PgpKeyHelper {
key = secretKey.getPublicKey();
}
- return convertFingerprintToHex(key.getFingerprint());
+ return convertFingerprintToHex(key.getFingerprint(), true);
}
public static boolean isSecretKeyPrivateEmpty(PGPSecretKey secretKey) {
return secretKey.isPrivateKeyEmpty();
}
- public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
- PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
- if (secretKey == null) {
- Log.e(Constants.TAG, "Key could not be found!");
- return false; // could be a public key, assume it is not empty
- }
- return isSecretKeyPrivateEmpty(secretKey);
- }
+// public static boolean isSecretKeyPrivateEmpty(Context context, long keyId) {
+// PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
+// if (secretKey == null) {
+// Log.e(Constants.TAG, "Key could not be found!");
+// return false; // could be a public key, assume it is not empty
+// }
+// return isSecretKeyPrivateEmpty(secretKey);
+// }
public static String convertKeyIdToHex(long keyId) {
String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index d2381f6f0..d8de30b37 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -45,6 +45,7 @@ public class KeychainContract {
String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID
String KEY_DATA = "key_data"; // PGPPublicKey/PGPSecretKey blob
String RANK = "rank";
+ String FINGERPRINT = "fingerprint";
}
interface UserIdsColumns {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 60c5c91a8..3a00f3101 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -31,7 +31,7 @@ import android.provider.BaseColumns;
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "apg.db";
- private static final int DATABASE_VERSION = 6;
+ private static final int DATABASE_VERSION = 7;
public interface Tables {
String KEY_RINGS = "key_rings";
@@ -42,33 +42,45 @@ public class KeychainDatabase extends SQLiteOpenHelper {
private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + KeyRingsColumns.MASTER_KEY_ID + " INT64, " + KeyRingsColumns.TYPE + " INTEGER, "
+ + KeyRingsColumns.MASTER_KEY_ID + " INT64, "
+ + KeyRingsColumns.TYPE + " INTEGER, "
+ KeyRingsColumns.KEY_RING_DATA + " BLOB)";
private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " ("
- + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KeysColumns.KEY_ID
- + " INT64, " + KeysColumns.TYPE + " INTEGER, " + KeysColumns.IS_MASTER_KEY
- + " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.KEY_SIZE
- + " INTEGER, " + KeysColumns.CAN_CERTIFY + " INTEGER, " + KeysColumns.CAN_SIGN
- + " INTEGER, " + KeysColumns.CAN_ENCRYPT + " INTEGER, " + KeysColumns.IS_REVOKED
- + " INTEGER, " + KeysColumns.CREATION + " INTEGER, " + KeysColumns.EXPIRY
- + " INTEGER, " + KeysColumns.KEY_DATA + " BLOB," + KeysColumns.RANK + " INTEGER, "
+ + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + KeysColumns.KEY_ID + " INT64, "
+ + KeysColumns.TYPE + " INTEGER, "
+ + KeysColumns.IS_MASTER_KEY + " INTEGER, "
+ + KeysColumns.ALGORITHM + " INTEGER, "
+ + KeysColumns.KEY_SIZE + " INTEGER, "
+ + KeysColumns.CAN_CERTIFY + " INTEGER, "
+ + KeysColumns.CAN_SIGN + " INTEGER, "
+ + KeysColumns.CAN_ENCRYPT + " INTEGER, "
+ + KeysColumns.IS_REVOKED + " INTEGER, "
+ + KeysColumns.CREATION + " INTEGER, "
+ + KeysColumns.EXPIRY + " INTEGER, "
+ + KeysColumns.KEY_DATA + " BLOB,"
+ + KeysColumns.RANK + " INTEGER, "
+ + KeysColumns.FINGERPRINT + " BLOB, "
+ KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
+ KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
+ BaseColumns._ID + ") ON DELETE CASCADE)";
private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, "
+ + UserIdsColumns.USER_ID + " TEXT, "
+ + UserIdsColumns.RANK + " INTEGER, "
+ UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
+ UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
+ BaseColumns._ID + ") ON DELETE CASCADE)";
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " + ApiAppsColumns.PACKAGE_SIGNATURE
- + " BLOB, " + ApiAppsColumns.KEY_ID + " INT64, " + ApiAppsColumns.ENCRYPTION_ALGORITHM
- + " INTEGER, " + ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
+ + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, "
+ + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, "
+ + ApiAppsColumns.KEY_ID + " INT64, "
+ + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
+ + ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
+ ApiAppsColumns.COMPRESSION + " INTEGER)";
KeychainDatabase(Context context) {
@@ -103,21 +115,24 @@ public class KeychainDatabase extends SQLiteOpenHelper {
Log.w(Constants.TAG, "Upgrading database to version " + version);
switch (version) {
- case 3:
- db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY
- + " INTEGER DEFAULT 0;");
- db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY
- + " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;");
- break;
- case 4:
- db.execSQL(CREATE_API_APPS);
- case 5:
- // new column: package_signature
- db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
- db.execSQL(CREATE_API_APPS);
-
- default:
- break;
+ case 3:
+ db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY
+ + " INTEGER DEFAULT 0;");
+ db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY
+ + " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;");
+ break;
+ case 4:
+ db.execSQL(CREATE_API_APPS);
+ case 5:
+ // new column: package_signature
+ db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
+ db.execSQL(CREATE_API_APPS);
+ case 6:
+ // new column: fingerprint
+ db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
+ + " BLOB;");
+ default:
+ break;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 70fb11e47..62fcf4f84 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -96,7 +96,7 @@ public class KeychainProvider extends ContentProvider {
/**
* public key rings
- *
+ *
* <pre>
* key_rings/public
* key_rings/public/#
@@ -128,7 +128,7 @@ public class KeychainProvider extends ContentProvider {
/**
* public keys
- *
+ *
* <pre>
* key_rings/public/#/keys
* key_rings/public/#/keys/#
@@ -143,7 +143,7 @@ public class KeychainProvider extends ContentProvider {
/**
* public user ids
- *
+ *
* <pre>
* key_rings/public/#/user_ids
* key_rings/public/#/user_ids/#
@@ -158,7 +158,7 @@ public class KeychainProvider extends ContentProvider {
/**
* secret key rings
- *
+ *
* <pre>
* key_rings/secret
* key_rings/secret/#
@@ -190,7 +190,7 @@ public class KeychainProvider extends ContentProvider {
/**
* secret keys
- *
+ *
* <pre>
* key_rings/secret/#/keys
* key_rings/secret/#/keys/#
@@ -205,7 +205,7 @@ public class KeychainProvider extends ContentProvider {
/**
* secret user ids
- *
+ *
* <pre>
* key_rings/secret/#/user_ids
* key_rings/secret/#/user_ids/#
@@ -228,7 +228,7 @@ public class KeychainProvider extends ContentProvider {
/**
* data stream
- *
+ *
* <pre>
* data / _
* </pre>
@@ -240,7 +240,9 @@ public class KeychainProvider extends ContentProvider {
private KeychainDatabase mApgDatabase;
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public boolean onCreate() {
mUriMatcher = buildUriMatcher();
@@ -248,94 +250,96 @@ public class KeychainProvider extends ContentProvider {
return true;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public String getType(Uri uri) {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_BY_EMAILS:
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- return KeyRings.CONTENT_TYPE;
-
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_KEY_ID:
- return KeyRings.CONTENT_ITEM_TYPE;
-
- case PUBLIC_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY:
- return Keys.CONTENT_TYPE;
-
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- return Keys.CONTENT_ITEM_TYPE;
-
- case PUBLIC_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID:
- return UserIds.CONTENT_TYPE;
-
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- return UserIds.CONTENT_ITEM_TYPE;
-
- case API_APPS:
- return ApiApps.CONTENT_TYPE;
-
- case API_APPS_BY_ROW_ID:
- case API_APPS_BY_PACKAGE_NAME:
- return ApiApps.CONTENT_ITEM_TYPE;
-
- default:
- throw new UnsupportedOperationException("Unknown uri: " + uri);
+ case PUBLIC_KEY_RING:
+ case PUBLIC_KEY_RING_BY_EMAILS:
+ case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
+ case SECRET_KEY_RING:
+ case SECRET_KEY_RING_BY_EMAILS:
+ case SECRET_KEY_RING_BY_LIKE_EMAIL:
+ return KeyRings.CONTENT_TYPE;
+
+ case PUBLIC_KEY_RING_BY_ROW_ID:
+ case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
+ case PUBLIC_KEY_RING_BY_KEY_ID:
+ case SECRET_KEY_RING_BY_ROW_ID:
+ case SECRET_KEY_RING_BY_MASTER_KEY_ID:
+ case SECRET_KEY_RING_BY_KEY_ID:
+ return KeyRings.CONTENT_ITEM_TYPE;
+
+ case PUBLIC_KEY_RING_KEY:
+ case SECRET_KEY_RING_KEY:
+ return Keys.CONTENT_TYPE;
+
+ case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
+ case SECRET_KEY_RING_KEY_BY_ROW_ID:
+ return Keys.CONTENT_ITEM_TYPE;
+
+ case PUBLIC_KEY_RING_USER_ID:
+ case SECRET_KEY_RING_USER_ID:
+ return UserIds.CONTENT_TYPE;
+
+ case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
+ case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
+ return UserIds.CONTENT_ITEM_TYPE;
+
+ case API_APPS:
+ return ApiApps.CONTENT_TYPE;
+
+ case API_APPS_BY_ROW_ID:
+ case API_APPS_BY_PACKAGE_NAME:
+ return ApiApps.CONTENT_ITEM_TYPE;
+
+ default:
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
}
}
/**
* Returns type of the query (secret/public)
- *
+ *
* @param uri
* @return
*/
private int getKeyType(int match) {
int type;
switch (match) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- case PUBLIC_KEY_RING_KEY:
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case PUBLIC_KEY_RING_USER_ID:
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- type = KeyTypes.PUBLIC;
- break;
-
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_KEY_ID:
- case SECRET_KEY_RING_BY_EMAILS:
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- case SECRET_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- type = KeyTypes.SECRET;
- break;
-
- default:
- Log.e(Constants.TAG, "Unknown match " + match);
- type = -1;
- break;
+ case PUBLIC_KEY_RING:
+ case PUBLIC_KEY_RING_BY_ROW_ID:
+ case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
+ case PUBLIC_KEY_RING_BY_KEY_ID:
+ case PUBLIC_KEY_RING_BY_EMAILS:
+ case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
+ case PUBLIC_KEY_RING_KEY:
+ case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
+ case PUBLIC_KEY_RING_USER_ID:
+ case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
+ type = KeyTypes.PUBLIC;
+ break;
+
+ case SECRET_KEY_RING:
+ case SECRET_KEY_RING_BY_ROW_ID:
+ case SECRET_KEY_RING_BY_MASTER_KEY_ID:
+ case SECRET_KEY_RING_BY_KEY_ID:
+ case SECRET_KEY_RING_BY_EMAILS:
+ case SECRET_KEY_RING_BY_LIKE_EMAIL:
+ case SECRET_KEY_RING_KEY:
+ case SECRET_KEY_RING_KEY_BY_ROW_ID:
+ case SECRET_KEY_RING_USER_ID:
+ case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
+ type = KeyTypes.SECRET;
+ break;
+
+ default:
+ Log.e(Constants.TAG, "Unknown match " + match);
+ type = -1;
+ break;
}
return type;
@@ -343,17 +347,20 @@ public class KeychainProvider extends ContentProvider {
/**
* Set result of query to specific columns, don't show blob column for external content provider
- *
+ *
* @return
*/
private HashMap<String, String> getProjectionMapForKeyRings() {
HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
- projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
- + KeyRingsColumns.MASTER_KEY_ID);
projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
+ KeyRingsColumns.KEY_RING_DATA);
+ projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID);
+ // TODO: deprecated master key id
+ //projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID);
+ projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT);
+
projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
return projectionMap;
@@ -361,7 +368,7 @@ public class KeychainProvider extends ContentProvider {
/**
* Set result of query to specific columns, don't show blob column for external content provider
- *
+ *
* @return
*/
private HashMap<String, String> getProjectionMapForKeys() {
@@ -386,22 +393,20 @@ public class KeychainProvider extends ContentProvider {
}
/**
- * Builds default query for keyRings: KeyRings table is joined with UserIds
- *
- * @param qb
- * @param match
- * @param isMasterKey
- * @param sortOrder
- * @return
+ * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
*/
- private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match, String sortOrder) {
+ private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
// public or secret keyring
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
- // join keyrings with userIds to every keyring
- qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.USER_IDS + " ON " + "("
- + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
+ // join keyrings with keys and userIds
+ // Only get user id and key with rank 0 (main user id and main key)
+ qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "("
+ + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "."
+ + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "."
+ + KeysColumns.RANK + " = '0') " + " INNER JOIN " + Tables.USER_IDS + " ON "
+ + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
+ UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "."
+ UserIdsColumns.RANK + " = '0')");
@@ -411,16 +416,11 @@ public class KeychainProvider extends ContentProvider {
}
/**
- * Builds default query for keyRings: KeyRings table is joined with Keys and UserIds
- *
- * @param qb
- * @param match
- * @param isMasterKey
- * @param sortOrder
- * @return
+ * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
+ * <p/>
+ * Here only one key should be selected in the query to return a single keyring!
*/
- private SQLiteQueryBuilder buildKeyRingQueryWithKeys(SQLiteQueryBuilder qb, int match,
- String sortOrder) {
+ private SQLiteQueryBuilder buildKeyRingQueryWithSpecificKey(SQLiteQueryBuilder qb, int match) {
// public or secret keyring
qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
@@ -438,11 +438,13 @@ public class KeychainProvider extends ContentProvider {
return qb;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@SuppressWarnings("deprecation")
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ String sortOrder) {
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@@ -451,169 +453,169 @@ public class KeychainProvider extends ContentProvider {
int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING:
- case SECRET_KEY_RING:
- qb = buildKeyRingQuery(qb, match, sortOrder);
+ case PUBLIC_KEY_RING:
+ case SECRET_KEY_RING:
+ qb = buildKeyRingQuery(qb, match);
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ }
- break;
+ break;
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- qb = buildKeyRingQuery(qb, match, sortOrder);
+ case PUBLIC_KEY_RING_BY_ROW_ID:
+ case SECRET_KEY_RING_BY_ROW_ID:
+ qb = buildKeyRingQuery(qb, match);
- qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ }
- break;
+ break;
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- qb = buildKeyRingQuery(qb, match, sortOrder);
+ case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
+ case SECRET_KEY_RING_BY_MASTER_KEY_ID:
+ qb = buildKeyRingQuery(qb, match);
- qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
-
- break;
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ }
- case SECRET_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- qb = buildKeyRingQueryWithKeys(qb, match, sortOrder);
+ break;
- qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ case SECRET_KEY_RING_BY_KEY_ID:
+ case PUBLIC_KEY_RING_BY_KEY_ID:
+ qb = buildKeyRingQueryWithSpecificKey(qb, match);
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
+ qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
- break;
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ }
- case SECRET_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_EMAILS:
- qb = buildKeyRingQuery(qb, match, sortOrder);
+ break;
- String emails = uri.getLastPathSegment();
- String chunks[] = emails.split(" *, *");
- boolean gotCondition = false;
- String emailWhere = "";
- for (int i = 0; i < chunks.length; ++i) {
- if (chunks[i].length() == 0) {
- continue;
+ case SECRET_KEY_RING_BY_EMAILS:
+ case PUBLIC_KEY_RING_BY_EMAILS:
+ qb = buildKeyRingQuery(qb, match);
+
+ String emails = uri.getLastPathSegment();
+ String chunks[] = emails.split(" *, *");
+ boolean gotCondition = false;
+ String emailWhere = "";
+ for (int i = 0; i < chunks.length; ++i) {
+ if (chunks[i].length() == 0) {
+ continue;
+ }
+ if (i != 0) {
+ emailWhere += " OR ";
+ }
+ emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE ";
+ // match '*<email>', so it has to be at the *end* of the user id
+ emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
+ gotCondition = true;
}
- if (i != 0) {
- emailWhere += " OR ";
- }
- emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE ";
- // match '*<email>', so it has to be at the *end* of the user id
- emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
- gotCondition = true;
- }
- if (gotCondition) {
- qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
- + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
- + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere
- + "))");
- }
+ if (gotCondition) {
+ qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
+ + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
+ + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere
+ + "))");
+ }
- break;
+ break;
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- qb = buildKeyRingQuery(qb, match, sortOrder);
+ case SECRET_KEY_RING_BY_LIKE_EMAIL:
+ case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
+ qb = buildKeyRingQuery(qb, match);
- String likeEmail = uri.getLastPathSegment();
+ String likeEmail = uri.getLastPathSegment();
- String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE "
- + DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>");
+ String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE "
+ + DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>");
- qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
- + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
- + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere
- + "))");
+ qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
+ + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
+ + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere
+ + "))");
- break;
+ break;
- case PUBLIC_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY:
- qb.setTables(Tables.KEYS);
- qb.appendWhere(KeysColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ case PUBLIC_KEY_RING_KEY:
+ case SECRET_KEY_RING_KEY:
+ qb.setTables(Tables.KEYS);
+ qb.appendWhere(KeysColumns.TYPE + " = ");
+ qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
- qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
- qb.setProjectionMap(getProjectionMapForKeys());
+ qb.setProjectionMap(getProjectionMapForKeys());
- break;
+ break;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- qb.setTables(Tables.KEYS);
- qb.appendWhere(KeysColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
+ case SECRET_KEY_RING_KEY_BY_ROW_ID:
+ qb.setTables(Tables.KEYS);
+ qb.appendWhere(KeysColumns.TYPE + " = ");
+ qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
- qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
- qb.appendWhere(" AND " + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ qb.appendWhere(" AND " + BaseColumns._ID + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
- qb.setProjectionMap(getProjectionMapForKeys());
+ qb.setProjectionMap(getProjectionMapForKeys());
- break;
+ break;
- case PUBLIC_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID:
- qb.setTables(Tables.USER_IDS);
- qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ case PUBLIC_KEY_RING_USER_ID:
+ case SECRET_KEY_RING_USER_ID:
+ qb.setTables(Tables.USER_IDS);
+ qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
- break;
+ break;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- qb.setTables(Tables.USER_IDS);
- qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
+ case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
+ qb.setTables(Tables.USER_IDS);
+ qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
- qb.appendWhere(" AND " + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ qb.appendWhere(" AND " + BaseColumns._ID + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
- break;
+ break;
- case API_APPS:
- qb.setTables(Tables.API_APPS);
+ case API_APPS:
+ qb.setTables(Tables.API_APPS);
- break;
- case API_APPS_BY_ROW_ID:
- qb.setTables(Tables.API_APPS);
+ break;
+ case API_APPS_BY_ROW_ID:
+ qb.setTables(Tables.API_APPS);
- qb.appendWhere(BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ qb.appendWhere(BaseColumns._ID + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
- break;
- case API_APPS_BY_PACKAGE_NAME:
- qb.setTables(Tables.API_APPS);
- qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ break;
+ case API_APPS_BY_PACKAGE_NAME:
+ qb.setTables(Tables.API_APPS);
+ qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
- break;
+ break;
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
}
@@ -634,14 +636,16 @@ public class KeychainProvider extends ContentProvider {
Log.d(Constants.TAG,
"Query: "
+ qb.buildQuery(projection, selection, selectionArgs, null, null,
- orderBy, null));
+ orderBy, null));
Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c));
}
return c;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
@@ -654,56 +658,56 @@ public class KeychainProvider extends ContentProvider {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING:
- values.put(KeyRings.TYPE, KeyTypes.PUBLIC);
-
- rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
- rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_KEY:
- values.put(Keys.TYPE, KeyTypes.PUBLIC);
-
- rowId = db.insertOrThrow(Tables.KEYS, null, values);
- rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_USER_ID:
- rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
- rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case SECRET_KEY_RING:
- values.put(KeyRings.TYPE, KeyTypes.SECRET);
-
- rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
- rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case SECRET_KEY_RING_KEY:
- values.put(Keys.TYPE, KeyTypes.SECRET);
-
- rowId = db.insertOrThrow(Tables.KEYS, null, values);
- rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case SECRET_KEY_RING_USER_ID:
- rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
- rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
-
- break;
- case API_APPS:
- rowId = db.insertOrThrow(Tables.API_APPS, null, values);
- rowUri = ApiApps.buildIdUri(Long.toString(rowId));
-
- break;
- default:
- throw new UnsupportedOperationException("Unknown uri: " + uri);
+ case PUBLIC_KEY_RING:
+ values.put(KeyRings.TYPE, KeyTypes.PUBLIC);
+
+ rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
+ rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case PUBLIC_KEY_RING_KEY:
+ values.put(Keys.TYPE, KeyTypes.PUBLIC);
+
+ rowId = db.insertOrThrow(Tables.KEYS, null, values);
+ rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case PUBLIC_KEY_RING_USER_ID:
+ rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
+ rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case SECRET_KEY_RING:
+ values.put(KeyRings.TYPE, KeyTypes.SECRET);
+
+ rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
+ rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case SECRET_KEY_RING_KEY:
+ values.put(Keys.TYPE, KeyTypes.SECRET);
+
+ rowId = db.insertOrThrow(Tables.KEYS, null, values);
+ rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case SECRET_KEY_RING_USER_ID:
+ rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
+ rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
+
+ break;
+ case API_APPS:
+ rowId = db.insertOrThrow(Tables.API_APPS, null, values);
+ rowUri = ApiApps.buildIdUri(Long.toString(rowId));
+
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// notify of changes in db
@@ -716,7 +720,9 @@ public class KeychainProvider extends ContentProvider {
return rowUri;
}
- /** {@inheritDoc} */
+ /**
+ * {@inheritDoc}
+ */
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.v(Constants.TAG, "delete(uri=" + uri + ")");
@@ -728,113 +734,115 @@ public class KeychainProvider extends ContentProvider {
String defaultSelection = null;
switch (match) {
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
- // corresponding keys and userIds are deleted by ON DELETE CASCADE
- count = db.delete(Tables.KEY_RINGS,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
- break;
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
- // corresponding keys and userIds are deleted by ON DELETE CASCADE
- count = db.delete(Tables.KEY_RINGS,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
- break;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- count = db.delete(Tables.KEYS,
- buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
- break;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
- selectionArgs);
- break;
- case API_APPS_BY_ROW_ID:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
- selectionArgs);
- break;
- case API_APPS_BY_PACKAGE_NAME:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
- selectionArgs);
- break;
- default:
- throw new UnsupportedOperationException("Unknown uri: " + uri);
- }
-
- // notify of changes in db
- getContext().getContentResolver().notifyChange(uri, null);
-
- return count;
- }
-
- /** {@inheritDoc} */
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
-
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
-
- String defaultSelection = null;
- int count = 0;
- try {
- final int match = mUriMatcher.match(uri);
- switch (match) {
case PUBLIC_KEY_RING_BY_ROW_ID:
case SECRET_KEY_RING_BY_ROW_ID:
defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
-
- count = db.update(
- Tables.KEY_RINGS,
- values,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
- selection), selectionArgs);
+ // corresponding keys and userIds are deleted by ON DELETE CASCADE
+ count = db.delete(Tables.KEY_RINGS,
+ buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
+ selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
break;
case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
case SECRET_KEY_RING_BY_MASTER_KEY_ID:
defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
-
- count = db.update(
- Tables.KEY_RINGS,
- values,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
- selection), selectionArgs);
+ // corresponding keys and userIds are deleted by ON DELETE CASCADE
+ count = db.delete(Tables.KEY_RINGS,
+ buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
+ selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
break;
case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
case SECRET_KEY_RING_KEY_BY_ROW_ID:
- count = db
- .update(Tables.KEYS, values,
- buildDefaultKeysSelection(uri, getKeyType(match), selection),
- selectionArgs);
+ count = db.delete(Tables.KEYS,
+ buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs);
sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
break;
case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- count = db.update(Tables.USER_IDS, values,
- buildDefaultUserIdsSelection(uri, selection), selectionArgs);
+ count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
+ selectionArgs);
break;
case API_APPS_BY_ROW_ID:
- count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
+ count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
+ selectionArgs);
break;
case API_APPS_BY_PACKAGE_NAME:
- count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
+ count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
+ selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+
+ // notify of changes in db
+ getContext().getContentResolver().notifyChange(uri, null);
+
+ return count;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
+
+ final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+
+ String defaultSelection = null;
+ int count = 0;
+ try {
+ final int match = mUriMatcher.match(uri);
+ switch (match) {
+ case PUBLIC_KEY_RING_BY_ROW_ID:
+ case SECRET_KEY_RING_BY_ROW_ID:
+ defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
+
+ count = db.update(
+ Tables.KEY_RINGS,
+ values,
+ buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
+ selection), selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
+ case SECRET_KEY_RING_BY_MASTER_KEY_ID:
+ defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
+
+ count = db.update(
+ Tables.KEY_RINGS,
+ values,
+ buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
+ selection), selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
+ case SECRET_KEY_RING_KEY_BY_ROW_ID:
+ count = db
+ .update(Tables.KEYS, values,
+ buildDefaultKeysSelection(uri, getKeyType(match), selection),
+ selectionArgs);
+ sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+
+ break;
+ case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
+ case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
+ count = db.update(Tables.USER_IDS, values,
+ buildDefaultUserIdsSelection(uri, selection), selectionArgs);
+ break;
+ case API_APPS_BY_ROW_ID:
+ count = db.update(Tables.API_APPS, values,
+ buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
+ break;
+ case API_APPS_BY_PACKAGE_NAME:
+ count = db.update(Tables.API_APPS, values,
+ buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
}
// notify of changes in db
@@ -850,13 +858,13 @@ public class KeychainProvider extends ContentProvider {
/**
* Build default selection statement for KeyRings. If no extra selection is specified only build
* where clause with rowId
- *
+ *
* @param uri
* @param selection
* @return
*/
private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType,
- String selection) {
+ String selection) {
String andType = "";
if (keyType != null) {
andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType;
@@ -873,7 +881,7 @@ public class KeychainProvider extends ContentProvider {
/**
* Build default selection statement for Keys. If no extra selection is specified only build
* where clause with rowId
- *
+ *
* @param uri
* @param selection
* @return
@@ -901,7 +909,7 @@ public class KeychainProvider extends ContentProvider {
/**
* Build default selection statement for UserIds. If no extra selection is specified only build
* where clause with rowId
- *
+ *
* @param uri
* @param selection
* @return
@@ -924,7 +932,7 @@ public class KeychainProvider extends ContentProvider {
/**
* Build default selection statement for API apps. If no extra selection is specified only build
* where clause with rowId
- *
+ *
* @param uri
* @param selection
* @return
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 10404e0ff..a1d7ad511 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
import java.util.Date;
import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.UserAttributePacket;
+import org.spongycastle.bcpg.UserAttributeSubpacket;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
@@ -55,10 +57,6 @@ public class ProviderHelper {
/**
* Private helper method to get PGPKeyRing from database
- *
- * @param context
- * @param queryUri
- * @return
*/
public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri,
@@ -83,10 +81,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId
- *
- * @param context
- * @param rowId
- * @return
*/
public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
@@ -95,10 +89,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId
- *
- * @param context
- * @param masterKeyId
- * @return
*/
public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
long masterKeyId) {
@@ -109,10 +99,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key
* with this keyId
- *
- * @param context
- * @param keyId
- * @return
*/
public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) {
Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId));
@@ -122,10 +108,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPPublicKey object from the database blob associated with a key with
* this keyId
- *
- * @param context
- * @param keyId
- * @return
*/
public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
@@ -138,10 +120,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId
- *
- * @param context
- * @param rowId
- * @return
*/
public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
@@ -150,10 +128,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
- *
- * @param context
- * @param masterKeyId
- * @return
*/
public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context,
long masterKeyId) {
@@ -164,10 +138,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key
* with this keyId
- *
- * @param context
- * @param keyId
- * @return
*/
public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) {
Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId));
@@ -177,10 +147,6 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPSecretKey object from the database blob associated with a key with
* this keyId
- *
- * @param context
- * @param keyId
- * @return
*/
public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
@@ -193,12 +159,6 @@ public class ProviderHelper {
/**
* Saves PGPPublicKeyRing with its keys and userIds in DB
- *
- * @param context
- * @param keyRing
- * @return
- * @throws IOException
- * @throws GeneralException
*/
@SuppressWarnings("unchecked")
public static void saveKeyRing(Context context, PGPPublicKeyRing keyRing) throws IOException {
@@ -263,12 +223,6 @@ public class ProviderHelper {
/**
* Saves PGPSecretKeyRing with its keys and userIds in DB
- *
- * @param context
- * @param keyRing
- * @return
- * @throws IOException
- * @throws GeneralException
*/
@SuppressWarnings("unchecked")
public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException {
@@ -333,13 +287,6 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
- *
- * @param context
- * @param keyRingRowId
- * @param key
- * @param rank
- * @return
- * @throws IOException
*/
private static ContentProviderOperation buildPublicKeyOperations(Context context,
long keyRingRowId, PGPPublicKey key, int rank) throws IOException {
@@ -367,13 +314,6 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing
- *
- * @param context
- * @param keyRingRowId
- * @param key
- * @param rank
- * @return
- * @throws IOException
*/
private static ContentProviderOperation buildPublicUserIdOperations(Context context,
long keyRingRowId, String userId, int rank) {
@@ -389,13 +329,6 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing
- *
- * @param context
- * @param keyRingRowId
- * @param key
- * @param rank
- * @return
- * @throws IOException
*/
private static ContentProviderOperation buildSecretKeyOperations(Context context,
long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
@@ -432,13 +365,6 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing
- *
- * @param context
- * @param keyRingRowId
- * @param key
- * @param rank
- * @return
- * @throws IOException
*/
private static ContentProviderOperation buildSecretUserIdOperations(Context context,
long keyRingRowId, String userId, int rank) {
@@ -454,10 +380,6 @@ public class ProviderHelper {
/**
* Private helper method
- *
- * @param context
- * @param queryUri
- * @return
*/
private static ArrayList<Long> getKeyRingsMasterKeyIds(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri,
@@ -482,9 +404,6 @@ public class ProviderHelper {
/**
* Retrieves ids of all SecretKeyRings
- *
- * @param context
- * @return
*/
public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri();
@@ -493,9 +412,6 @@ public class ProviderHelper {
/**
* Retrieves ids of all PublicKeyRings
- *
- * @param context
- * @return
*/
public static ArrayList<Long> getPublicKeyRingsMasterKeyIds(Context context) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri();
@@ -514,10 +430,6 @@ public class ProviderHelper {
/**
* Get master key id of keyring by its row id
- *
- * @param context
- * @param keyRingRowId
- * @return
*/
public static long getPublicMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId));
@@ -526,10 +438,6 @@ public class ProviderHelper {
/**
* Get empty status of master key of keyring by its row id
- *
- * @param context
- * @param keyRingRowId
- * @return
*/
public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
@@ -538,11 +446,6 @@ public class ProviderHelper {
/**
* Private helper method to get master key private empty status of keyring by its row id
- *
- * @param context
- * @param queryUri
- * @param keyRingRowId
- * @return
*/
private static boolean getMasterKeyCanSign(Context context, Uri queryUri, long keyRingRowId) {
String[] projection = new String[]{
@@ -572,10 +475,6 @@ public class ProviderHelper {
/**
* Get master key id of keyring by its row id
- *
- * @param context
- * @param keyRingRowId
- * @return
*/
public static long getSecretMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
@@ -583,41 +482,84 @@ public class ProviderHelper {
}
/**
- * Private helper method to get master key id of keyring by its row id
- *
- * @param context
- * @param queryUri
- * @param keyRingRowId
- * @return
+ * Get master key id of key
*/
public static long getMasterKeyId(Context context, Uri queryUri) {
String[] projection = new String[]{KeyRings.MASTER_KEY_ID};
-
- ContentResolver cr = context.getContentResolver();
- Cursor cursor = cr.query(queryUri, projection, null, null, null);
+ Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
long masterKeyId = -1;
- if (cursor != null && cursor.moveToFirst()) {
- int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
-
- masterKeyId = cursor.getLong(masterKeyIdCol);
- }
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ int masterKeyIdCol = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
- if (cursor != null) {
- cursor.close();
+ masterKeyId = cursor.getLong(masterKeyIdCol);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
return masterKeyId;
}
- public static ArrayList<String> getPublicKeyRingsAsArmoredString(Context context,
- long[] masterKeyIds) {
- return getKeyRingsAsArmoredString(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds);
- }
+ /**
+ * Get fingerprint of key
+ */
+ public static byte[] getFingerprint(Context context, Uri queryUri) {
+ String[] projection = new String[]{Keys.FINGERPRINT};
+ Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
- public static ArrayList<String> getSecretKeyRingsAsArmoredString(Context context,
- long[] masterKeyIds) {
- return getKeyRingsAsArmoredString(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds);
+ byte[] fingerprint = null;
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ int col = cursor.getColumnIndexOrThrow(Keys.FINGERPRINT);
+
+ fingerprint = cursor.getBlob(col);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ // FALLBACK: If fingerprint is not in database, get it from key blob!
+ // this could happen if the key was saved by a previous version of Keychain!
+ if (fingerprint == null) {
+ Log.d(Constants.TAG, "FALLBACK: fingerprint is not in database, get it from key blob!");
+
+ // get master key id
+ projection = new String[]{KeyRings.MASTER_KEY_ID};
+ cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
+ long masterKeyId = 0;
+ try {
+ if (cursor != null && cursor.moveToFirst()) {
+ int col = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
+
+ masterKeyId = cursor.getLong(col);
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, masterKeyId);
+ // if it is no public key get it from your own keys...
+ if (key == null) {
+ PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, masterKeyId);
+ if (secretKey == null) {
+ Log.e(Constants.TAG, "Key could not be found!");
+ return null;
+ }
+ key = secretKey.getPublicKey();
+ }
+
+ fingerprint = key.getFingerprint();
+ }
+
+ return fingerprint;
}
public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
@@ -681,14 +623,6 @@ public class ProviderHelper {
}
}
- public static byte[] getPublicKeyRingsAsByteArray(Context context, long[] masterKeyIds) {
- return getKeyRingsAsByteArray(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds);
- }
-
- public static byte[] getSecretKeyRingsAsByteArray(Context context, long[] masterKeyIds) {
- return getKeyRingsAsByteArray(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds);
- }
-
public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 1be5b8548..cccbcfa14 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -35,6 +36,7 @@ import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.os.Bundle;
@@ -56,6 +58,8 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY";
public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_QR_CODE";
+ public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = Constants.INTENT_PREFIX
+ + "IMPORT_KEY_FROM_KEYSERVER";
// Actions for internal use only:
public static final String ACTION_IMPORT_KEY_FROM_FILE = Constants.INTENT_PREFIX
@@ -63,24 +67,21 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
public static final String ACTION_IMPORT_KEY_FROM_NFC = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_NFC";
- // only used by IMPORT
+ // only used by ACTION_IMPORT_KEY
public static final String EXTRA_KEY_BYTES = "key_bytes";
- // TODO: import keys from server
- // public static final String EXTRA_KEY_ID = "keyId";
+ // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER
+ public static final String EXTRA_QUERY = "query";
- protected boolean mDeleteAfterImport = false;
-
- FileDialogFragment mFileDialog;
- ImportKeysListFragment mListFragment;
- OnNavigationListener mOnNavigationListener;
- String[] mNavigationStrings;
+ public static final String FINGERPRINT_SCHEME = "openpgp4fpr";
- Fragment mCurrentFragment;
-
- BootstrapButton mImportButton;
+ protected boolean mDeleteAfterImport = false;
- // BootstrapButton mImportSignUploadButton;
+ // view
+ private ImportKeysListFragment mListFragment;
+ private String[] mNavigationStrings;
+ private Fragment mCurrentFragment;
+ private BootstrapButton mImportButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -95,21 +96,11 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
importKeys();
}
});
- // mImportSignUploadButton = (BootstrapButton) findViewById(R.id.import_sign_and_upload);
- // mImportSignUploadButton.setOnClickListener(new OnClickListener() {
- // @Override
- // public void onClick(View v) {
- // signAndUploadOnClick();
- // }
- // });
getSupportActionBar().setDisplayShowTitleEnabled(false);
setupDrawerNavigation(savedInstanceState);
- // set actionbar without home button if called from another app
- // ActionBarHelper.setBackButton(this);
-
// set drop down navigation
mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
Context context = getSupportActionBar().getThemedContext();
@@ -125,6 +116,8 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
protected void handleActions(Bundle savedInstanceState, Intent intent) {
String action = intent.getAction();
Bundle extras = intent.getExtras();
+ Uri dataUri = intent.getData();
+ String scheme = intent.getScheme();
if (extras == null) {
extras = new Bundle();
@@ -140,6 +133,15 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
}
/**
+ * Scanning a fingerprint directly with Barcode Scanner
+ */
+ if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(FINGERPRINT_SCHEME)) {
+ getSupportActionBar().setSelectedNavigationItem(0);
+ loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[0]);
+ loadFromFingerprintUri(dataUri);
+ }
+
+ /**
* Keychain's own Actions
*/
if (ACTION_IMPORT_KEY.equals(action)) {
@@ -160,14 +162,25 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
// directly load data
startListFragment(savedInstanceState, importData, null);
}
+ } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)) {
+ if (!extras.containsKey(EXTRA_QUERY)) {
+ Log.e(Constants.TAG, "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query' extra!");
+ return;
+ }
+
+ String query = extras.getString(EXTRA_QUERY);
+
+ // TODO: implement KEYSERVER!
+
} else {
- // Internal actions
+ // Other actions
startListFragment(savedInstanceState, null, null);
if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
getSupportActionBar().setSelectedNavigationItem(1);
loadFragment(ImportKeysFileFragment.class, null, mNavigationStrings[1]);
} else if (ACTION_IMPORT_KEY_FROM_QR_CODE.equals(action)) {
+ // also exposed in AndroidManifest
getSupportActionBar().setSelectedNavigationItem(2);
loadFragment(ImportKeysQrCodeFragment.class, null, mNavigationStrings[2]);
} else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) {
@@ -239,6 +252,23 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
ft.commit();
}
+ public void loadFromFingerprintUri(Uri dataUri) {
+ String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH);
+
+ Log.d(Constants.TAG, "fingerprint: " + fingerprint);
+
+ if (fingerprint.length() < 16) {
+ Toast.makeText(this, R.string.import_qr_code_too_short_fingerprint,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ Intent queryIntent = new Intent(this, KeyServerQueryActivity.class);
+ queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID);
+ queryIntent.putExtra(KeyServerQueryActivity.EXTRA_FINGERPRINT, fingerprint);
+ startActivity(queryIntent);
+ }
+
public void loadCallback(byte[] importData, String importFilename) {
mListFragment.loadNew(importData, importFilename);
}
@@ -413,19 +443,6 @@ public class ImportKeysActivity extends DrawerActivity implements OnNavigationLi
}
}
- public void importOnClick() {
- importKeys();
- }
-
- // public void signAndUploadOnClick() {
- // // first, import!
- // // importOnClick(view);
- //
- // // TODO: implement sign and upload!
- // Toast.makeText(ImportKeysActivity.this, "Not implemented right now!", Toast.LENGTH_SHORT)
- // .show();
- // }
-
/**
* NFC
*/
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index 3ede641d3..9d7d16a42 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -26,6 +26,7 @@ import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@@ -101,8 +102,8 @@ public class ImportKeysQrCodeFragment extends Fragment {
Log.d(Constants.TAG, "scanResult content: " + scanResult.getContents());
// look if it's fingerprint only
- if (scanResult.getContents().toLowerCase(Locale.ENGLISH).startsWith("openpgp4fpr")) {
- importFingerprint(scanResult.getContents().toLowerCase(Locale.ENGLISH));
+ if (scanResult.getContents().toLowerCase(Locale.ENGLISH).startsWith(ImportKeysActivity.FINGERPRINT_SCHEME)) {
+ importFingerprint(Uri.parse(scanResult.getContents()));
return;
}
@@ -128,21 +129,8 @@ public class ImportKeysQrCodeFragment extends Fragment {
}
}
- private void importFingerprint(String uri) {
- String fingerprint = uri.split(":")[1];
-
- Log.d(Constants.TAG, "fingerprint: " + fingerprint);
-
- if (fingerprint.length() < 16) {
- Toast.makeText(getActivity(), R.string.import_qr_code_too_short_fingerprint,
- Toast.LENGTH_LONG).show();
- return;
- }
-
- Intent queryIntent = new Intent(getActivity(), KeyServerQueryActivity.class);
- queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID);
- queryIntent.putExtra(KeyServerQueryActivity.EXTRA_FINGERPRINT, fingerprint);
- startActivity(queryIntent);
+ public void importFingerprint(Uri dataUri) {
+ mImportActivity.loadFromFingerprintUri(dataUri);
}
private void importParts(String[] parts) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index c985f1f60..5d8885e2f 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -32,10 +32,12 @@ import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysServerFragment extends Fragment {
private BootstrapButton mButton;
+ String mQuery;
+
/**
* Creates new instance of this fragment
*/
- public static ImportKeysServerFragment newInstance() {
+ public static ImportKeysServerFragment newInstance(String query) {
ImportKeysServerFragment frag = new ImportKeysServerFragment();
Bundle args = new Bundle();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
index 1a100a585..3ad82ea0b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
@@ -83,7 +83,7 @@ public class ImportKeysListEntry implements Serializable {
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
- .getFingerprint());
+ .getFingerprint(), true);
this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId);
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
index 771816bfc..48ed5310b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
@@ -40,12 +40,14 @@ import android.widget.TextView;
import com.actionbarsherlock.app.SherlockDialogFragment;
public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
- private static final String ARG_URI = "uri";
+ private static final String ARG_KEY_URI = "uri";
private static final String ARG_FINGERPRINT_ONLY = "fingerprint_only";
private ImageView mImage;
private TextView mText;
+ private boolean mFingerprintOnly;
+
private ArrayList<String> mContentList;
private int mCounter;
@@ -53,15 +55,11 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
/**
* Creates new instance of this dialog fragment
- *
- * @param content
- * Content to be shared via QR Codes
- * @return
*/
public static ShareQrCodeDialogFragment newInstance(Uri dataUri, boolean fingerprintOnly) {
ShareQrCodeDialogFragment frag = new ShareQrCodeDialogFragment();
Bundle args = new Bundle();
- args.putParcelable(ARG_URI, dataUri);
+ args.putParcelable(ARG_KEY_URI, dataUri);
args.putBoolean(ARG_FINGERPRINT_ONLY, fingerprintOnly);
frag.setArguments(args);
@@ -76,8 +74,8 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
- Uri dataUri = getArguments().getParcelable(ARG_URI);
- boolean fingerprintOnly = getArguments().getBoolean(ARG_FINGERPRINT_ONLY);
+ Uri dataUri = getArguments().getParcelable(ARG_KEY_URI);
+ mFingerprintOnly = getArguments().getBoolean(ARG_FINGERPRINT_ONLY);
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
@@ -90,29 +88,31 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
mImage = (ImageView) view.findViewById(R.id.share_qr_code_dialog_image);
mText = (TextView) view.findViewById(R.id.share_qr_code_dialog_text);
- // TODO
- long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
-
String content = null;
- if (fingerprintOnly) {
+ if (mFingerprintOnly) {
content = "openpgp4fpr:";
- String fingerprint = PgpKeyHelper.convertKeyToHex(masterKeyId);
-
- mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " "
- + fingerprint);
+ byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
+ mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
content = content + fingerprint;
Log.d(Constants.TAG, "content: " + content);
alert.setPositiveButton(R.string.btn_okay, null);
+
+ setQrCode(content);
} else {
mText.setText(R.string.share_qr_code_dialog_start);
+ // TODO
+ long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
+
+
// get public keyring as ascii armored string
ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
- getActivity(), dataUri, new long[] { masterKeyId });
+ getActivity(), dataUri, new long[]{masterKeyId});
// TODO: binary?
@@ -122,13 +122,13 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
// http://stackoverflow.com/questions/2620444/how-to-prevent-a-dialog-from-closing-when-a-button-is-clicked
alert.setPositiveButton(R.string.btn_next, null);
alert.setNegativeButton(android.R.string.cancel, null);
- }
- mContentList = splitString(content, 1000);
+ mContentList = splitString(content, 1000);
- // start with first
- mCounter = 0;
- updateQrCode();
+ // start with first
+ mCounter = 0;
+ updatePartsQrCode();
+ }
return alert.create();
}
@@ -136,41 +136,47 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
@Override
public void onResume() {
super.onResume();
- AlertDialog alertDialog = (AlertDialog) getDialog();
- final Button backButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
- final Button nextButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
-
- backButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mCounter > 0) {
- mCounter--;
- updateQrCode();
- updateDialog(backButton, nextButton);
- } else {
- dismiss();
+
+ if (!mFingerprintOnly) {
+ AlertDialog alertDialog = (AlertDialog) getDialog();
+ final Button backButton = alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+ final Button nextButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
+
+ backButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mCounter > 0) {
+ mCounter--;
+ updatePartsQrCode();
+ updateDialog(backButton, nextButton);
+ } else {
+ dismiss();
+ }
}
- }
- });
- nextButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
-
- if (mCounter < mContentList.size() - 1) {
- mCounter++;
- updateQrCode();
- updateDialog(backButton, nextButton);
- } else {
- dismiss();
+ });
+ nextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ if (mCounter < mContentList.size() - 1) {
+ mCounter++;
+ updatePartsQrCode();
+ updateDialog(backButton, nextButton);
+ } else {
+ dismiss();
+ }
}
- }
- });
+ });
+ }
}
- private void updateQrCode() {
+ private void updatePartsQrCode() {
// Content: <counter>,<size>,<content>
- mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(mCounter + "," + mContentList.size()
- + "," + mContentList.get(mCounter), QR_CODE_SIZE));
+ setQrCode(mCounter + "," + mContentList.size() + "," + mContentList.get(mCounter));
+ }
+
+ private void setQrCode(String data) {
+ mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(data, QR_CODE_SIZE));
}
private void updateDialog(Button backButton, Button nextButton) {
@@ -191,7 +197,7 @@ public class ShareQrCodeDialogFragment extends SherlockDialogFragment {
/**
* Split String by number of characters
- *
+ *
* @param text
* @param size
* @return
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml
index 2cbc81cf7..79daef590 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml
@@ -3,8 +3,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
android:orientation="vertical" >
<TextView
diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_clipboard_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_clipboard_fragment.xml
index abde4e972..046768495 100644
--- a/OpenPGP-Keychain/src/main/res/layout/import_keys_clipboard_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_clipboard_fragment.xml
@@ -8,7 +8,7 @@
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_clipboard_button"
android:layout_width="match_parent"
- android:layout_height="60dp"
+ android:layout_height="70dp"
android:layout_margin="10dp"
android:text="@string/import_clipboard_button"
bootstrapbutton:bb_icon_left="fa-clipboard"
diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_keyserver_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_keyserver_fragment.xml
index 74a2d7853..e866e05dc 100644
--- a/OpenPGP-Keychain/src/main/res/layout/import_keys_keyserver_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_keyserver_fragment.xml
@@ -7,7 +7,7 @@
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_keyserver_button"
android:layout_width="match_parent"
- android:layout_height="60dp"
+ android:layout_height="70dp"
android:layout_margin="10dp"
android:text="@string/menu_key_server"
bootstrapbutton:bb_size="default"
diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_nfc_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_nfc_fragment.xml
index 203cc6ad6..2a8e74fc2 100644
--- a/OpenPGP-Keychain/src/main/res/layout/import_keys_nfc_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_nfc_fragment.xml
@@ -3,13 +3,13 @@
xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_margin="10dp"
+ android:padding="10dp"
android:orientation="horizontal" >
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_nfc_button"
android:layout_width="wrap_content"
- android:layout_height="60dp"
+ android:layout_height="70dp"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="10dp"
diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_qr_code_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_qr_code_fragment.xml
index 5229e7cf5..472c05e65 100644
--- a/OpenPGP-Keychain/src/main/res/layout/import_keys_qr_code_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_qr_code_fragment.xml
@@ -8,7 +8,7 @@
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_qrcode_button"
android:layout_width="match_parent"
- android:layout_height="60dp"
+ android:layout_height="70dp"
android:layout_margin="10dp"
android:text="@string/import_qr_scan_button"
bootstrapbutton:bb_icon_left="fa-barcode"
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_server_export.xml b/OpenPGP-Keychain/src/main/res/layout/key_server_export.xml
index cfbf7d79d..c162a6e28 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_server_export.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_server_export.xml
@@ -8,8 +8,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
android:orientation="vertical" >
<TextView
diff --git a/OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml b/OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml
index 98c602e88..07f63b91b 100644
--- a/OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/sign_key_activity.xml
@@ -8,8 +8,8 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
android:orientation="vertical" >
<TextView
diff --git a/README.md b/README.md
index 110880423..9a1e24091 100644
--- a/README.md
+++ b/README.md
@@ -57,12 +57,12 @@ I am using the newest [Android Studio](http://developer.android.com/sdk/installi
All Intents require user interaction, e.g. to finally encrypt the user needs to press the "Encrypt" button.
To do automatic encryption/decryption/sign/verify use the OpenPGP Remote API.
-#### Android Intent actions provided by OpenPGP Keychain:
+#### Android Intent actions:
* ``android.intent.action.VIEW`` connected to .gpg and .asc files: Import Key and Decrypt
* ``android.intent.action.SEND`` connected to all mime types (text/plain and every binary data like files and images): Encrypt and Decrypt
-#### OpenPGP Keychain specific Intent actions:
+#### OpenPGP Keychain Intent actions:
* ``org.sufficientlysecure.keychain.action.ENCRYPT``
* To encrypt or sign text, use extra ``text`` (type: ``String``)
@@ -74,8 +74,14 @@ To do automatic encryption/decryption/sign/verify use the OpenPGP Remote API.
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY``
* Extras: ``key_bytes`` (type: ``byte[]``)
* or set data ``Uri`` (``intent.setData()``) pointing to a file
+* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_KEYSERVER``
+ * Extras: ``query`` (type: ``String``)
* ``org.sufficientlysecure.keychain.action.IMPORT_KEY_FROM_QR_CODE``
* without extras, starts Barcode Scanner to get QR Code
+
+#### OpenPGP Keychain special registered Intents:
+* ``android.intent.action.VIEW`` with URIs following the ``openpgp4fpr`` schema. For example: ``openpgp4fpr:718C070100012282``. This is used in QR Codes, but could also be embedded into your website. (compatible with Monkeysphere's and Guardian Project's QR Codes)
+* NFC (``android.nfc.action.NDEF_DISCOVERED``) on mime type ``application/pgp-keys`` (as specified in http://tools.ietf.org/html/rfc3156, section 7)
### OpenPGP Remote API
To do asyncronous fast encryption/decryption/sign/verify operations bind to the OpenPGP remote service.