key lists somewhat wrapped into one, using the new database stuff, Apg cleaned up a bit, preparing to use the database there as well

This commit is contained in:
Thialfihar 2010-05-26 00:33:26 +00:00
parent e83f1e7b3a
commit 6f28f5ee4e
13 changed files with 773 additions and 1319 deletions

View File

@ -36,8 +36,6 @@ import java.security.SecureRandom;
import java.security.Security; import java.security.Security;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.HashMap; import java.util.HashMap;
@ -126,9 +124,6 @@ public class Apg {
CompressionAlgorithmTags.BZIP2, CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP }; CompressionAlgorithmTags.ZIP };
protected static Vector<PGPPublicKeyRing> mPublicKeyRings = new Vector<PGPPublicKeyRing>();
protected static Vector<PGPSecretKeyRing> mSecretKeyRings = new Vector<PGPSecretKeyRing>();
public static Pattern PGP_MESSAGE = public static Pattern PGP_MESSAGE =
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
Pattern.DOTALL); Pattern.DOTALL);
@ -137,13 +132,6 @@ public class Apg {
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
Pattern.DOTALL); Pattern.DOTALL);
protected static boolean mInitialized = false;
protected static HashMap<Long, Integer> mSecretKeyIdToIdMap;
protected static HashMap<Long, PGPSecretKeyRing> mSecretKeyIdToKeyRingMap;
protected static HashMap<Long, Integer> mPublicKeyIdToIdMap;
protected static HashMap<Long, PGPPublicKeyRing> mPublicKeyIdToKeyRingMap;
public static final String PUBLIC_KEY_PROJECTION[] = public static final String PUBLIC_KEY_PROJECTION[] =
new String[] { new String[] {
PublicKeys._ID, PublicKeys._ID,
@ -177,104 +165,6 @@ public class Apg {
} }
} }
static {
mPublicKeyRings = new Vector<PGPPublicKeyRing>();
mSecretKeyRings = new Vector<PGPSecretKeyRing>();
mSecretKeyIdToIdMap = new HashMap<Long, Integer>();
mSecretKeyIdToKeyRingMap = new HashMap<Long, PGPSecretKeyRing>();
mPublicKeyIdToIdMap = new HashMap<Long, Integer>();
mPublicKeyIdToKeyRingMap = new HashMap<Long, PGPPublicKeyRing>();
}
public static void initialize(Activity context) {
if (mInitialized) {
return;
}
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Constants.path.app_dir);
if (!dir.exists() && !dir.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
}
}
loadKeyRings(context, Id.type.public_key);
loadKeyRings(context, Id.type.secret_key);
mInitialized = true;
}
public static class PublicKeySorter implements Comparator<PGPPublicKeyRing> {
@Override
public int compare(PGPPublicKeyRing object1, PGPPublicKeyRing object2) {
PGPPublicKey key1 = getMasterKey(object1);
PGPPublicKey key2 = getMasterKey(object2);
if (key1 == null && key2 == null) {
return 0;
}
if (key1 == null) {
return -1;
}
if (key2 == null) {
return 1;
}
String uid1 = getMainUserId(key1);
String uid2 = getMainUserId(key2);
if (uid1 == null && uid2 == null) {
return 0;
}
if (uid1 == null) {
return -1;
}
if (uid2 == null) {
return 1;
}
return uid1.compareTo(uid2);
}
}
public static class SecretKeySorter implements Comparator<PGPSecretKeyRing> {
@Override
public int compare(PGPSecretKeyRing object1, PGPSecretKeyRing object2) {
PGPSecretKey key1 = getMasterKey(object1);
PGPSecretKey key2 = getMasterKey(object2);
if (key1 == null && key2 == null) {
return 0;
}
if (key1 == null) {
return -1;
}
if (key2 == null) {
return 1;
}
String uid1 = getMainUserId(key1);
String uid2 = getMainUserId(key2);
if (uid1 == null && uid2 == null) {
return 0;
}
if (uid1 == null) {
return -1;
}
if (uid2 == null) {
return 1;
}
return uid1.compareTo(uid2);
}
}
public static void setEditPassPhrase(String passPhrase) { public static void setEditPassPhrase(String passPhrase) {
mEditPassPhrase = passPhrase; mEditPassPhrase = passPhrase;
} }
@ -290,7 +180,7 @@ public class Apg {
public static String getCachedPassPhrase(long keyId) { public static String getCachedPassPhrase(long keyId) {
long realId = keyId; long realId = keyId;
if (realId != Id.key.symmetric) { if (realId != Id.key.symmetric) {
PGPSecretKeyRing keyRing = findSecretKeyRing(keyId); PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
if (keyRing == null) { if (keyRing == null) {
return null; return null;
} }
@ -623,8 +513,6 @@ public class Apg {
saveKeyRing(context, secretKeyRing); saveKeyRing(context, secretKeyRing);
saveKeyRing(context, publicKeyRing); saveKeyRing(context, publicKeyRing);
loadKeyRings(context, Id.type.public_key);
loadKeyRings(context, Id.type.secret_key);
progress.setProgress(R.string.progress_done, 100, 100); progress.setProgress(R.string.progress_done, 100, 100);
} }
@ -754,7 +642,6 @@ public class Apg {
} }
progress.setProgress(R.string.progress_reloadingKeys, 100, 100); progress.setProgress(R.string.progress_reloadingKeys, 100, 100);
loadKeyRings(context, type);
returnData.putInt("added", newKeys); returnData.putInt("added", newKeys);
returnData.putInt("updated", oldKeys); returnData.putInt("updated", oldKeys);
@ -808,61 +695,6 @@ public class Apg {
return returnData; return returnData;
} }
private static void loadKeyRings(Activity context, int type) {
Cursor cursor;
if (type == Id.type.secret_key) {
mSecretKeyRings.clear();
mSecretKeyIdToIdMap.clear();
mSecretKeyIdToKeyRingMap.clear();
cursor = context.managedQuery(SecretKeys.CONTENT_URI, SECRET_KEY_PROJECTION,
null, null, null);
} else {
mPublicKeyRings.clear();
mPublicKeyIdToIdMap.clear();
mPublicKeyIdToKeyRingMap.clear();
cursor = context.managedQuery(PublicKeys.CONTENT_URI, PUBLIC_KEY_PROJECTION,
null, null, null);
}
for (int i = 0; i < cursor.getCount(); ++i) {
cursor.moveToPosition(i);
String sharedIdColumn = PublicKeys._ID; // same in both
String sharedKeyIdColumn = PublicKeys.KEY_ID; // same in both
String sharedKeyDataColumn = PublicKeys.KEY_DATA; // same in both
int idIndex = cursor.getColumnIndex(sharedIdColumn);
int keyIdIndex = cursor.getColumnIndex(sharedKeyIdColumn);
int keyDataIndex = cursor.getColumnIndex(sharedKeyDataColumn);
byte keyData[] = cursor.getBlob(keyDataIndex);
int id = cursor.getInt(idIndex);
long keyId = cursor.getLong(keyIdIndex);
try {
if (type == Id.type.secret_key) {
PGPSecretKeyRing key = new PGPSecretKeyRing(keyData);
mSecretKeyRings.add(key);
mSecretKeyIdToIdMap.put(keyId, id);
mSecretKeyIdToKeyRingMap.put(keyId, key);
} else {
PGPPublicKeyRing key = new PGPPublicKeyRing(keyData);
mPublicKeyRings.add(key);
mPublicKeyIdToIdMap.put(keyId, id);
mPublicKeyIdToKeyRingMap.put(keyId, key);
}
} catch (IOException e) {
// TODO: some error handling
} catch (PGPException e) {
// TODO: some error handling
}
}
if (type == Id.type.secret_key) {
Collections.sort(mSecretKeyRings, new SecretKeySorter());
} else {
Collections.sort(mPublicKeyRings, new PublicKeySorter());
}
}
public static Date getCreationDate(PGPPublicKey key) { public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime(); return key.getCreationTime();
} }
@ -987,7 +819,7 @@ public class Apg {
} }
public static PGPPublicKey getEncryptPublicKey(long masterKeyId) { public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
PGPPublicKeyRing keyRing = mPublicKeyIdToKeyRingMap.get(masterKeyId); PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
if (keyRing == null) { if (keyRing == null) {
return null; return null;
} }
@ -999,7 +831,7 @@ public class Apg {
} }
public static PGPSecretKey getSigningKey(long masterKeyId) { public static PGPSecretKey getSigningKey(long masterKeyId) {
PGPSecretKeyRing keyRing = mSecretKeyIdToKeyRingMap.get(masterKeyId); PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
if (keyRing == null) { if (keyRing == null) {
return null; return null;
} }
@ -1040,14 +872,6 @@ public class Apg {
return userId; return userId;
} }
public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
return mPublicKeyIdToKeyRingMap.get(keyId);
}
public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
return mSecretKeyIdToKeyRingMap.get(keyId);
}
public static boolean isEncryptionKey(PGPPublicKey key) { public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) { if (!key.isEncryptionKey()) {
return false; return false;
@ -1127,9 +951,17 @@ public class Apg {
} }
public static String getAlgorithmInfo(PGPPublicKey key) { public static String getAlgorithmInfo(PGPPublicKey key) {
return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength());
}
public static String getAlgorithmInfo(PGPSecretKey key) {
return getAlgorithmInfo(key.getPublicKey());
}
public static String getAlgorithmInfo(int algorithm, int keySize) {
String algorithmStr = null; String algorithmStr = null;
switch (key.getAlgorithm()) { switch (algorithm) {
case PGPPublicKey.RSA_ENCRYPT: case PGPPublicKey.RSA_ENCRYPT:
case PGPPublicKey.RSA_GENERAL: case PGPPublicKey.RSA_GENERAL:
case PGPPublicKey.RSA_SIGN: { case PGPPublicKey.RSA_SIGN: {
@ -1153,96 +985,38 @@ public class Apg {
break; break;
} }
} }
return algorithmStr + ", " + key.getBitStrength() + "bit"; return algorithmStr + ", " + keySize + "bit";
}
public static String getAlgorithmInfo(PGPSecretKey key) {
return getAlgorithmInfo(key.getPublicKey());
} }
public static void deleteKey(Activity context, PGPPublicKeyRing keyRing) { public static void deleteKey(Activity context, PGPPublicKeyRing keyRing) {
PGPPublicKey masterKey = getMasterKey(keyRing); PGPPublicKey masterKey = getMasterKey(keyRing);
Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID()); Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
context.getContentResolver().delete(uri, null, null); context.getContentResolver().delete(uri, null, null);
loadKeyRings(context, Id.type.public_key);
} }
public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) { public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) {
PGPSecretKey masterKey = getMasterKey(keyRing); PGPSecretKey masterKey = getMasterKey(keyRing);
Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID()); Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
context.getContentResolver().delete(uri, null, null); context.getContentResolver().delete(uri, null, null);
loadKeyRings(context, Id.type.secret_key);
} }
public static PGPPublicKey findPublicKey(long keyId) { public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
PGPPublicKey key = null; // TODO
for (int i = 0; i < mPublicKeyRings.size(); ++i) {
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
try {
key = keyRing.getPublicKey(keyId);
if (key != null) {
return key;
}
} catch (PGPException e) {
// just not found, can ignore this
}
}
return null; return null;
} }
public static PGPSecretKey findSecretKey(long keyId) { public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
PGPSecretKey key = null; // TODO
for (int i = 0; i < mSecretKeyRings.size(); ++i) {
PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
key = keyRing.getSecretKey(keyId);
if (key != null) {
return key;
}
}
return null; return null;
} }
public static PGPSecretKeyRing findSecretKeyRing(long keyId) { public static PGPSecretKey getSecretKey(long keyId) {
for (int i = 0; i < mSecretKeyRings.size(); ++i) { // TODO
PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
PGPSecretKey key = null;
key = keyRing.getSecretKey(keyId);
if (key != null) {
return keyRing;
}
}
return null; return null;
} }
public static PGPPublicKeyRing findPublicKeyRing(long keyId) { public static PGPPublicKey getPublicKey(long keyId) {
for (int i = 0; i < mPublicKeyRings.size(); ++i) { // TODO
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
PGPPublicKey key = null;
try {
key = keyRing.getPublicKey(keyId);
if (key != null) {
return keyRing;
}
} catch (PGPException e) {
// key not found
}
}
return null;
}
public static PGPPublicKey getPublicMasterKey(long keyId) {
PGPPublicKey key = null;
for (int i = 0; i < mPublicKeyRings.size(); ++i) {
PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
try {
key = keyRing.getPublicKey(keyId);
if (key != null) {
return getMasterKey(keyRing);
}
} catch (PGPException e) {
// just not found, can ignore this
}
}
return null; return null;
} }
@ -1282,7 +1056,7 @@ public class Apg {
} }
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
signingKeyRing = findSecretKeyRing(signatureKeyId); signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId); signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) { if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed)); throw new GeneralException(context.getString(R.string.error_signatureFailed));
@ -1398,7 +1172,7 @@ public class Apg {
throw new GeneralException(context.getString(R.string.error_noSignatureKey)); throw new GeneralException(context.getString(R.string.error_noSignatureKey));
} }
signingKeyRing = findSecretKeyRing(signatureKeyId); signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId); signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) { if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed)); throw new GeneralException(context.getString(R.string.error_signatureFailed));
@ -1484,7 +1258,7 @@ public class Apg {
if (obj instanceof PGPPublicKeyEncryptedData) { if (obj instanceof PGPPublicKeyEncryptedData) {
gotAsymmetricEncryption = true; gotAsymmetricEncryption = true;
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj; PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
secretKey = findSecretKey(pbe.getKeyID()); secretKey = getSecretKey(pbe.getKeyID());
if (secretKey != null) { if (secretKey != null) {
break; break;
} }
@ -1594,7 +1368,7 @@ public class Apg {
Object obj = it.next(); Object obj = it.next();
if (obj instanceof PGPPublicKeyEncryptedData) { if (obj instanceof PGPPublicKeyEncryptedData) {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
secretKey = findSecretKey(encData.getKeyID()); secretKey = getSecretKey(encData.getKeyID());
if (secretKey != null) { if (secretKey != null) {
pbe = encData; pbe = encData;
break; break;
@ -1643,7 +1417,7 @@ public class Apg {
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
for (int i = 0; i < sigList.size(); ++i) { for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i); signature = sigList.get(i);
signatureKey = findPublicKey(signature.getKeyID()); signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) { if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
} }
@ -1653,7 +1427,7 @@ public class Apg {
signatureIndex = i; signatureIndex = i;
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
String userId = null; String userId = null;
PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId); PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) { if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing)); userId = getMainUserId(getMasterKey(sigKeyRing));
} }
@ -1788,7 +1562,7 @@ public class Apg {
PGPPublicKey signatureKey = null; PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) { for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i); signature = sigList.get(i);
signatureKey = findPublicKey(signature.getKeyID()); signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) { if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
} }
@ -1797,7 +1571,7 @@ public class Apg {
} else { } else {
signatureKeyId = signature.getKeyID(); signatureKeyId = signature.getKeyID();
String userId = null; String userId = null;
PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId); PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) { if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing)); userId = getMainUserId(getMasterKey(sigKeyRing));
} }
@ -1840,15 +1614,6 @@ public class Apg {
return returnData; return returnData;
} }
public static Vector<PGPPublicKeyRing> getPublicKeyRings() {
return mPublicKeyRings;
}
public static Vector<PGPSecretKeyRing> getSecretKeyRings() {
return mSecretKeyRings;
}
// taken from ClearSignedFileProcessor in BC // taken from ClearSignedFileProcessor in BC
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
throws IOException { throws IOException {

View File

@ -47,7 +47,7 @@ public class AskForSecretKeyPassPhrase {
secretKey = null; secretKey = null;
alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption)); alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
} else { } else {
secretKey = Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId)); secretKey = Apg.getSecretKey(secretKeyId);
if (secretKey == null) { if (secretKey == null) {
return null; return null;
} }

View File

@ -32,6 +32,7 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -68,7 +69,15 @@ public class BaseActivity extends Activity
if (mPreferences == null) { if (mPreferences == null) {
mPreferences = getPreferences(MODE_PRIVATE); mPreferences = getPreferences(MODE_PRIVATE);
} }
Apg.initialize(this);
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
File dir = new File(Constants.path.app_dir);
if (!dir.exists() && !dir.mkdirs()) {
// ignore this for now, it's not crucial
// that the directory doesn't exist at this point
}
}
if (mCacheTimer == null) { if (mCacheTimer == null) {
setPassPhraseCacheTimer(); setPassPhraseCacheTimer();
} }

View File

@ -279,7 +279,7 @@ public class EncryptActivity extends BaseActivity {
long signatureKeyId = extras.getLong("signatureKeyId"); long signatureKeyId = extras.getLong("signatureKeyId");
long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds"); long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds");
if (signatureKeyId != 0) { if (signatureKeyId != 0) {
PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId); PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId);
PGPSecretKey masterKey = null; PGPSecretKey masterKey = null;
if (keyRing != null) { if (keyRing != null) {
masterKey = Apg.getMasterKey(keyRing); masterKey = Apg.getMasterKey(keyRing);
@ -295,7 +295,7 @@ public class EncryptActivity extends BaseActivity {
if (encryptionKeyIds != null) { if (encryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>(); Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < encryptionKeyIds.length; ++i) { for (int i = 0; i < encryptionKeyIds.length; ++i) {
PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]); PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]);
PGPPublicKey masterKey = null; PGPPublicKey masterKey = null;
if (keyRing == null) { if (keyRing == null) {
continue; continue;

View File

@ -80,6 +80,11 @@ public final class Id {
public static final int export_keys = 0x21070002; public static final int export_keys = 0x21070002;
} }
public static final class database {
public static final int type_public = 0;
public static final int type_secret = 1;
}
public static final class type { public static final class type {
public static final int public_key = 0x21070001; public static final int public_key = 0x21070001;
public static final int secret_key = 0x21070002; public static final int secret_key = 0x21070002;
@ -87,11 +92,6 @@ public final class Id {
public static final int key = 0x21070004; public static final int key = 0x21070004;
} }
public static final class database {
public static final int type_public = 0;
public static final int type_secret = 1;
}
public static final class choice { public static final class choice {
public static final class algorithm { public static final class algorithm {
public static final int dsa = 0x21070001; public static final int dsa = 0x21070001;

View File

@ -0,0 +1,633 @@
/*
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thialfihar.android.apg;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.thialfihar.android.apg.provider.Database;
import org.thialfihar.android.apg.provider.KeyRings;
import org.thialfihar.android.apg.provider.Keys;
import org.thialfihar.android.apg.provider.UserIds;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class KeyListActivity extends BaseActivity {
protected ExpandableListView mList;
protected int mSelectedItem = -1;
protected int mTask = 0;
protected String mImportFilename = Constants.path.app_dir + "/";
protected String mExportFilename = Constants.path.app_dir + "/";
protected int mKeyType = Id.type.public_key;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mList.setAdapter(new KeyListAdapter(this));
registerForContextMenu(mList);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.import_keys: {
showDialog(Id.dialog.import_keys);
return true;
}
case Id.menu.option.export_keys: {
showDialog(Id.dialog.export_keys);
return true;
}
default: {
return super.onOptionsItemSelected(item);
}
}
}
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case Id.dialog.delete_key: {
final long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
// TODO: String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
String userId = "SOME KEY";
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.warning);
builder.setMessage(getString(mKeyType == Id.type.public_key ?
R.string.keyDeletionConfirmation :
R.string.secretKeyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(keyId);
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, getString(R.string.title_importKeys),
getString(R.string.specifyFileToImportFrom),
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_titleOpen),
getString(R.string.filemanager_btnOpen),
Id.request.filename);
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = (singleKeyExport ?
getString(R.string.title_exportKey) :
getString(R.string.title_exportKeys));
final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
getString(R.string.specifyFileToExportTo),
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_btnSave),
Id.request.filename);
}
default: {
return super.onCreateDialog(id);
}
}
}
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, mKeyType, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
// TODO
/*
if (mSelectedItem == -1) {
for (PGPSecretKeyRing key : Apg.getSecretKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getSecretKeyRings().get(mSelectedItem));
}
if (mSelectedItem == -1) {
for (PGPPublicKeyRing key : Apg.getPublicKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getPublicKeyRings().get(mSelectedItem));
}*/
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = "" + e;
} catch (PGPException e) {
error = "" + e;
} catch (Apg.GeneralException e) {
error = "" + e;
}
if (mTask == Id.task.import_keys) {
data.putInt("type", Id.message.import_done);
} else {
data.putInt("type", Id.message.export_done);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
protected void deleteKey(long keyId) {
// TODO
//PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(index);
//Apg.deleteKey(this, keyRing);
refreshList();
}
protected void refreshList() {
((KeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString("error");
if (error != null) {
Toast.makeText(KeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int added = data.getInt("added");
int updated = data.getInt("updated");
String message;
if (added > 0 && updated > 0) {
message = getString(R.string.keysAddedAndUpdated, added, updated);
} else if (added > 0) {
message = getString(R.string.keysAdded, added);
} else if (updated > 0) {
message = getString(R.string.keysUpdated, updated);
} else {
message = getString(R.string.noKeysAddedOrUpdated);
}
Toast.makeText(KeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
refreshList();
break;
}
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString("error");
if (error != null) {
Toast.makeText(KeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int exported = data.getInt("exported");
String message;
if (exported == 1) {
message = getString(R.string.keyExported);
} else if (exported > 0) {
message = getString(R.string.keysExported);
} else{
message = getString(R.string.noKeysExported);
}
Toast.makeText(KeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
}
protected class KeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
private Vector<Vector<KeyChild>> mChildren;
private SQLiteDatabase mDatabase;
private Cursor mCursor;
private class KeyChild {
public static final int KEY = 0;
public static final int USER_ID = 1;
public int type;
public String userId;
public long keyId;
public boolean isMasterKey;
public int algorithm;
public int keySize;
public boolean canSign;
public boolean canEncrypt;
public KeyChild(long keyId, boolean isMasterKey, int algorithm, int keySize,
boolean canSign, boolean canEncrypt) {
this.keyId = keyId;
this.isMasterKey = isMasterKey;
this.algorithm = algorithm;
this.keySize = keySize;
this.canSign = canSign;
this.canEncrypt = canEncrypt;
}
public KeyChild(String userId) {
type = USER_ID;
this.userId = userId;
}
}
public KeyListAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDatabase = new Database(context).getReadableDatabase();
mCursor = mDatabase.query(
KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
"(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
") " +
" INNER JOIN " + UserIds.TABLE_NAME + " ON " +
"(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ",
new String[] {
KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
},
KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
new String[] { "" + (mKeyType == Id.type.public_key ?
Id.database.type_public :
Id.database.type_secret) },
null, null, null);
mChildren = new Vector<Vector<KeyChild>>();
for (int i = 0; i < mCursor.getCount(); ++i) {
mChildren.add(null);
}
}
@Override
protected void finalize() throws Throwable {
mCursor.close();
mDatabase.close();
super.finalize();
}
protected Vector<KeyChild> getChildrenOfGroup(int groupPosition) {
Vector<KeyChild> children = mChildren.get(groupPosition);
if (children != null) {
return children;
}
mCursor.moveToPosition(groupPosition);
children = new Vector<KeyChild>();
Cursor c = mDatabase.query(Keys.TABLE_NAME,
new String[] {
Keys._ID, // 0
Keys.KEY_ID, // 1
Keys.IS_MASTER_KEY, // 2
Keys.ALGORITHM, // 3
Keys.KEY_SIZE, // 4
Keys.CAN_SIGN, // 5
Keys.CAN_ENCRYPT, // 6
},
Keys.KEY_RING_ID + " = ?",
new String[] { mCursor.getString(0) },
null, null, Keys.RANK + " ASC");
long masterKeyId = -1;
for (int i = 0; i < c.getCount(); ++i) {
c.moveToPosition(i);
children.add(new KeyChild(c.getLong(1), c.getInt(2) == 1, c.getInt(3), c.getInt(4),
c.getInt(5) == 1, c.getInt(6) == 1));
if (i == 0) {
masterKeyId = c.getInt(0);
}
}
c.close();
if (masterKeyId != -1) {
c = mDatabase.query(UserIds.TABLE_NAME,
new String[] {
UserIds.USER_ID, // 0
},
UserIds.KEY_ID + " = ?",
new String[] { "" + masterKeyId },
null, null, UserIds.RANK + " ASC");
for (int i = 0; i < c.getCount(); ++i) {
c.moveToPosition(i);
children.add(new KeyChild(c.getString(0)));
}
c.close();
}
mChildren.set(groupPosition, children);
return children;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public int getGroupCount() {
return mCursor.getCount();
}
public Object getChild(int groupPosition, int childPosition) {
return null;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
return getChildrenOfGroup(groupPosition).size();
}
public Object getGroup(int position) {
return position;
}
public long getGroupId(int position) {
mCursor.moveToPosition(position);
return mCursor.getLong(1); // MASTER_KEY_ID
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
mCursor.moveToPosition(groupPosition);
View view = mInflater.inflate(R.layout.key_list_group_item, null);
view.setBackgroundResource(android.R.drawable.list_selector_background);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = mCursor.getString(2); // USER_ID
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
if (mainUserId.getText().length() == 0) {
mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
return view;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView,
ViewGroup parent) {
mCursor.moveToPosition(groupPosition);
Vector<KeyChild> children = getChildrenOfGroup(groupPosition);
KeyChild child = children.get(childPosition);
View view = null;
switch (child.type) {
case KeyChild.KEY: {
if (child.isMasterKey) {
view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
} else {
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
TextView keyId = (TextView) view.findViewById(R.id.keyId);
String keyIdStr = Long.toHexString(child.keyId & 0xffffffffL);
while (keyIdStr.length() < 8) {
keyIdStr = "0" + keyIdStr;
}
keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
keyDetails.setText("(" + algorithmStr + ")");
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
if (!child.canEncrypt) {
encryptIcon.setVisibility(View.GONE);
}
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!child.canSign) {
signIcon.setVisibility(View.GONE);
}
break;
}
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
}
return view;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}

View File

@ -16,54 +16,19 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class PublicKeyListActivity extends BaseActivity {
ExpandableListView mList;
protected int mSelectedItem = -1;
protected int mTask = 0;
private String mImportFilename = Constants.path.app_dir + "/";
private String mExportFilename = Constants.path.app_dir + "/pubexport.asc";
public class PublicKeyListActivity extends KeyListActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
mExportFilename = Constants.path.app_dir + "/pubexport.asc";
mKeyType = Id.type.public_key;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mList.setAdapter(new PublicKeyListAdapter(this));
registerForContextMenu(mList);
} }
@Override @Override
@ -79,507 +44,17 @@ public class PublicKeyListActivity extends BaseActivity {
return true; return true;
} }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case Id.menu.option.import_keys: {
showDialog(Id.dialog.import_keys);
return true;
}
case Id.menu.option.export_keys: {
showDialog(Id.dialog.export_keys);
return true;
}
default: {
return super.onOptionsItemSelected(item);
}
}
}
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info = ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo; (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition); int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition); // TODO: user id? menu.setHeaderTitle("Key");
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, Id.menu.export, 0, R.string.menu_exportKey); menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey); menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
} }
} }
@Override
public boolean onContextItemSelected(MenuItem menuItem) {
ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
return super.onContextItemSelected(menuItem);
}
switch (menuItem.getItemId()) {
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: {
return super.onContextItemSelected(menuItem);
}
}
}
@Override
protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) {
case Id.dialog.delete_key: {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.warning);
builder.setMessage(getString(R.string.keyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, getString(R.string.title_importKeys),
getString(R.string.specifyFileToImportFrom),
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_titleOpen),
getString(R.string.filemanager_btnOpen),
Id.request.filename);
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = (singleKeyExport ?
getString(R.string.title_exportKey) :
getString(R.string.title_exportKeys));
final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
getString(R.string.specifyFileToExportTo),
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_btnSave),
Id.request.filename);
}
default: {
return super.onCreateDialog(id);
}
}
}
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, Id.type.public_key, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPPublicKeyRing key : Apg.getPublicKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getPublicKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = "" + e;
} catch (PGPException e) {
error = "" + e;
} catch (Apg.GeneralException e) {
error = "" + e;
}
if (mTask == Id.task.import_keys) {
data.putInt("type", Id.message.import_done);
} else {
data.putInt("type", Id.message.export_done);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
private void deleteKey(int index) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((PublicKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString("error");
if (error != null) {
Toast.makeText(PublicKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int added = data.getInt("added");
int updated = data.getInt("updated");
String message;
if (added > 0 && updated > 0) {
message = getString(R.string.keysAddedAndUpdated, added, updated);
} else if (added > 0) {
message = getString(R.string.keysAdded, added);
} else if (updated > 0) {
message = getString(R.string.keysUpdated, updated);
} else {
message = getString(R.string.noKeysAddedOrUpdated);
}
Toast.makeText(PublicKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
refreshList();
break;
}
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString("error");
if (error != null) {
Toast.makeText(PublicKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int exported = data.getInt("exported");
String message;
if (exported == 1) {
message = getString(R.string.keyExported);
} else if (exported > 0) {
message = getString(R.string.keysExported);
} else{
message = getString(R.string.noKeysExported);
}
Toast.makeText(PublicKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
}
private static class PublicKeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
private class KeyChild {
public static final int KEY = 0;
public static final int USER_ID = 1;
public int type;
public PGPPublicKey key;
public String userId;
public KeyChild(PGPPublicKey key) {
type = KEY;
this.key = key;
}
public KeyChild(String userId) {
type = USER_ID;
this.userId = userId;
}
}
public PublicKeyListAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
protected Vector<KeyChild> getChildrenOfKeyRing(PGPPublicKeyRing keyRing) {
Vector<KeyChild> children = new Vector<KeyChild>();
PGPPublicKey masterKey = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
children.add(new KeyChild(key));
if (key.isMasterKey()) {
masterKey = key;
}
}
if (masterKey != null) {
boolean isFirst = true;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
if (isFirst) {
// ignore first, it's in the group already
isFirst = false;
continue;
}
children.add(new KeyChild(userId));
}
}
return children;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public int getGroupCount() {
return Apg.getPublicKeyRings().size();
}
public Object getChild(int groupPosition, int childPosition) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
return child;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
return getChildrenOfKeyRing(Apg.getPublicKeyRings().get(groupPosition)).size();
}
public Object getGroup(int position) {
return position;
}
public long getGroupId(int position) {
return position;
}
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
View view;
if (!key.isMasterKey()) {
continue;
}
view = mInflater.inflate(R.layout.key_list_group_item, null);
view.setBackgroundResource(android.R.drawable.list_selector_background);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = Apg.getMainUserId(key);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
if (mainUserId.getText().length() == 0) {
mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
return view;
}
return null;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView,
ViewGroup parent) {
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
View view = null;
switch (child.type) {
case KeyChild.KEY: {
PGPPublicKey key = child.key;
if (key.isMasterKey()) {
view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
} else {
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
TextView keyId = (TextView) view.findViewById(R.id.keyId);
String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
while (keyIdStr.length() < 8) {
keyIdStr = "0" + keyIdStr;
}
keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(key);
keyDetails.setText("(" + algorithmStr + ")");
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
if (!Apg.isEncryptionKey(key)) {
encryptIcon.setVisibility(View.GONE);
}
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!Apg.isSigningKey(key)) {
signIcon.setVisibility(View.GONE);
}
break;
}
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
}
return view;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default: {
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
} }

View File

@ -16,55 +16,24 @@
package org.thialfihar.android.apg; package org.thialfihar.android.apg;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView; import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo; import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener; import android.widget.ExpandableListView.OnChildClickListener;
public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener { public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
ExpandableListView mList;
protected int mSelectedItem = -1;
protected int mTask = 0;
private String mImportFilename = Constants.path.app_dir + "/";
private String mExportFilename = Constants.path.app_dir + "/secexport.asc";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
mExportFilename = Constants.path.app_dir + "/secexport.asc";
mKeyType = Id.type.secret_key;
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.key_list);
mList = (ExpandableListView) findViewById(R.id.list);
mList.setAdapter(new SecretKeyListAdapter(this));
registerForContextMenu(mList);
mList.setOnChildClickListener(this); mList.setOnChildClickListener(this);
} }
@ -86,16 +55,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case Id.menu.option.import_keys: {
showDialog(Id.dialog.import_keys);
return true;
}
case Id.menu.option.export_keys: {
showDialog(Id.dialog.export_keys);
return true;
}
case Id.menu.option.create: { case Id.menu.option.create: {
createKey(); createKey();
return true; return true;
@ -113,12 +72,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
ExpandableListView.ExpandableListContextMenuInfo info = ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo; (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition); int type = ExpandableListView.getPackedPositionType(info.packedPosition);
int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition); // TODO: user id? menu.setHeaderTitle("Key");
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
menu.add(0, Id.menu.edit, 0, R.string.menu_editKey); menu.add(0, Id.menu.edit, 0, R.string.menu_editKey);
menu.add(0, Id.menu.export, 1, R.string.menu_exportKey); menu.add(0, Id.menu.export, 1, R.string.menu_exportKey);
menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey); menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
@ -142,18 +98,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
return true; return true;
} }
case Id.menu.export: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.export_key);
return true;
}
case Id.menu.delete: {
mSelectedItem = groupPosition;
showDialog(Id.dialog.delete_key);
return true;
}
default: { default: {
return super.onContextItemSelected(menuItem); return super.onContextItemSelected(menuItem);
} }
@ -170,96 +114,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
@Override @Override
protected Dialog onCreateDialog(int id) { protected Dialog onCreateDialog(int id) {
boolean singleKeyExport = false;
switch (id) { switch (id) {
case Id.dialog.delete_key: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.warning);
builder.setMessage(getString(R.string.secretKeyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setPositiveButton(R.string.btn_delete,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
case Id.dialog.import_keys: {
return FileDialog.build(this, getString(R.string.title_importKeys),
getString(R.string.specifyFileToImportFrom),
mImportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(Id.dialog.import_keys);
mImportFilename = filename;
importKeys();
}
@Override
public void onCancelClick() {
removeDialog(Id.dialog.import_keys);
}
},
getString(R.string.filemanager_titleOpen),
getString(R.string.filemanager_btnOpen),
Id.request.filename);
}
case Id.dialog.export_key: {
singleKeyExport = true;
// break intentionally omitted, to use the Id.dialog.export_keys dialog
}
case Id.dialog.export_keys: {
String title = (singleKeyExport ?
getString(R.string.title_exportKey) :
getString(R.string.title_exportKeys));
final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
return FileDialog.build(this, title,
getString(R.string.specifyFileToExportSecretKeysTo),
mExportFilename,
new FileDialog.OnClickListener() {
@Override
public void onOkClick(String filename) {
removeDialog(thisDialogId);
mExportFilename = filename;
exportKeys();
}
@Override
public void onCancelClick() {
removeDialog(thisDialogId);
}
},
getString(R.string.filemanager_titleSave),
getString(R.string.filemanager_btnSave),
Id.request.filename);
}
case Id.dialog.pass_phrase: { case Id.dialog.pass_phrase: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this); return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
} }
@ -270,8 +127,7 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
} }
public void checkPassPhraseAndEdit() { public void checkPassPhraseAndEdit() {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
String passPhrase = Apg.getCachedPassPhrase(keyId); String passPhrase = Apg.getCachedPassPhrase(keyId);
if (passPhrase == null) { if (passPhrase == null) {
showDialog(Id.dialog.pass_phrase); showDialog(Id.dialog.pass_phrase);
@ -295,8 +151,7 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
} }
private void editKey() { private void editKey() {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem); long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
Intent intent = new Intent(this, EditKeyActivity.class); Intent intent = new Intent(this, EditKeyActivity.class);
intent.putExtra("keyId", keyId); intent.putExtra("keyId", keyId);
startActivityForResult(intent, Id.message.edit_key); startActivityForResult(intent, Id.message.edit_key);
@ -313,24 +168,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
break; break;
} }
case Id.request.filename: {
if (resultCode == RESULT_OK && data != null) {
String filename = data.getDataString();
if (filename != null) {
// Get rid of URI prefix:
if (filename.startsWith("file://")) {
filename = filename.substring(7);
}
// replace %20 and so on
filename = Uri.decode(filename);
FileDialog.setFilename(filename);
}
}
return;
}
default: { default: {
break; break;
} }
@ -338,320 +175,4 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
super.onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data);
} }
public void importKeys() {
showDialog(Id.dialog.importing);
mTask = Id.task.import_keys;
startThread();
}
public void exportKeys() {
showDialog(Id.dialog.exporting);
mTask = Id.task.export_keys;
startThread();
}
@Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
String filename = null;
if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
if (mTask == Id.task.import_keys) {
data = Apg.importKeyRings(this, Id.type.secret_key, filename, this);
} else {
Vector<Object> keys = new Vector<Object>();
if (mSelectedItem == -1) {
for (PGPSecretKeyRing key : Apg.getSecretKeyRings()) {
keys.add(key);
}
} else {
keys.add(Apg.getSecretKeyRings().get(mSelectedItem));
}
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = "" + e;
} catch (PGPException e) {
error = "" + e;
} catch (Apg.GeneralException e) {
error = "" + e;
}
if (mTask == Id.task.import_keys) {
data.putInt("type", Id.message.import_done);
} else {
data.putInt("type", Id.message.export_done);
}
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
sendMessage(msg);
}
private void deleteKey(int index) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(index);
Apg.deleteKey(this, keyRing);
refreshList();
}
private void refreshList() {
((SecretKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
@Override
public void doneCallback(Message msg) {
super.doneCallback(msg);
Bundle data = msg.getData();
if (data != null) {
int type = data.getInt("type");
switch (type) {
case Id.message.import_done: {
removeDialog(Id.dialog.importing);
String error = data.getString("error");
if (error != null) {
Toast.makeText(SecretKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int added = data.getInt("added");
int updated = data.getInt("updated");
String message;
if (added > 0 && updated > 0) {
message = getString(R.string.keysAddedAndUpdated, added, updated);
} else if (added > 0) {
message = getString(R.string.keysAdded, added);
} else if (updated > 0) {
message = getString(R.string.keysUpdated, updated);
} else {
message = getString(R.string.noKeysAddedOrUpdated);
}
Toast.makeText(SecretKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
refreshList();
break;
}
case Id.message.export_done: {
removeDialog(Id.dialog.exporting);
String error = data.getString("error");
if (error != null) {
Toast.makeText(SecretKeyListActivity.this,
getString(R.string.errorMessage, data.getString("error")),
Toast.LENGTH_SHORT).show();
} else {
int exported = data.getInt("exported");
String message;
if (exported == 1) {
message = getString(R.string.keyExported);
} else if (exported > 0) {
message = getString(R.string.keysExported);
} else{
message = getString(R.string.noKeysExported);
}
Toast.makeText(SecretKeyListActivity.this, message,
Toast.LENGTH_SHORT).show();
}
break;
}
default: {
break;
}
}
}
}
private static class SecretKeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
private class KeyChild {
static final int KEY = 0;
static final int USER_ID = 1;
public int type;
public PGPSecretKey key;
public String userId;
public KeyChild(PGPSecretKey key) {
type = KEY;
this.key = key;
}
public KeyChild(String userId) {
type = USER_ID;
this.userId = userId;
}
}
public SecretKeyListAdapter(Context context) {
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
protected Vector<KeyChild> getChildrenOfKeyRing(PGPSecretKeyRing keyRing) {
Vector<KeyChild> children = new Vector<KeyChild>();
PGPSecretKey masterKey = null;
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
children.add(new KeyChild(key));
if (key.isMasterKey()) {
masterKey = key;
}
}
if (masterKey != null) {
boolean isFirst = true;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
if (isFirst) {
// ignore first, it's in the group already
isFirst = false;
continue;
}
children.add(new KeyChild(userId));
}
}
return children;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public int getGroupCount() {
return Apg.getSecretKeyRings().size();
}
public Object getChild(int groupPosition, int childPosition) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
return child;
}
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
public int getChildrenCount(int groupPosition) {
return getChildrenOfKeyRing(Apg.getSecretKeyRings().get(groupPosition)).size();
}
public Object getGroup(int position) {
return position;
}
public long getGroupId(int position) {
return position;
}
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
View view;
if (!key.isMasterKey()) {
continue;
}
view = mInflater.inflate(R.layout.key_list_group_item, null);
view.setBackgroundResource(android.R.drawable.list_selector_background);
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = Apg.getMainUserId(key);
if (userId != null) {
String chunks[] = userId.split(" <", 2);
userId = chunks[0];
if (chunks.length > 1) {
mainUserIdRest.setText("<" + chunks[1]);
}
mainUserId.setText(userId);
}
if (mainUserId.getText().length() == 0) {
mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
return view;
}
return null;
}
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView,
ViewGroup parent) {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
Vector<KeyChild> children = getChildrenOfKeyRing(keyRing);
KeyChild child = children.get(childPosition);
View view = null;
switch (child.type) {
case KeyChild.KEY: {
PGPSecretKey key = child.key;
if (key.isMasterKey()) {
view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
} else {
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
TextView keyId = (TextView) view.findViewById(R.id.keyId);
String keyIdStr = Long.toHexString(key.getKeyID() & 0xffffffffL);
while (keyIdStr.length() < 8) {
keyIdStr = "0" + keyIdStr;
}
keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(key);
keyDetails.setText("(" + algorithmStr + ")");
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
if (!Apg.isEncryptionKey(key)) {
encryptIcon.setVisibility(View.GONE);
}
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!Apg.isSigningKey(key)) {
signIcon.setVisibility(View.GONE);
}
break;
}
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
}
return view;
}
}
} }

View File

@ -21,12 +21,10 @@ import java.util.HashMap;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher; import android.content.UriMatcher;
import android.database.Cursor; import android.database.Cursor;
import android.database.SQLException; import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;

View File

@ -34,9 +34,9 @@ public class Database extends SQLiteOpenHelper {
public static final String AUTHORITY = "org.thialfihar.android.apg.database"; public static final String AUTHORITY = "org.thialfihar.android.apg.database";
private static HashMap<String, String> sKeyRingsProjection; public static HashMap<String, String> sKeyRingsProjection;
private static HashMap<String, String> sKeysProjection; public static HashMap<String, String> sKeysProjection;
private static HashMap<String, String> sUserIdsProjection; public static HashMap<String, String> sUserIdsProjection;
private SQLiteDatabase mCurrentDb = null; private SQLiteDatabase mCurrentDb = null;
@ -67,11 +67,11 @@ public class Database extends SQLiteOpenHelper {
sUserIdsProjection.put(UserIds.RANK, UserIds.RANK); sUserIdsProjection.put(UserIds.RANK, UserIds.RANK);
} }
Database(Context context) { public Database(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION); super(context, DATABASE_NAME, null, DATABASE_VERSION);
//getWritableDatabase(); //getWritableDatabase();
// force upgrade to test things // force upgrade to test things
onUpgrade(getWritableDatabase(), 1, 2); //onUpgrade(getWritableDatabase(), 1, 2);
} }
@Override @Override
@ -161,6 +161,7 @@ public class Database extends SQLiteOpenHelper {
} }
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
cursor.close();
cursor = db.query(SecretKeys.TABLE_NAME, cursor = db.query(SecretKeys.TABLE_NAME,
new String[]{ new String[]{
@ -182,6 +183,7 @@ public class Database extends SQLiteOpenHelper {
} }
} while (cursor.moveToNext()); } while (cursor.moveToNext());
} }
cursor.close();
break; break;
} }
@ -310,6 +312,7 @@ public class Database extends SQLiteOpenHelper {
} }
private long insertOrUpdateKeyRing(ContentValues values) { private long insertOrUpdateKeyRing(ContentValues values) {
boolean grabbedNewDatabase = (mCurrentDb == null);
SQLiteDatabase db = mCurrentDb != null ? mCurrentDb : getWritableDatabase(); SQLiteDatabase db = mCurrentDb != null ? mCurrentDb : getWritableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@ -332,10 +335,17 @@ public class Database extends SQLiteOpenHelper {
rowId = db.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values); rowId = db.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values);
} }
c.close();
if (grabbedNewDatabase) {
db.close();
}
return rowId; return rowId;
} }
private long insertOrUpdateKey(ContentValues values) { private long insertOrUpdateKey(ContentValues values) {
boolean grabbedNewDatabase = (mCurrentDb == null);
SQLiteDatabase db = mCurrentDb != null ? mCurrentDb : getWritableDatabase(); SQLiteDatabase db = mCurrentDb != null ? mCurrentDb : getWritableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@ -358,10 +368,17 @@ public class Database extends SQLiteOpenHelper {
rowId = db.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values); rowId = db.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values);
} }
c.close();
if (grabbedNewDatabase) {
db.close();
}
return rowId; return rowId;
} }
private long insertOrUpdateUserId(ContentValues values) { private long insertOrUpdateUserId(ContentValues values) {
boolean grabbedNewDatabase = (mCurrentDb == null);
SQLiteDatabase db = mCurrentDb != null ? mCurrentDb : getWritableDatabase(); SQLiteDatabase db = mCurrentDb != null ? mCurrentDb : getWritableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@ -384,6 +401,12 @@ public class Database extends SQLiteOpenHelper {
rowId = db.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values); rowId = db.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values);
} }
c.close();
if (grabbedNewDatabase) {
db.close();
}
return rowId; return rowId;
} }
} }

View File

@ -31,4 +31,14 @@ public class KeyRings implements BaseColumns {
public static final String WHO_ID_type = "INTEGER"; public static final String WHO_ID_type = "INTEGER";
public static final String KEY_RING_DATA = "c_key_ring_data"; public static final String KEY_RING_DATA = "c_key_ring_data";
public static final String KEY_RING_DATA_type = "BLOB"; public static final String KEY_RING_DATA_type = "BLOB";
public static final Uri CONTENT_URI =
Uri.parse("content://" + DataProvider.AUTHORITY + "/key_ring");
public static final Uri CONTENT_URI_BY_KEY_ID =
Uri.parse("content://" + DataProvider.AUTHORITY + "/key_ring/key_id");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
} }

View File

@ -16,6 +16,7 @@
package org.thialfihar.android.apg.provider; package org.thialfihar.android.apg.provider;
import android.net.Uri;
import android.provider.BaseColumns; import android.provider.BaseColumns;
public class Keys implements BaseColumns { public class Keys implements BaseColumns {
@ -42,4 +43,12 @@ public class Keys implements BaseColumns {
public static final String KEY_DATA_type = "BLOB"; public static final String KEY_DATA_type = "BLOB";
public static final String RANK = "c_key_data"; public static final String RANK = "c_key_data";
public static final String RANK_type = "INTEGER"; public static final String RANK_type = "INTEGER";
public static final Uri CONTENT_URI =
Uri.parse("content://" + DataProvider.AUTHORITY + "/keys");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.key";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.key";
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
} }

View File

@ -16,6 +16,7 @@
package org.thialfihar.android.apg.provider; package org.thialfihar.android.apg.provider;
import android.net.Uri;
import android.provider.BaseColumns; import android.provider.BaseColumns;
public class UserIds implements BaseColumns { public class UserIds implements BaseColumns {
@ -28,4 +29,14 @@ public class UserIds implements BaseColumns {
public static final String USER_ID_type = "TEXT"; public static final String USER_ID_type = "TEXT";
public static final String RANK = "c_rank"; public static final String RANK = "c_rank";
public static final String RANK_type = "INTEGER"; public static final String RANK_type = "INTEGER";
public static final Uri CONTENT_URI =
Uri.parse("content://" + DataProvider.AUTHORITY + "/user_ids");
public static final Uri CONTENT_URI_BY_KEY_ID =
Uri.parse("content://" + DataProvider.AUTHORITY + "/user_ids/key_id");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
} }