aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThialfihar <thialfihar@gmail.com>2010-06-05 00:33:33 +0000
committerThialfihar <thialfihar@gmail.com>2010-06-05 00:33:33 +0000
commita85ae5e0092057d1007ba62fada17c9f6a085c92 (patch)
tree0de13d1eb3bf345684ed83d6154a08fbebb2fd95
parented2cb1e525778d62949958bbf108687c571e4ebe (diff)
parenta571ce7c5222d1f2246acdfada7c95e48a170dd4 (diff)
downloadopen-keychain-a85ae5e0092057d1007ba62fada17c9f6a085c92.tar.gz
open-keychain-a85ae5e0092057d1007ba62fada17c9f6a085c92.tar.bz2
open-keychain-a85ae5e0092057d1007ba62fada17c9f6a085c92.zip
branching trunk out of latest 1.0.x to get a clean start for it
-rw-r--r--AndroidManifest.xml45
-rw-r--r--res/layout/decrypt.xml1
-rw-r--r--res/layout/select_public_key_item.xml14
-rw-r--r--res/layout/select_secret_key_item.xml14
-rw-r--r--res/values-de/strings.xml24
-rw-r--r--res/values-ko/strings.xml24
-rw-r--r--res/values-ru/strings.xml24
-rw-r--r--res/values-sl/strings.xml242
-rw-r--r--res/values/strings.xml16
-rw-r--r--src/org/openintents/intents/FileManager.java78
-rw-r--r--src/org/thialfihar/android/apg/Apg.java668
-rw-r--r--src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java60
-rw-r--r--src/org/thialfihar/android/apg/BaseActivity.java81
-rw-r--r--src/org/thialfihar/android/apg/CachedPassPhrase.java6
-rw-r--r--src/org/thialfihar/android/apg/DecryptActivity.java148
-rw-r--r--src/org/thialfihar/android/apg/EditKeyActivity.java30
-rw-r--r--src/org/thialfihar/android/apg/EncryptActivity.java184
-rw-r--r--src/org/thialfihar/android/apg/FileDialog.java13
-rw-r--r--src/org/thialfihar/android/apg/Id.java5
-rw-r--r--src/org/thialfihar/android/apg/KeyListActivity.java642
-rw-r--r--src/org/thialfihar/android/apg/MailListActivity.java8
-rw-r--r--src/org/thialfihar/android/apg/MainActivity.java53
-rw-r--r--src/org/thialfihar/android/apg/PreferencesActivity.java1
-rw-r--r--src/org/thialfihar/android/apg/PublicKeyListActivity.java535
-rw-r--r--src/org/thialfihar/android/apg/SecretKeyListActivity.java497
-rw-r--r--src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java26
-rw-r--r--src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java153
-rw-r--r--src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java164
-rw-r--r--src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java147
-rw-r--r--src/org/thialfihar/android/apg/Service.java81
-rw-r--r--src/org/thialfihar/android/apg/provider/Accounts.java13
-rw-r--r--src/org/thialfihar/android/apg/provider/DataProvider.java572
-rw-r--r--src/org/thialfihar/android/apg/provider/Database.java605
-rw-r--r--src/org/thialfihar/android/apg/provider/KeyRings.java (renamed from src/org/thialfihar/android/apg/provider/Accounts1.java)69
-rw-r--r--src/org/thialfihar/android/apg/provider/Keys.java51
-rw-r--r--src/org/thialfihar/android/apg/provider/PublicKeys.java22
-rw-r--r--src/org/thialfihar/android/apg/provider/PublicKeys1.java42
-rw-r--r--src/org/thialfihar/android/apg/provider/SecretKeys1.java42
-rw-r--r--src/org/thialfihar/android/apg/provider/UserIds.java (renamed from src/org/thialfihar/android/apg/provider/SecretKeys.java)17
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/KeyEditor.java2
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/SectionView.java16
41 files changed, 2840 insertions, 2595 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 03a653c66..f9a9e3f13 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -17,7 +17,7 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.thialfihar.android.apg"
- android:versionName="0.9.5" android:versionCode="10">
+ android:versionCode="15" android:versionName="1.0.1">
<application
android:icon="@drawable/icon"
@@ -53,12 +53,28 @@
<activity
android:name=".SelectPublicKeyListActivity"
android:label="@string/title_selectRecipients"
- android:configChanges="keyboardHidden|orientation|keyboard"/>
+ android:configChanges="keyboardHidden|orientation|keyboard">
+
+ <intent-filter>
+ <action android:name="org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="text/*"/>
+ </intent-filter>
+
+ </activity>
<activity
android:name=".SelectSecretKeyListActivity"
android:label="@string/title_selectSignature"
- android:configChanges="keyboardHidden|orientation|keyboard"/>
+ android:configChanges="keyboardHidden|orientation|keyboard">
+
+ <intent-filter>
+ <action android:name="org.thialfihar.android.apg.intent.SELECT_SECRET_KEY" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="text/*"/>
+ </intent-filter>
+
+ </activity>
<activity
android:name=".EncryptActivity"
@@ -68,6 +84,9 @@
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT" />
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" />
+ <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="text/*"/>
</intent-filter>
</activity>
@@ -92,6 +111,9 @@
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.DECRYPT" />
<action android:name="org.thialfihar.android.apg.intent.DECRYPT_FILE" />
+ <action android:name="org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="text/*"/>
</intent-filter>
</activity>
@@ -106,13 +128,22 @@
android:label="@string/title_preferences"
android:configChanges="keyboardHidden|orientation|keyboard"/>
+ <service android:name=".Service" />
+
<provider
+ android:readPermission="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
android:name="org.thialfihar.android.apg.provider.DataProvider"
- android:authorities="org.thialfihar.android.apg.provider" />
+ android:authorities="org.thialfihar.android.apg.provider"/>
</application>
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="5" />
-<uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" />
-<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
-</manifest> \ No newline at end of file
+ <permission android:name="org.thialfihar.android.apg.permission.READ_KEY_DETAILS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permission_read_key_details_label"
+ android:description="@string/permission_read_key_details_description"/>
+
+ <uses-permission android:name="com.google.android.providers.gmail.permission.READ_GMAIL" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+</manifest>
diff --git a/res/layout/decrypt.xml b/res/layout/decrypt.xml
index 03e2f6311..41bbee479 100644
--- a/res/layout/decrypt.xml
+++ b/res/layout/decrypt.xml
@@ -77,6 +77,7 @@
<EditText
android:id="@+id/message"
android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
+ android:scrollHorizontally="true"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="top"/>
diff --git a/res/layout/select_public_key_item.xml b/res/layout/select_public_key_item.xml
index bb0dd30a4..beca23176 100644
--- a/res/layout/select_public_key_item.xml
+++ b/res/layout/select_public_key_item.xml
@@ -70,20 +70,6 @@
android:layout_height="fill_parent"/>
<TextView
- android:id="@+id/creation"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="31.12.2009"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
- android:id="@+id/expiry"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="31.12.2010"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
android:id="@+id/status"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="expired"
diff --git a/res/layout/select_secret_key_item.xml b/res/layout/select_secret_key_item.xml
index 35bf58e23..022545152 100644
--- a/res/layout/select_secret_key_item.xml
+++ b/res/layout/select_secret_key_item.xml
@@ -63,20 +63,6 @@
android:layout_height="fill_parent"/>
<TextView
- android:id="@+id/creation"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="31.12.2009"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
- android:id="@+id/expiry"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="31.12.2010"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
android:id="@+id/status"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="expired"
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 5bb4dc2ba..008981b0e 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -38,10 +38,12 @@
<string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string>
+ <string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string>
<string name="section_keys">Keys</string>
+ <string name="section_general">General</string>
<string name="section_defaults">Defaults</string>
<!-- btn_lowerCase: capitalized words, no punctuation -->
@@ -92,6 +94,9 @@
<string name="label_hashAlgorithm">Hash Algorithm</string>
<string name="label_asymmetric">Public Key</string>
<string name="label_symmetric">Pass Phrase</string>
+ <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string>
+ <string name="label_messageCompression">Message Compression</string>
+ <string name="label_fileCompression">File Compression</string>
<string name="noKeysSelected">Select</string>
<string name="oneKeySelected">1 Selected</string>
@@ -108,9 +113,16 @@
<string name="notValid">not valid</string>
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_none">None</string>
<string name="choice_signOnly">Sign only</string>
<string name="choice_encryptOnly">Encrypt only</string>
<string name="choice_signAndEncrypt">Sign and Encrypt</string>
+ <string name="choice_15secs">15 secs</string>
+ <string name="choice_1min">1 min</string>
+ <string name="choice_3mins">3 mins</string>
+ <string name="choice_5mins">5 mins</string>
+ <string name="choice_10mins">10 mins</string>
+ <string name="choice_untilQuit">until quit</string>
<string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string>
@@ -133,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
- <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string>
+ <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@@ -150,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
- <string name="specifyFileToImportFrom">Please specify which file to import from.</string>
+ <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
- <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string>
- <string name="keysAdded">Succssfully added %s keys.</string>
- <string name="keysUpdated">Succssfully updated %s keys.</string>
+ <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
+ <string name="keysAdded">Succssfully added %s key(s).</string>
+ <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
+ <string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" -->
@@ -227,4 +240,3 @@
<string name="progress_verifyingIntegrity">verifying integrity...</string>
</resources>
-
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 5bb4dc2ba..008981b0e 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -38,10 +38,12 @@
<string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string>
+ <string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string>
<string name="section_keys">Keys</string>
+ <string name="section_general">General</string>
<string name="section_defaults">Defaults</string>
<!-- btn_lowerCase: capitalized words, no punctuation -->
@@ -92,6 +94,9 @@
<string name="label_hashAlgorithm">Hash Algorithm</string>
<string name="label_asymmetric">Public Key</string>
<string name="label_symmetric">Pass Phrase</string>
+ <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string>
+ <string name="label_messageCompression">Message Compression</string>
+ <string name="label_fileCompression">File Compression</string>
<string name="noKeysSelected">Select</string>
<string name="oneKeySelected">1 Selected</string>
@@ -108,9 +113,16 @@
<string name="notValid">not valid</string>
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_none">None</string>
<string name="choice_signOnly">Sign only</string>
<string name="choice_encryptOnly">Encrypt only</string>
<string name="choice_signAndEncrypt">Sign and Encrypt</string>
+ <string name="choice_15secs">15 secs</string>
+ <string name="choice_1min">1 min</string>
+ <string name="choice_3mins">3 mins</string>
+ <string name="choice_5mins">5 mins</string>
+ <string name="choice_10mins">10 mins</string>
+ <string name="choice_untilQuit">until quit</string>
<string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string>
@@ -133,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
- <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string>
+ <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@@ -150,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
- <string name="specifyFileToImportFrom">Please specify which file to import from.</string>
+ <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
- <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string>
- <string name="keysAdded">Succssfully added %s keys.</string>
- <string name="keysUpdated">Succssfully updated %s keys.</string>
+ <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
+ <string name="keysAdded">Succssfully added %s key(s).</string>
+ <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
+ <string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" -->
@@ -227,4 +240,3 @@
<string name="progress_verifyingIntegrity">verifying integrity...</string>
</resources>
-
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 5bb4dc2ba..008981b0e 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -38,10 +38,12 @@
<string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string>
+ <string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string>
<string name="section_keys">Keys</string>
+ <string name="section_general">General</string>
<string name="section_defaults">Defaults</string>
<!-- btn_lowerCase: capitalized words, no punctuation -->
@@ -92,6 +94,9 @@
<string name="label_hashAlgorithm">Hash Algorithm</string>
<string name="label_asymmetric">Public Key</string>
<string name="label_symmetric">Pass Phrase</string>
+ <string name="label_passPhraseCacheTtl">Pass Phrase Cache</string>
+ <string name="label_messageCompression">Message Compression</string>
+ <string name="label_fileCompression">File Compression</string>
<string name="noKeysSelected">Select</string>
<string name="oneKeySelected">1 Selected</string>
@@ -108,9 +113,16 @@
<string name="notValid">not valid</string>
<!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_none">None</string>
<string name="choice_signOnly">Sign only</string>
<string name="choice_encryptOnly">Encrypt only</string>
<string name="choice_signAndEncrypt">Sign and Encrypt</string>
+ <string name="choice_15secs">15 secs</string>
+ <string name="choice_1min">1 min</string>
+ <string name="choice_3mins">3 mins</string>
+ <string name="choice_5mins">5 mins</string>
+ <string name="choice_10mins">10 mins</string>
+ <string name="choice_untilQuit">until quit</string>
<string name="dsa">DSA</string>
<string name="elgamal">ElGamal</string>
@@ -133,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
- <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string>
+ <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@@ -150,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
- <string name="specifyFileToImportFrom">Please specify which file to import from.</string>
+ <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
- <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string>
- <string name="keysAdded">Succssfully added %s keys.</string>
- <string name="keysUpdated">Succssfully updated %s keys.</string>
+ <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
+ <string name="keysAdded">Succssfully added %s key(s).</string>
+ <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
+ <string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" -->
@@ -227,4 +240,3 @@
<string name="progress_verifyingIntegrity">verifying integrity...</string>
</resources>
-
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
new file mode 100644
index 000000000..8bb198ad6
--- /dev/null
+++ b/res/values-sl/strings.xml
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+ <string name="app_name">APG</string>
+
+ <!-- title_lowerCase: capitalized words, no punctuation -->
+ <string name="title_mailInbox">Poštni nabiralnik</string>
+ <string name="title_managePublicKeys">Upravljanje javnih ključev</string>
+ <string name="title_manageSecretKeys">Upravljanje zasebnih ključev</string>
+ <string name="title_selectRecipients">Izberi prejemnike</string>
+ <string name="title_selectSignature">Izberi podpis</string>
+ <string name="title_encrypt">Šifriraj</string>
+ <string name="title_decrypt">Dešifriraj</string>
+ <string name="title_authentification">Avtentikacija</string>
+ <string name="title_createKey">Ustvari ključ</string>
+ <string name="title_editKey">Uredi ključ</string>
+ <string name="title_preferences">Nastavitve</string>
+ <string name="title_changePassPhrase">Spremeni geslo</string>
+ <string name="title_setPassPhrase">Določi geslo</string>
+ <string name="title_sendEmail">"Pošlji e-pošto..."</string>
+ <string name="title_encryptToFile">Šifriraj v datoteko</string>
+ <string name="title_decryptToFile">Dešifriraj v datoteko</string>
+ <string name="title_addAccount">Dodaj račun</string>
+ <string name="title_importKeys">Uvozi ključe</string>
+ <string name="title_exportKey">Izvozi ključ</string>
+ <string name="title_exportKeys">Izvozi ključe</string>
+ <string name="title_keyNotFound">Ključ ni bil najden</string>
+
+ <!-- section_lowerCase: capitalized words, no punctuation -->
+ <string name="section_userIds">Uporabniške identitete</string>
+ <string name="section_keys">Ključi</string>
+ <string name="section_general">Splošno</string>
+ <string name="section_defaults">Privzete nastavitve</string>
+
+ <!-- btn_lowerCase: capitalized words, no punctuation -->
+ <string name="btn_encryptToClipboard">Šifriraj v odložišče</string>
+ <string name="btn_send">Šifriraj in pošlji</string>
+ <string name="btn_encrypt">Šifriraj</string>
+ <string name="btn_decrypt">Dešifriraj</string>
+ <string name="btn_verify">Overi</string>
+ <string name="btn_selectEncryptKeys">Izberi prejemnike</string>
+ <string name="btn_reply">Odgovori</string>
+ <string name="btn_encryptMessage">Šifriraj sporočilo</string>
+ <string name="btn_decryptMessage">Dešifriraj sporočilo</string>
+ <string name="btn_encryptFile">Šifriraj datoteko</string>
+ <string name="btn_decryptFile">Dešifriraj datoteko</string>
+ <string name="btn_save">Shrani</string>
+ <string name="btn_doNotSave">Prekliči</string>
+ <string name="btn_delete">Izbriši</string>
+ <string name="btn_noDate">Brez</string>
+
+ <!-- menu_lowerCase: capitalized words, no punctuation -->
+ <string name="menu_about">O programu</string>
+ <string name="menu_addAccount">Dodaj GMail račun</string>
+ <string name="menu_deleteAccount">Izbriši račun</string>
+ <string name="menu_managePublicKeys">Upravljanje javnih ključev</string>
+ <string name="menu_manageSecretKeys">Upravljanje zasebnih ključev</string>
+ <string name="menu_preferences">Nastavitve</string>
+ <string name="menu_changePassPhrase">Spremeni geslo</string>
+ <string name="menu_setPassPhrase">Določi geslo</string>
+ <string name="menu_importKeys">Uvozi ključe</string>
+ <string name="menu_exportKeys">Izvozi ključe</string>
+ <string name="menu_exportKey">Izvozi ključ</string>
+ <string name="menu_deleteKey">Izbriši ključ</string>
+ <string name="menu_createKey">Ustvari ključ</string>
+ <string name="menu_editKey">Uredi ključ</string>
+
+ <!-- label_lowerCase: capitalized words, no punctuation -->
+ <string name="label_sign">Podpiši</string>
+ <string name="label_message">Sporočilo</string>
+ <string name="label_file">Datoteka</string>
+ <string name="label_passPhrase">Geslo</string>
+ <string name="label_passPhraseAgain">Ponovi</string>
+ <string name="label_algorithm">Algoritem</string>
+ <string name="label_asciiArmour">ASCII Armour</string>
+ <string name="label_selectPublicKeys">Javni ključ(i)</string>
+ <string name="label_deleteAfterEncryption">Po šifriranju izbriši</string>
+ <string name="label_deleteAfterDecryption">Po dešifriranju izbriši</string>
+ <string name="label_encryptionAlgorithm">Šifrirni algoritem</string>
+ <string name="label_hashAlgorithm">Hash algoritem</string>
+ <string name="label_asymmetric">Javni ključ</string>
+ <string name="label_symmetric">Geslo</string>
+ <string name="label_passPhraseCacheTtl">Predpomnilnik gesel</string>
+ <string name="label_messageCompression">Zgoščevanje sporočil</string>
+ <string name="label_fileCompression">Zgoščevanje datotek</string>
+
+ <string name="noKeysSelected">Izberi</string>
+ <string name="oneKeySelected">1 izbran</string>
+ <string name="nKeysSelected">Izbrani</string>
+ <string name="unknownUserId">&lt;nepoznan&gt;</string>
+ <string name="none">&lt;brez&gt;</string>
+ <string name="noKey">&lt;brez ključa&gt;</string>
+ <string name="noDate">-</string>
+ <string name="noExpiry">&lt;nikoli&gt;</string>
+ <string name="unknownStatus"></string>
+ <string name="canEncrypt">lahko šifrira</string>
+ <string name="canSign">lahko podpiše</string>
+ <string name="expired">potečeno</string>
+ <string name="notValid">neveljavno</string>
+
+ <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_none">Brez</string>
+ <string name="choice_signOnly">Samo podpis</string>
+ <string name="choice_encryptOnly">Samo šifriranje</string>
+ <string name="choice_signAndEncrypt">Podpis in šifriranje</string>
+ <string name="choice_15secs">15 sek</string>
+ <string name="choice_1min">1 min</string>
+ <string name="choice_3mins">3 min</string>
+ <string name="choice_5mins">5 min</string>
+ <string name="choice_10mins">10 min</string>
+ <string name="choice_untilQuit">do izhoda</string>
+
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+
+ <string name="filemanager_titleOpen">Odpri...</string>
+ <string name="filemanager_titleSave">Shrani kot...</string>
+ <string name="filemanager_titleEncrypt">Izberi datoteko za šifriranje...</string>
+ <string name="filemanager_titleDecrypt">Izberi datoteko za dešifriranje...</string>
+ <string name="filemanager_btnOpen">Odpri</string>
+ <string name="filemanager_btnSave">Shrani</string>
+
+ <string name="warning">Opozorilo</string>
+ <string name="error">Napaka</string>
+ <string name="warningMessage">Opozorilo: %s</string>
+ <string name="errorMessage">Napaka: %s</string>
+
+ <!-- sentences -->
+ <string name="wrongPassPhrase">Napačno geslo.</string>
+ <string name="usingClipboardContent">Uporabljam vsebino odložišča.</string>
+ <string name="keySaved">Ključ shranjen.</string>
+ <string name="setAPassPhrase">Najprej preko menija možnosti določite geslo.</string>
+ <string name="noFilemanagerInstalled">Nameščen ni noben združljiv upravitelj datotek.</string>
+ <string name="passPhrasesDoNotMatch">Gesli se ne ujemata.</string>
+ <string name="passPhraseMustNotBeEmpty">Prazna gesla niso dovoljena.</string>
+ <string name="passPhraseForSymmetricEncryption">Geslo za simetrično enkripcijo:</string>
+ <string name="passPhraseFor">Geslo za %s:</string>
+ <string name="fileDeleteConfirmation">Ali ste prepričani, da želite izbrisati\n%s?</string>
+ <string name="fileDeleteSuccessful">Uspešno izbrisano.</string>
+ <string name="noFileSelected">Najprej izberite datoteko.</string>
+ <string name="decryptionSuccessful">Uspešno dešifrirano.</string>
+ <string name="encryptionSuccessful">Uspešno šifrirano.</string>
+ <string name="encryptionToClipboardSuccessful">Uspešno šifrirano v odložišče.</string>
+ <string name="enterPassPhraseTwice">Vstavite geslo dvakrat.</string>
+ <string name="selectEncryptionKey">Izberite vsaj en šifrirni ključ.</string>
+ <string name="selectEncryptionOrSignatureKey">Izberite vsaj en šifrirni ključ ali ključ za podpis.</string>
+ <string name="specifyFileToEncryptTo">Določite datoteko v katero želite šifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
+ <string name="specifyFileToDecryptTo">Določite datoteko v katero želite dešifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
+ <string name="specifyGoogleMailAccount">Določite Google Mail račun, ki ga želite dodati.</string>
+ <string name="specifyFileToImportFrom">Določite iz katere datoteke želite uvoziti ključe. (.asc ali .gpg)</string>
+ <string name="specifyFileToExportTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
+ <string name="specifyFileToExportSecretKeysTo">Določite v katero datoteko želite izvoziti.\nPOZOR! Izvozili boste ZASEBNI ključ.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.</string>
+ <string name="keyDeletionConfirmation">Ali zares želite izbrisati ključ '%s'?\nTega ne boste mogli popraviti!</string>
+ <string name="secretKeyDeletionConfirmation">Ali zares želite izbrisati ZASEBNI ključ '%s'?\nTega ne boste mogli popraviti!</string>
+ <string name="keysAddedAndUpdated">Uspešno dodani ključi: %s. Uspešno posodobljeni ključi: %s."</string>
+ <string name="keysAdded">Uspešno dodani ključi: %s.</string>
+ <string name="keysUpdated">Uspešno posodobljeni ključi: %s.</string>
+ <string name="noKeysAddedOrUpdated">Noben ključ ni bil dodan ali posodobljen.</string>
+ <string name="keyExported">Uspešno izvožen 1 ključ.</string>
+ <string name="keysExported">Uspešno izvoženi ključi: %s</string>
+ <string name="noKeysExported">Noben ključ ni bil izvožen.</string>
+ <string name="keyCreationElGamalInfo">Opomba: le podključi podpirajo ElGamal. Za ElGamal bo uporabljena velikost najbližja 1536, 2048, 3072, 4096, ali 8192.</string>
+ <string name="keyNotFound">Ne najdem ključa %08X.</string>
+
+ <!-- error_lowerCase: phrases, no punctuation, all lowercase,
+ they will be put after "errorMessage", e.g. "Error: file not found" -->
+ <string name="error_fileDeleteFailed">izbris '%s' ni uspel</string>
+ <string name="error_fileNotFound">ne najdem datoteke</string>
+ <string name="error_noSecretKeyFound">najden ni bil noben ustrezen zasebni kluč</string>
+ <string name="error_noKnownEncryptionFound">najdena ni bila nobena poznana vrsta enkripcije</string>
+ <string name="error_externalStorageNotReady">zunanji pomnilnik ni pripravljen</string>
+ <string name="error_accountNotFound">račun '%s' ni najden</string>
+ <string name="error_addingAccountFailed">dodajanje računa '%s' ni uspelo</string>
+ <string name="error_invalidEmail">neveljaven e-naslov '%s'</string>
+ <string name="error_keySizeMinimum512bit">velikost ključa mora biti vsaj 512bit</string>
+ <string name="error_masterKeyMustNotBeElGamal">statični ključ ne more biti ključ ElGamal</string>
+ <string name="error_unknownAlgorithmChoice">neznana izbira algoritma</string>
+ <string name="error_userIdNeedsAName">določiti morate ime</string>
+ <string name="error_userIdNeedsAnEmailAddress">določiti morate naslov e-pošte</string>
+ <string name="error_keyNeedsAUserId">potrebujem vsaj eno uporabniško identiteto</string>
+ <string name="error_mainUserIdMustNotBeEmpty">glavna uporabniška identiteta ne more biti prazna</string>
+ <string name="error_keyNeedsMasterKey">potrebujem vsaj statični ključ</string>
+ <string name="error_expiryMustComeAfterCreation">datum poteka mora biti kasnejši od datuma nastanka</string>
+ <string name="error_noEncryptionKeysOrPassPhrase">dan ni bil noben šifrirni ključ ali geslo</string>
+ <string name="error_signatureFailed">podpis ni bil uspešen</string>
+ <string name="error_noSignaturePassPhrase">dano ni bilo nobeno geslo</string>
+ <string name="error_noSignatureKey">dan ni bil noben podpisni ključ</string>
+ <string name="error_invalidData">neveljavni šifrirni podatki</string>
+ <string name="error_corruptData">pokvarjeni podatki</string>
+ <string name="error_noSymmetricEncryptionPacket">ne najdem podatkov s simetrično enkripcijo</string>
+ <string name="error_wrongPassPhrase">napačno geslo</string>
+ <string name="error_savingKeys">napaka pri shranjevanju nakaterih ključev</string>
+
+ <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
+ <string name="progress_done">končano.</string>
+ <string name="progress_initializing">inicializiram...</string>
+ <string name="progress_saving">shranjujem...</string>
+ <string name="progress_importing">uvažam...</string>
+ <string name="progress_exporting">izvažam...</string>
+ <string name="progress_generating">generiram ključ, to lahko traja nekaj časa...</string>
+ <string name="progress_buildingKey">gradim ključ...</string>
+ <string name="progress_preparingMasterKey">pripravljam statični ključ...</string>
+ <string name="progress_certifyingMasterKey">potrjujem statični ključ...</string>
+ <string name="progress_buildingMasterKeyRing">gradim datoteko s statičnimi ključi...</string>
+ <string name="progress_addingSubKeys">dodajam podključe...</string>
+ <string name="progress_savingKeyRing">shranjujem datoteko s ključi...</string>
+ <string name="progress_importingSecretKeys">uvažam zasebne ključe...</string>
+ <string name="progress_importingPublicKeys">uvažam javne ključe...</string>
+ <string name="progress_reloadingKeys">reloading keys...</string>
+ <string name="progress_exportingKey">izvažam ključ...</string>
+ <string name="progress_exportingKeys">izvažam ključe...</string>
+ <string name="progress_extractingSignatureKey">izvlačim podpisni kluč...</string>
+ <string name="progress_extractingKey">izvlačim ključ...</string>
+ <string name="progress_preparingStreams">pripravljam tok...</string>
+ <string name="progress_encrypting">šifriram podatke...</string>
+ <string name="progress_decrypting">dešifriram podatke...</string>
+ <string name="progress_preparingSignature">pripravljam podpis...</string>
+ <string name="progress_generatingSignature">generiram podpis...</string>
+ <string name="progress_processingSignature">obdelujem podpis...</string>
+ <string name="progress_verifyingSignature">overovljam podpis...</string>
+ <string name="progress_signing">podpisujem...</string>
+ <string name="progress_readingData">berem podatke...</string>
+ <string name="progress_findingKey">iščem ključ...</string>
+ <string name="progress_decompressingData">raztezam podatke...</string>
+ <string name="progress_verifyingIntegrity">overovljam integriteto...</string>
+
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 856ed0c4e..72c1d1f9c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -38,6 +38,7 @@
<string name="title_importKeys">Import Keys</string>
<string name="title_exportKey">Export Key</string>
<string name="title_exportKeys">Export Keys</string>
+ <string name="title_keyNotFound">Key Not Found</string>
<!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string>
@@ -144,7 +145,7 @@
<string name="usingClipboardContent">Using clipboard content.</string>
<string name="keySaved">Key saved.</string>
<string name="setAPassPhrase">Set a pass phrase via the option menu first.</string>
- <string name="oiFilemanagerNotInstalled">OI File Manager not installed.</string>
+ <string name="noFilemanagerInstalled">No compatible file manager installed.</string>
<string name="passPhrasesDoNotMatch">The pass phrases didn't match.</string>
<string name="passPhraseMustNotBeEmpty">Empty pass phrases are not allowed.</string>
<string name="passPhraseForSymmetricEncryption">Pass phrase for symmetric encryption:</string>
@@ -161,19 +162,20 @@
<string name="specifyFileToEncryptTo">Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToDecryptTo">Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyGoogleMailAccount">Specify the Google Mail account you want to add.</string>
- <string name="specifyFileToImportFrom">Please specify which file to import from.</string>
+ <string name="specifyFileToImportFrom">Please specify which file to import keys from. (.asc or .gpg)</string>
<string name="specifyFileToExportTo">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specifyFileToExportSecretKeysTo">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="keyDeletionConfirmation">Do you really want to delete the key '%s'?\nYou can't undo this!</string>
<string name="secretKeyDeletionConfirmation">Do you really want to delete the SECRET key '%s'?\nYou can't undo this!</string>
- <string name="keysAddedAndUpdated">Succssfully added %s keys and updated %s keys."</string>
- <string name="keysAdded">Succssfully added %s keys.</string>
- <string name="keysUpdated">Succssfully updated %s keys.</string>
+ <string name="keysAddedAndUpdated">Succssfully added %s key(s) and updated %s key(s)."</string>
+ <string name="keysAdded">Succssfully added %s key(s).</string>
+ <string name="keysUpdated">Succssfully updated %s key(s).</string>
<string name="noKeysAddedOrUpdated">No keys added or updated.</string>
<string name="keyExported">Succssfully exported 1 key.</string>
<string name="keysExported">Succssfully exported %s keys.</string>
<string name="noKeysExported">No keys exported.</string>
<string name="keyCreationElGamalInfo">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
+ <string name="keyNotFound">Couldn't find key %08X.</string>
<!-- error_lowerCase: phrases, no punctuation, all lowercase,
they will be put after "errorMessage", e.g. "Error: file not found" -->
@@ -237,5 +239,9 @@
<string name="progress_decompressingData">decompressing data...</string>
<string name="progress_verifyingIntegrity">verifying integrity...</string>
+ <!-- permission strings -->
+ <string name="permission_read_key_details_label">Read key details from APG.</string>
+ <string name="permission_read_key_details_description">Read of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.</string>
+
</resources>
diff --git a/src/org/openintents/intents/FileManager.java b/src/org/openintents/intents/FileManager.java
deleted file mode 100644
index 3a5cc0d86..000000000
--- a/src/org/openintents/intents/FileManager.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2008 OpenIntents.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.openintents.intents;
-
-// Version Dec 9, 2008
-
-/**
- * Provides OpenIntents actions, extras, and categories used by providers.
- * <p>
- * These specifiers extend the standard Android specifiers.
- * </p>
- */
-public final class FileManager {
-
- /**
- * Activity Action: Pick a file through the file manager, or let user
- * specify a custom file name. Data is the current file name or file name
- * suggestion. Returns a new file name as file URI in data.
- *
- * <p>
- * Constant Value: "org.openintents.action.PICK_FILE"
- * </p>
- */
- public static final String ACTION_PICK_FILE = "org.openintents.action.PICK_FILE";
-
- /**
- * Activity Action: Pick a directory through the file manager, or let user
- * specify a custom file name. Data is the current directory name or
- * directory name suggestion. Returns a new directory name as file URI in
- * data.
- *
- * <p>
- * Constant Value: "org.openintents.action.PICK_DIRECTORY"
- * </p>
- */
- public static final String ACTION_PICK_DIRECTORY = "org.openintents.action.PICK_DIRECTORY";
-
- /**
- * The title to display.
- *
- * <p>
- * This is shown in the title bar of the file manager.
- * </p>
- *
- * <p>
- * Constant Value: "org.openintents.extra.TITLE"
- * </p>
- */
- public static final String EXTRA_TITLE = "org.openintents.extra.TITLE";
-
- /**
- * The text on the button to display.
- *
- * <p>
- * Depending on the use, it makes sense to set this to "Open" or "Save".
- * </p>
- *
- * <p>
- * Constant Value: "org.openintents.extra.BUTTON_TEXT"
- * </p>
- */
- public static final String EXTRA_BUTTON_TEXT = "org.openintents.extra.BUTTON_TEXT";
-
-}
diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java
index e290a501e..3c35064a4 100644
--- a/src/org/thialfihar/android/apg/Apg.java
+++ b/src/org/thialfihar/android/apg/Apg.java
@@ -19,6 +19,7 @@ package org.thialfihar.android.apg;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -35,8 +36,6 @@ import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.util.Calendar;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
@@ -80,18 +79,19 @@ import org.bouncycastle2.openpgp.PGPSignatureList;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle2.openpgp.PGPSignatureSubpacketVector;
import org.bouncycastle2.openpgp.PGPUtil;
-import org.thialfihar.android.apg.provider.PublicKeys;
-import org.thialfihar.android.apg.provider.SecretKeys;
+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 org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.ui.widget.UserIdEditor;
import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.Activity;
-import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
-import android.net.Uri;
+import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Environment;
import android.view.ViewGroup;
@@ -102,9 +102,35 @@ public class Apg {
public static final String ENCRYPT = "org.thialfihar.android.apg.intent.ENCRYPT";
public static final String DECRYPT_FILE = "org.thialfihar.android.apg.intent.DECRYPT_FILE";
public static final String ENCRYPT_FILE = "org.thialfihar.android.apg.intent.ENCRYPT_FILE";
- }
-
- public static String VERSION = "0.9.5";
+ public static final String DECRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN";
+ public static final String ENCRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN";
+ public static final String SELECT_PUBLIC_KEYS = "org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS";
+ public static final String SELECT_SECRET_KEY = "org.thialfihar.android.apg.intent.SELECT_SECRET_KEY";
+ }
+
+ public static final String EXTRA_DATA = "data";
+ public static final String EXTRA_STATUS = "status";
+ public static final String EXTRA_ERROR = "error";
+ public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
+ public static final String EXTRA_ENCRYPTED_MESSAGE = "decryptedMessage";
+ public static final String EXTRA_SIGNATURE = "signature";
+ public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
+ public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
+ public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
+ public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
+ public static final String EXTRA_USER_ID = "userId";
+ public static final String EXTRA_KEY_ID = "keyId";
+ public static final String EXTRA_REPLY_TO = "replyTo";
+ public static final String EXTRA_SEND_TO = "sendTo";
+ public static final String EXTRA_SUBJECT = "subject";
+ public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
+ public static final String EXTRA_SELECTION = "selection";
+ public static final String EXTRA_MESSAGE = "message";
+ public static final String EXTRA_PROGRESS = "progress";
+ public static final String EXTRA_MAX = "max";
+ public static final String EXTRA_ACCOUNT = "account";
+
+ public static String VERSION = "1.0.1";
public static String FULL_VERSION = "APG v" + VERSION;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
@@ -125,41 +151,20 @@ public class Apg {
CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP };
- protected static Vector<PGPPublicKeyRing> mPublicKeyRings = new Vector<PGPPublicKeyRing>();
- protected static Vector<PGPSecretKeyRing> mSecretKeyRings = new Vector<PGPSecretKeyRing>();
-
public static Pattern PGP_MESSAGE =
Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
Pattern.DOTALL);
public static Pattern PGP_SIGNED_MESSAGE =
- Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
- 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[] =
- new String[] {
- PublicKeys._ID,
- PublicKeys.KEY_ID,
- PublicKeys.KEY_DATA,
- PublicKeys.WHO_ID, };
- public static final String SECRET_KEY_PROJECTION[] =
- new String[] {
- PublicKeys._ID,
- PublicKeys.KEY_ID,
- PublicKeys.KEY_DATA,
- PublicKeys.WHO_ID, };
+ Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
+ Pattern.DOTALL);
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
new HashMap<Long, CachedPassPhrase>();
private static String mEditPassPhrase = null;
+ private static Database mDatabase = null;
+
public static class GeneralException extends Exception {
static final long serialVersionUID = 0xf812773342L;
@@ -176,102 +181,14 @@ 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 void initialize(Context context) {
+ if (mDatabase == null) {
+ mDatabase = new Database(context);
}
}
- 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 Database getDatabase() {
+ return mDatabase;
}
public static void setEditPassPhrase(String passPhrase) {
@@ -289,7 +206,7 @@ public class Apg {
public static String getCachedPassPhrase(long keyId) {
long realId = keyId;
if (realId != Id.key.symmetric) {
- PGPSecretKeyRing keyRing = findSecretKeyRing(keyId);
+ PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
if (keyRing == null) {
return null;
}
@@ -308,19 +225,30 @@ public class Apg {
return cpp.passPhrase;
}
- public static void cleanUpCache(int ttl) {
+ public static int cleanUpCache(int ttl, int initialDelay) {
+ int delay = initialDelay;
+ long realTtl = ttl * 1000;
long now = new Date().getTime();
-
Vector<Long> oldKeys = new Vector<Long>();
for (Map.Entry<Long, CachedPassPhrase> pair : mPassPhraseCache.entrySet()) {
- if ((now - pair.getValue().timestamp) >= 1000 * ttl) {
+ long lived = now - pair.getValue().timestamp;
+ if (lived >= realTtl) {
oldKeys.add(pair.getKey());
+ } else {
+ // see, whether the remaining time for this cache entry improves our
+ // check delay
+ long nextCheck = realTtl - lived + 1000;
+ if (nextCheck < delay) {
+ delay = (int)nextCheck;
+ }
}
}
for (long keyId : oldKeys) {
mPassPhraseCache.remove(keyId);
}
+
+ return delay;
}
public static PGPSecretKey createKey(Context context,
@@ -413,11 +341,10 @@ public class Apg {
secretKey = (PGPSecretKey) it.next();
}
-
return secretKey;
}
- private static long getNumDatesBetween(GregorianCalendar first, GregorianCalendar second) {
+ private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
GregorianCalendar tmp = new GregorianCalendar();
tmp.setTime(first.getTime());
long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
@@ -434,7 +361,7 @@ public class Apg {
String oldPassPhrase, String newPassPhrase,
ProgressDialogUpdater progress)
throws Apg.GeneralException, NoSuchProviderException, PGPException,
- NoSuchAlgorithmException, SignatureException {
+ NoSuchAlgorithmException, SignatureException, IOException, Database.GeneralException {
progress.setProgress(R.string.progress_buildingKey, 0, 100);
@@ -465,7 +392,7 @@ public class Apg {
} catch (UserIdEditor.NoEmailException e) {
throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
} catch (UserIdEditor.InvalidEmailException e) {
- throw new Apg.GeneralException(e.getMessage());
+ throw new Apg.GeneralException("" + e);
}
if (userId.equals("")) {
@@ -549,11 +476,12 @@ public class Apg {
hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+ // TODO: this doesn't work quite right yet
if (keyEditor.getExpiryDate() != null) {
GregorianCalendar creationDate = new GregorianCalendar();
creationDate.setTime(getCreationDate(masterKey));
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
- long numDays = getNumDatesBetween(creationDate, expiryDate);
+ long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
}
@@ -600,11 +528,12 @@ public class Apg {
}
hashedPacketsGen.setKeyFlags(true, keyFlags);
+ // TODO: this doesn't work quite right yet
if (keyEditor.getExpiryDate() != null) {
GregorianCalendar creationDate = new GregorianCalendar();
creationDate.setTime(getCreationDate(masterKey));
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
- long numDays = getNumDatesBetween(creationDate, expiryDate);
+ long numDays = getNumDaysBetween(creationDate, expiryDate);
if (numDays <= 0) {
throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
}
@@ -619,79 +548,16 @@ public class Apg {
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
- saveKeyRing(context, secretKeyRing);
- saveKeyRing(context, publicKeyRing);
+ mDatabase.saveKeyRing(secretKeyRing);
+ mDatabase.saveKeyRing(publicKeyRing);
- loadKeyRings(context, Id.type.public_key);
- loadKeyRings(context, Id.type.secret_key);
progress.setProgress(R.string.progress_done, 100, 100);
}
- private static int saveKeyRing(Activity context, PGPPublicKeyRing keyRing) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ContentValues values = new ContentValues();
-
- PGPPublicKey masterKey = getMasterKey(keyRing);
- if (masterKey == null) {
- return Id.return_value.no_master_key;
- }
-
- try {
- keyRing.encode(out);
- out.close();
- } catch (IOException e) {
- return Id.return_value.error;
- }
-
- values.put(PublicKeys.KEY_ID, masterKey.getKeyID());
- values.put(PublicKeys.KEY_DATA, out.toByteArray());
-
- Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- Cursor cursor = context.managedQuery(uri, PUBLIC_KEY_PROJECTION, null, null, null);
- if (cursor != null && cursor.getCount() > 0) {
- context.getContentResolver().update(uri, values, null, null);
- return Id.return_value.updated;
- } else {
- context.getContentResolver().insert(PublicKeys.CONTENT_URI, values);
- return Id.return_value.ok;
- }
- }
-
- private static int saveKeyRing(Activity context, PGPSecretKeyRing keyRing) {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ContentValues values = new ContentValues();
-
- PGPSecretKey masterKey = getMasterKey(keyRing);
- if (masterKey == null) {
- return Id.return_value.no_master_key;
- }
-
- try {
- keyRing.encode(out);
- out.close();
- } catch (IOException e) {
- return Id.return_value.error;
- }
-
- values.put(SecretKeys.KEY_ID, masterKey.getKeyID());
- values.put(SecretKeys.KEY_DATA, out.toByteArray());
-
- Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- Cursor cursor = context.managedQuery(uri, SECRET_KEY_PROJECTION, null, null, null);
- if (cursor != null && cursor.getCount() > 0) {
- context.getContentResolver().update(uri, values, null, null);
- return Id.return_value.updated;
- } else {
- context.getContentResolver().insert(SecretKeys.CONTENT_URI, values);
- return Id.return_value.ok;
- }
- }
-
public static Bundle importKeyRings(Activity context, int type, String filename,
ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle();
- PGPObjectFactory objectFactory = null;
if (type == Id.type.secret_key) {
progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
@@ -704,53 +570,61 @@ public class Apg {
}
FileInputStream fileIn = new FileInputStream(filename);
- InputStream in = PGPUtil.getDecoderStream(fileIn);
- objectFactory = new PGPObjectFactory(in);
-
- Vector<Object> objects = new Vector<Object>();
- Object obj = objectFactory.nextObject();
- while (obj != null) {
- objects.add(obj);
- obj = objectFactory.nextObject();
- }
-
+ long fileSize = new File(filename).length();
+ PositionAwareInputStream progressIn = new PositionAwareInputStream(fileIn);
+ // need to have access to the bufferedInput, so we can reuse it for the possible
+ // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
+ // armour blocks
+ BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
int newKeys = 0;
int oldKeys = 0;
- for (int i = 0; i < objects.size(); ++i) {
- progress.setProgress(i * 100 / objects.size(), 100);
- obj = objects.get(i);
- PGPPublicKeyRing publicKeyRing;
- PGPSecretKeyRing secretKeyRing;
- int retValue;
-
- if (type == Id.type.secret_key) {
- if (!(obj instanceof PGPSecretKeyRing)) {
- continue;
- }
- secretKeyRing = (PGPSecretKeyRing) obj;
- retValue = saveKeyRing(context, secretKeyRing);
- } else {
- if (!(obj instanceof PGPPublicKeyRing)) {
- continue;
+ try {
+ while (true) {
+ InputStream in = PGPUtil.getDecoderStream(bufferedInput);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+ Object obj = objectFactory.nextObject();
+ // if the first is already a null object, then we can stop trying
+ if (obj == null) {
+ break;
}
- publicKeyRing = (PGPPublicKeyRing) obj;
- retValue = saveKeyRing(context, publicKeyRing);
- }
+ while (obj != null) {
+ PGPPublicKeyRing publicKeyRing;
+ PGPSecretKeyRing secretKeyRing;
+ // a return value that doesn't match any Id.return_value.* values, in case
+ // saveKeyRing is never called
+ int retValue = 2107;
- if (retValue == Id.return_value.error) {
- throw new GeneralException(context.getString(R.string.error_savingKeys));
- }
+ try {
+ if (type == Id.type.secret_key && obj instanceof PGPSecretKeyRing) {
+ secretKeyRing = (PGPSecretKeyRing) obj;
+ retValue = mDatabase.saveKeyRing(secretKeyRing);
+ } else if (type == Id.type.public_key && obj instanceof PGPPublicKeyRing) {
+ publicKeyRing = (PGPPublicKeyRing) obj;
+ retValue = mDatabase.saveKeyRing(publicKeyRing);
+ }
+ } catch (IOException e) {
+ retValue = Id.return_value.error;
+ } catch (Database.GeneralException e) {
+ retValue = Id.return_value.error;
+ }
+
+ if (retValue == Id.return_value.error) {
+ throw new GeneralException(context.getString(R.string.error_savingKeys));
+ }
- if (retValue == Id.return_value.updated) {
- ++oldKeys;
- } else if (retValue == Id.return_value.ok) {
- ++newKeys;
+ if (retValue == Id.return_value.updated) {
+ ++oldKeys;
+ } else if (retValue == Id.return_value.ok) {
+ ++newKeys;
+ }
+ progress.setProgress((int)(100 * progressIn.position() / fileSize), 100);
+ obj = objectFactory.nextObject();
+ }
}
+ } catch (EOFException e) {
+ // nothing to do, we are done
}
- progress.setProgress(R.string.progress_reloadingKeys, 100, 100);
- loadKeyRings(context, type);
-
returnData.putInt("added", newKeys);
returnData.putInt("updated", oldKeys);
@@ -759,12 +633,13 @@ public class Apg {
return returnData;
}
- public static Bundle exportKeyRings(Activity context, Vector<Object> keys, String filename,
+ public static Bundle exportKeyRings(Activity context, Vector<Integer> keyRingIds,
+ String filename,
ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle();
- if (keys.size() == 1) {
+ if (keyRingIds.size() == 1) {
progress.setProgress(R.string.progress_exportingKey, 0, 100);
} else {
progress.setProgress(R.string.progress_exportingKeys, 0, 100);
@@ -777,9 +652,9 @@ public class Apg {
ArmoredOutputStream out = new ArmoredOutputStream(fileOut);
int numKeys = 0;
- for (int i = 0; i < keys.size(); ++i) {
- progress.setProgress(i * 100 / keys.size(), 100);
- Object obj = keys.get(i);
+ for (int i = 0; i < keyRingIds.size(); ++i) {
+ progress.setProgress(i * 100 / keyRingIds.size(), 100);
+ Object obj = mDatabase.getKeyRing(keyRingIds.get(i));
PGPPublicKeyRing publicKeyRing;
PGPSecretKeyRing secretKeyRing;
@@ -803,61 +678,6 @@ public class Apg {
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) {
return key.getCreationTime();
}
@@ -867,6 +687,9 @@ public class Apg {
}
public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
if (key.isMasterKey()) {
return key;
@@ -877,6 +700,9 @@ public class Apg {
}
public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
if (key.isMasterKey()) {
return key;
@@ -982,7 +808,7 @@ public class Apg {
}
public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
- PGPPublicKeyRing keyRing = mPublicKeyIdToKeyRingMap.get(masterKeyId);
+ PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
if (keyRing == null) {
return null;
}
@@ -994,7 +820,7 @@ public class Apg {
}
public static PGPSecretKey getSigningKey(long masterKeyId) {
- PGPSecretKeyRing keyRing = mSecretKeyIdToKeyRingMap.get(masterKeyId);
+ PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
if (keyRing == null) {
return null;
}
@@ -1035,14 +861,6 @@ public class Apg {
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) {
if (!key.isEncryptionKey()) {
return false;
@@ -1122,9 +940,17 @@ public class Apg {
}
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;
- switch (key.getAlgorithm()) {
+ switch (algorithm) {
case PGPPublicKey.RSA_ENCRYPT:
case PGPPublicKey.RSA_GENERAL:
case PGPPublicKey.RSA_SIGN: {
@@ -1148,97 +974,121 @@ public class Apg {
break;
}
}
- return algorithmStr + ", " + key.getBitStrength() + "bit";
- }
-
- public static String getAlgorithmInfo(PGPSecretKey key) {
- return getAlgorithmInfo(key.getPublicKey());
+ return algorithmStr + ", " + keySize + "bit";
}
- public static void deleteKey(Activity context, PGPPublicKeyRing keyRing) {
- PGPPublicKey masterKey = getMasterKey(keyRing);
- Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- context.getContentResolver().delete(uri, null, null);
- loadKeyRings(context, Id.type.public_key);
+ public static void deleteKey(int keyRingId) {
+ mDatabase.deleteKeyRing(keyRingId);
}
- public static void deleteKey(Activity context, PGPSecretKeyRing keyRing) {
- PGPSecretKey masterKey = getMasterKey(keyRing);
- Uri uri = Uri.withAppendedPath(SecretKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
- context.getContentResolver().delete(uri, null, null);
- loadKeyRings(context, Id.type.secret_key);
+ public static Object getKeyRing(int keyRingId) {
+ return mDatabase.getKeyRing(keyRingId);
}
- public static PGPPublicKey findPublicKey(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 key;
- }
- } catch (PGPException e) {
- // just not found, can ignore this
- }
+ public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
+ byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_secret, keyId);
+ if (data == null) {
+ return null;
+ }
+ try {
+ return new PGPSecretKeyRing(data);
+ } catch (IOException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
+ } catch (PGPException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
}
return null;
}
- public static PGPSecretKey findSecretKey(long keyId) {
- PGPSecretKey key = null;
- for (int i = 0; i < mSecretKeyRings.size(); ++i) {
- PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
- key = keyRing.getSecretKey(keyId);
- if (key != null) {
- return key;
- }
+ public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
+ byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_public, keyId);
+ if (data == null) {
+ return null;
+ }
+ try {
+ return new PGPPublicKeyRing(data);
+ } catch (IOException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
}
return null;
}
- public static PGPSecretKeyRing findSecretKeyRing(long keyId) {
- for (int i = 0; i < mSecretKeyRings.size(); ++i) {
- PGPSecretKeyRing keyRing = mSecretKeyRings.get(i);
- PGPSecretKey key = null;
- key = keyRing.getSecretKey(keyId);
- if (key != null) {
- return keyRing;
- }
+ public static PGPSecretKey getSecretKey(long keyId) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
}
- return null;
+ return keyRing.getSecretKey(keyId);
}
- public static PGPPublicKeyRing findPublicKeyRing(long keyId) {
- for (int i = 0; i < mPublicKeyRings.size(); ++i) {
- PGPPublicKeyRing keyRing = mPublicKeyRings.get(i);
- PGPPublicKey key = null;
- try {
- key = keyRing.getPublicKey(keyId);
- if (key != null) {
- return keyRing;
- }
- } catch (PGPException e) {
- // key not found
- }
+ public static PGPPublicKey getPublicKey(long keyId) {
+ PGPPublicKeyRing keyRing = getPublicKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
+ }
+ try {
+ return keyRing.getPublicKey(keyId);
+ } catch (PGPException e) {
+ return null;
}
- 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
- }
+ public static Vector<Integer> getKeyRingIds(int type) {
+ SQLiteDatabase db = mDatabase.db();
+ Vector<Integer> keyIds = new Vector<Integer>();
+ Cursor c = db.query(KeyRings.TABLE_NAME,
+ new String[] { KeyRings._ID },
+ KeyRings.TYPE + " = ?", new String[] { "" + type },
+ null, null, null);
+ if (c != null && c.moveToFirst()) {
+ do {
+ keyIds.add(c.getInt(0));
+ } while (c.moveToNext());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return keyIds;
+ }
+
+ public static String getMainUserId(long keyId, int type) {
+ SQLiteDatabase db = mDatabase.db();
+ Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ") " +
+ " INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ "masterKey." + Keys.KEY_RING_ID + " AND " +
+ "masterKey." + Keys.IS_MASTER_KEY + " = '1') " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON (" +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " = " +
+ "masterKey." + Keys._ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0')",
+ new String[] { UserIds.USER_ID },
+ Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+ String userId = "";
+ if (c != null && c.moveToFirst()) {
+ do {
+ userId = c.getString(0);
+ } while (c.moveToNext());
}
- return null;
+
+ if (c != null) {
+ c.close();
+ }
+
+ return userId;
}
public static void encrypt(Context context,
@@ -1277,7 +1127,7 @@ public class Apg {
}
if (signatureKeyId != 0) {
- signingKeyRing = findSecretKeyRing(signatureKeyId);
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed));
@@ -1329,7 +1179,7 @@ public class Apg {
if (compression == Id.choice.compression.none) {
bcpgOut = new BCPGOutputStream(encryptOut);
} else {
- compressGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB);
+ compressGen = new PGPCompressedDataGenerator(compression);
bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
}
if (signatureKeyId != 0) {
@@ -1393,7 +1243,7 @@ public class Apg {
throw new GeneralException(context.getString(R.string.error_noSignatureKey));
}
- signingKeyRing = findSecretKeyRing(signatureKeyId);
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) {
throw new GeneralException(context.getString(R.string.error_signatureFailed));
@@ -1479,7 +1329,7 @@ public class Apg {
if (obj instanceof PGPPublicKeyEncryptedData) {
gotAsymmetricEncryption = true;
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
- secretKey = findSecretKey(pbe.getKeyID());
+ secretKey = getSecretKey(pbe.getKeyID());
if (secretKey != null) {
break;
}
@@ -1532,6 +1382,9 @@ public class Apg {
String passPhrase, ProgressDialogUpdater progress,
boolean assumeSymmetric)
throws IOException, GeneralException, PGPException, SignatureException {
+ if (passPhrase == null) {
+ passPhrase = "";
+ }
Bundle returnData = new Bundle();
InputStream in = PGPUtil.getDecoderStream(inStream);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
@@ -1589,7 +1442,7 @@ public class Apg {
Object obj = it.next();
if (obj instanceof PGPPublicKeyEncryptedData) {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- secretKey = findSecretKey(encData.getKeyID());
+ secretKey = getSecretKey(encData.getKeyID());
if (secretKey != null) {
pbe = encData;
break;
@@ -1634,11 +1487,11 @@ public class Apg {
if (dataChunk instanceof PGPOnePassSignatureList) {
progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
- returnData.putBoolean("signature", true);
+ returnData.putBoolean(EXTRA_SIGNATURE, true);
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
- signatureKey = findPublicKey(signature.getKeyID());
+ signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -1648,21 +1501,21 @@ public class Apg {
signatureIndex = i;
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing));
}
- returnData.putString("signatureUserId", userId);
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
break;
}
}
- returnData.putLong("signatureKeyId", signatureKeyId);
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
if (signature != null) {
signature.initVerify(signatureKey, new BouncyCastleProvider());
} else {
- returnData.putBoolean("signatureUnknown", true);
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
}
dataChunk = plainFact.nextObject();
@@ -1694,7 +1547,7 @@ public class Apg {
try {
signature.update(buffer, 0, n);
} catch (SignatureException e) {
- returnData.putBoolean("signatureSuccess", false);
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
signature = null;
}
}
@@ -1714,9 +1567,9 @@ public class Apg {
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
if (signature.verify(messageSignature)) {
- returnData.putBoolean("signatureSuccess", true);
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
} else {
- returnData.putBoolean("signatureSuccess", false);
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
}
}
}
@@ -1769,7 +1622,7 @@ public class Apg {
byte[] clearText = out.toByteArray();
outStream.write(clearText);
- returnData.putBoolean("signature", true);
+ returnData.putBoolean(EXTRA_SIGNATURE, true);
progress.setProgress(R.string.progress_processingSignature, 60, 100);
PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
@@ -1783,7 +1636,7 @@ public class Apg {
PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
- signatureKey = findPublicKey(signature.getKeyID());
+ signatureKey = getPublicKey(signature.getKeyID());
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -1792,19 +1645,19 @@ public class Apg {
} else {
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
if (sigKeyRing != null) {
userId = getMainUserId(getMasterKey(sigKeyRing));
}
- returnData.putString("signatureUserId", userId);
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
break;
}
}
- returnData.putLong("signatureKeyId", signatureKeyId);
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
if (signature == null) {
- returnData.putBoolean("signatureUnknown", true);
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
progress.setProgress(R.string.progress_done, 100, 100);
return returnData;
}
@@ -1829,21 +1682,12 @@ public class Apg {
while (lookAhead != -1);
}
- returnData.putBoolean("signatureSuccess", signature.verify());
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, signature.verify());
progress.setProgress(R.string.progress_done, 100, 100);
return returnData;
}
- public static Vector<PGPPublicKeyRing> getPublicKeyRings() {
- return mPublicKeyRings;
- }
-
- public static Vector<PGPSecretKeyRing> getSecretKeyRings() {
- return mSecretKeyRings;
- }
-
-
// taken from ClearSignedFileProcessor in BC
private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
throws IOException {
diff --git a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
index 67aad7529..01cd2de25 100644
--- a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
+++ b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
@@ -25,6 +25,7 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
@@ -42,14 +43,24 @@ public class AskForSecretKeyPassPhrase {
alert.setTitle(R.string.title_authentification);
final PGPSecretKey secretKey;
+ final Activity activity = context;
if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
secretKey = null;
alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
} else {
- secretKey = Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId));
+ secretKey = Apg.getMasterKey(Apg.getSecretKeyRing(secretKeyId));
if (secretKey == null) {
- return null;
+ alert.setTitle(R.string.title_keyNotFound);
+ alert.setMessage(context.getString(R.string.keyNotFound, secretKeyId));
+ alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ }
+ });
+ alert.setCancelable(false);
+ return alert.create();
}
String userId = Apg.getMainUserIdSafe(context, secretKey);
alert.setMessage(context.getString(R.string.passPhraseFor, userId));
@@ -65,30 +76,29 @@ public class AskForSecretKeyPassPhrase {
alert.setView(view);
final PassPhraseCallbackInterface cb = callback;
- final Activity activity = context;
alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- activity.removeDialog(Id.dialog.pass_phrase);
- String passPhrase = "" + input.getText();
- long keyId;
- if (secretKey != null) {
- try {
- secretKey.extractPrivateKey(passPhrase.toCharArray(),
- new BouncyCastleProvider());
- } catch (PGPException e) {
- Toast.makeText(activity,
- R.string.wrongPassPhrase,
- Toast.LENGTH_SHORT).show();
- return;
- }
- keyId = secretKey.getKeyID();
- } else {
- keyId = Id.key.symmetric;
- }
- cb.passPhraseCallback(keyId, passPhrase);
- }
- });
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ String passPhrase = "" + input.getText();
+ long keyId;
+ if (secretKey != null) {
+ try {
+ secretKey.extractPrivateKey(passPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ } catch (PGPException e) {
+ Toast.makeText(activity,
+ R.string.wrongPassPhrase,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ keyId = secretKey.getKeyID();
+ } else {
+ keyId = Id.key.symmetric;
+ }
+ cb.passPhraseCallback(keyId, passPhrase);
+ }
+ });
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
diff --git a/src/org/thialfihar/android/apg/BaseActivity.java b/src/org/thialfihar/android/apg/BaseActivity.java
index 64705ba1f..210e09409 100644
--- a/src/org/thialfihar/android/apg/BaseActivity.java
+++ b/src/org/thialfihar/android/apg/BaseActivity.java
@@ -17,10 +17,7 @@
package org.thialfihar.android.apg;
import java.io.File;
-import java.util.Timer;
-import java.util.TimerTask;
-import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
import org.bouncycastle2.bcpg.HashAlgorithmTags;
import org.bouncycastle2.openpgp.PGPEncryptedData;
@@ -33,6 +30,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
@@ -53,8 +51,6 @@ public class BaseActivity extends Activity
private String mDeleteFile = null;
protected static SharedPreferences mPreferences = null;
- private static Timer mCacheTimer = new Timer();
-
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -66,33 +62,23 @@ public class BaseActivity extends Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Apg.initialize(this);
+
if (mPreferences == null) {
mPreferences = getPreferences(MODE_PRIVATE);
}
- Apg.initialize(this);
- if (mCacheTimer == null) {
- setPassPhraseCacheTimer();
- }
- }
- private void setPassPhraseCacheTimer() {
- if (mCacheTimer != null) {
- mCacheTimer.cancel();
- mCacheTimer = null;
- }
- int ttl = getPassPhraseCacheTtl();
- if (ttl == 0) {
- // no timer needed
- return;
- }
- // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
- // and makes sure the longest a pass phrase survives int the cache is 1.5 * ttl
- mCacheTimer = new Timer();
- mCacheTimer.scheduleAtFixedRate(new TimerTask() {
- public void run() {
- Apg.cleanUpCache(BaseActivity.this.getPassPhraseCacheTtl());
+ 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
}
- }, 0, ttl * 1000 / 2);
+ }
+
+ Intent intent = new Intent(this, Service.class);
+ intent.putExtra(Service.EXTRA_TTL, getPassPhraseCacheTtl());
+ startService(intent);
}
@Override
@@ -282,7 +268,7 @@ public class BaseActivity extends Activity
case Id.request.secret_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
- setSecretKeyId(bundle.getLong("selectedKeyId"));
+ setSecretKeyId(bundle.getLong(Apg.EXTRA_KEY_ID));
} else {
setSecretKeyId(Id.key.none);
}
@@ -304,9 +290,9 @@ public class BaseActivity extends Activity
public void setProgress(int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
- data.putInt("type", Id.message.progress_update);
- data.putInt("progress", progress);
- data.putInt("max", max);
+ data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
+ data.putInt(Apg.EXTRA_PROGRESS, progress);
+ data.putInt(Apg.EXTRA_MAX, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@@ -314,10 +300,10 @@ public class BaseActivity extends Activity
public void setProgress(String message, int progress, int max) {
Message msg = new Message();
Bundle data = new Bundle();
- data.putInt("type", Id.message.progress_update);
- data.putString("message", message);
- data.putInt("progress", progress);
- data.putInt("max", max);
+ data.putInt(Apg.EXTRA_STATUS, Id.message.progress_update);
+ data.putString(Apg.EXTRA_MESSAGE, message);
+ data.putInt(Apg.EXTRA_PROGRESS, progress);
+ data.putInt(Apg.EXTRA_MAX, max);
msg.setData(data);
mHandler.sendMessage(msg);
}
@@ -328,16 +314,16 @@ public class BaseActivity extends Activity
return;
}
- int type = data.getInt("type");
+ int type = data.getInt(Apg.EXTRA_STATUS);
switch (type) {
case Id.message.progress_update: {
- String message = data.getString("message");
+ String message = data.getString(Apg.EXTRA_MESSAGE);
if (mProgressDialog != null) {
if (message != null) {
mProgressDialog.setMessage(message);
}
- mProgressDialog.setMax(data.getInt("max"));
- mProgressDialog.setProgress(data.getInt("progress"));
+ mProgressDialog.setMax(data.getInt(Apg.EXTRA_MAX));
+ mProgressDialog.setProgress(data.getInt(Apg.EXTRA_PROGRESS));
}
break;
}
@@ -382,7 +368,14 @@ public class BaseActivity extends Activity
}
public int getPassPhraseCacheTtl() {
- return mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 300);
+ int ttl = mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 180);
+ // fix the value if it was set to "never" in previous versions, which currently is not
+ // supported
+ if (ttl == 0) {
+ ttl = 180;
+ setPassPhraseCacheTtl(ttl);
+ }
+ return ttl;
}
public void setPassPhraseCacheTtl(int value) {
@@ -390,7 +383,9 @@ public class BaseActivity extends Activity
editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
editor.commit();
- setPassPhraseCacheTimer();
+ Intent intent = new Intent(this, Service.class);
+ intent.putExtra(Service.EXTRA_TTL, value);
+ startService(intent);
}
public int getDefaultEncryptionAlgorithm() {
@@ -417,7 +412,7 @@ public class BaseActivity extends Activity
public int getDefaultMessageCompression() {
return mPreferences.getInt(Constants.pref.default_message_compression,
- CompressionAlgorithmTags.ZLIB);
+ Id.choice.compression.zlib);
}
public void setDefaultMessageCompression(int value) {
@@ -428,7 +423,7 @@ public class BaseActivity extends Activity
public int getDefaultFileCompression() {
return mPreferences.getInt(Constants.pref.default_file_compression,
- CompressionAlgorithmTags.ZLIB);
+ Id.choice.compression.none);
}
public void setDefaultFileCompression(int value) {
diff --git a/src/org/thialfihar/android/apg/CachedPassPhrase.java b/src/org/thialfihar/android/apg/CachedPassPhrase.java
index e7566220e..74248aee3 100644
--- a/src/org/thialfihar/android/apg/CachedPassPhrase.java
+++ b/src/org/thialfihar/android/apg/CachedPassPhrase.java
@@ -10,6 +10,12 @@ public class CachedPassPhrase {
this.passPhrase = passPhrase;
}
+ public int hashCode() {
+ int hc1 = (int)(this.timestamp & 0xffffffff);
+ int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode());
+ return (hc1 + hc2) * hc2 + hc1;
+ }
+
public boolean equals(Object other) {
if (!(other instanceof CachedPassPhrase)) {
return false;
diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java
index 80ad13d5e..de8dcb3ff 100644
--- a/src/org/thialfihar/android/apg/DecryptActivity.java
+++ b/src/org/thialfihar/android/apg/DecryptActivity.java
@@ -31,8 +31,6 @@ import java.util.regex.Matcher;
import org.bouncycastle2.jce.provider.BouncyCastleProvider;
import org.bouncycastle2.openpgp.PGPException;
-import org.bouncycastle2.util.Strings;
-import org.openintents.intents.FileManager;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
@@ -57,6 +55,9 @@ import android.widget.ViewFlipper;
public class DecryptActivity extends BaseActivity {
private long mSignatureKeyId = 0;
+ private Intent mIntent;
+
+ private boolean mReturnResult = false;
private String mReplyTo = null;
private String mSubject = null;
private boolean mSignedOnly = false;
@@ -158,9 +159,9 @@ public class DecryptActivity extends BaseActivity {
mSource.showNext();
}
- Intent intent = getIntent();
- if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) {
- Uri uri = intent.getData();
+ mIntent = getIntent();
+ if (Intent.ACTION_VIEW.equals(mIntent.getAction())) {
+ Uri uri = mIntent.getData();
try {
InputStream attachment = getContentResolver().openInputStream(uri);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
@@ -170,15 +171,15 @@ public class DecryptActivity extends BaseActivity {
byteOut.write(bytes, 0, length);
}
byteOut.close();
- String data = Strings.fromUTF8ByteArray(byteOut.toByteArray());
+ String data = new String(byteOut.toByteArray());
mMessage.setText(data);
} catch (FileNotFoundException e) {
// ignore, then
} catch (IOException e) {
// ignore, then
}
- } else if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_SEND)) {
- Bundle extras = intent.getExtras();
+ } else if (Intent.ACTION_SEND.equals(mIntent.getAction())) {
+ Bundle extras = mIntent.getExtras();
if (extras == null) {
extras = new Bundle();
}
@@ -187,15 +188,15 @@ public class DecryptActivity extends BaseActivity {
mMessage.setText(data);
}
mSubject = extras.getString(Intent.EXTRA_SUBJECT);
- if (mSubject.startsWith("Fwd: ")) {
+ if (mSubject != null && mSubject.startsWith("Fwd: ")) {
mSubject = mSubject.substring(5);
}
- } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT)) {
- Bundle extras = intent.getExtras();
+ } else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) {
+ Bundle extras = mIntent.getExtras();
if (extras == null) {
extras = new Bundle();
}
- String data = extras.getString("data");
+ String data = extras.getString(Apg.EXTRA_DATA);
if (data != null) {
Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
if (matcher.matches()) {
@@ -214,14 +215,39 @@ public class DecryptActivity extends BaseActivity {
}
}
}
- mReplyTo = extras.getString("replyTo");
- mSubject = extras.getString("subject");
- } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT_FILE)) {
+ mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO);
+ mSubject = extras.getString(Apg.EXTRA_SUBJECT);
+ } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) {
mSource.setInAnimation(null);
mSource.setOutAnimation(null);
while (mSource.getCurrentView().getId() != R.id.sourceFile) {
mSource.showNext();
}
+ } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) {
+ Bundle extras = mIntent.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ String data = extras.getString(Apg.EXTRA_DATA);
+ if (data != null) {
+ Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ // replace non breakable spaces
+ data = data.replaceAll("\\xa0", " ");
+ mMessage.setText(data);
+ } else {
+ matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data);
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ // replace non breakable spaces
+ data = data.replaceAll("\\xa0", " ");
+ mMessage.setText(data);
+ mDecryptButton.setText(R.string.btn_verify);
+ }
+ }
+ }
+ mReturnResult = true;
}
if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
@@ -256,29 +282,41 @@ public class DecryptActivity extends BaseActivity {
});
mReplyButton.setVisibility(View.INVISIBLE);
- if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
- mMessage.getText().length() > 0) {
- mDecryptButton.performClick();
+ if (mReturnResult) {
+ mSourcePrevious.setClickable(false);
+ mSourcePrevious.setEnabled(false);
+ mSourcePrevious.setVisibility(View.INVISIBLE);
+
+ mSourceNext.setClickable(false);
+ mSourceNext.setEnabled(false);
+ mSourceNext.setVisibility(View.INVISIBLE);
+
+ mSourceLabel.setClickable(false);
+ mSourceLabel.setEnabled(false);
}
updateSource();
+
+ if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
+ mMessage.getText().length() > 0) {
+ mDecryptButton.performClick();
+ }
}
private void openFile() {
String filename = mFilename.getText().toString();
- Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setData(Uri.parse("file://" + filename));
-
- intent.putExtra(FileManager.EXTRA_TITLE, getString(R.string.filemanager_titleDecrypt));
- intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen);
+ intent.setType("*/*");
try {
startActivityForResult(intent, Id.request.filename);
} catch (ActivityNotFoundException e) {
// No compatible file manager was found.
- Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
}
}
@@ -373,9 +411,9 @@ public class DecryptActivity extends BaseActivity {
// look at the file/message again to check whether there's
// symmetric encryption data in there
if (mDecryptTarget == Id.target.file) {
- ((FileInputStream) in).reset();
+ in = new FileInputStream(mInputFilename);
} else {
- ((ByteArrayInputStream) in).reset();
+ in = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
}
if (!Apg.hasSymmetricEncryption(this, in)) {
throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound));
@@ -396,9 +434,9 @@ public class DecryptActivity extends BaseActivity {
} catch (FileNotFoundException e) {
error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
- error = e.getLocalizedMessage();
+ error = "" + e;
} catch (Apg.GeneralException e) {
- error = e.getLocalizedMessage();
+ error = "" + e;
}
if (error != null) {
Toast.makeText(this, getString(R.string.errorMessage, error),
@@ -412,12 +450,11 @@ public class DecryptActivity extends BaseActivity {
String data = mMessage.getText().toString();
data = data.replaceAll("(?m)^", "> ");
data = "\n\n" + data;
- intent.putExtra("data", data);
- intent.putExtra("subject", "Re: " + mSubject);
- intent.putExtra("sendTo", mReplyTo);
- intent.putExtra("eyId", mSignatureKeyId);
- intent.putExtra("signatureKeyId", getSecretKeyId());
- intent.putExtra("encryptionKeyIds", new long[] { mSignatureKeyId });
+ intent.putExtra(Apg.EXTRA_DATA, data);
+ intent.putExtra(Apg.EXTRA_SUBJECT, "Re: " + mSubject);
+ intent.putExtra(Apg.EXTRA_SEND_TO, mReplyTo);
+ intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, getSecretKeyId());
+ intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId });
startActivity(intent);
}
@@ -474,25 +511,23 @@ public class DecryptActivity extends BaseActivity {
out.close();
if (mDecryptTarget == Id.target.message) {
- data.putString("decryptedMessage",
- Strings.fromUTF8ByteArray(((ByteArrayOutputStream)
- out).toByteArray()));
+ data.putByteArray(Apg.EXTRA_DECRYPTED_MESSAGE,
+ ((ByteArrayOutputStream) out).toByteArray());
}
} catch (PGPException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (IOException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (SignatureException e) {
- error = e.getMessage();
- e.printStackTrace();
+ error = "" + e;
} catch (Apg.GeneralException e) {
- error = e.getMessage();
+ error = "" + e;
}
- data.putInt("type", Id.message.done);
+ data.putInt(Apg.EXTRA_STATUS, Id.message.done);
if (error != null) {
- data.putString("error", error);
+ data.putString(Apg.EXTRA_ERROR, error);
}
msg.setData(data);
@@ -509,20 +544,20 @@ public class DecryptActivity extends BaseActivity {
mSignatureLayout.setVisibility(View.GONE);
mReplyButton.setVisibility(View.INVISIBLE);
- String error = data.getString("error");
+ String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(DecryptActivity.this,
- getString(R.string.errorMessage,
- data.getString("error")),
- Toast.LENGTH_SHORT).show();
+ getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show();
switch (mDecryptTarget) {
case Id.target.message: {
- String decryptedMessage = data.getString("decryptedMessage");
+ String decryptedMessage =
+ new String(data.getByteArray(Apg.EXTRA_DECRYPTED_MESSAGE));
mMessage.setText(decryptedMessage);
+ mMessage.setHorizontallyScrolling(false);
mReplyButton.setVisibility(View.VISIBLE);
break;
}
@@ -541,9 +576,9 @@ public class DecryptActivity extends BaseActivity {
}
}
- if (data.getBoolean("signature")) {
- String userId = data.getString("signatureUserId");
- mSignatureKeyId = data.getLong("signatureKeyId");
+ if (data.getBoolean(Apg.EXTRA_SIGNATURE)) {
+ String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
+ mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL));
if (userId == null) {
userId = getResources().getString(R.string.unknownUserId);
@@ -555,15 +590,22 @@ public class DecryptActivity extends BaseActivity {
}
mUserId.setText(userId);
- if (data.getBoolean("signatureSuccess")) {
+ if (data.getBoolean(Apg.EXTRA_SIGNATURE_SUCCESS)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
- } else if (data.getBoolean("signatureUnknown")) {
+ } else if (data.getBoolean(Apg.EXTRA_SIGNATURE_UNKNOWN)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
} else {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
}
mSignatureLayout.setVisibility(View.VISIBLE);
}
+
+ if (mReturnResult) {
+ Intent intent = new Intent();
+ intent.putExtras(data);
+ setResult(RESULT_OK, intent);
+ finish();
+ }
}
@Override
diff --git a/src/org/thialfihar/android/apg/EditKeyActivity.java b/src/org/thialfihar/android/apg/EditKeyActivity.java
index b1e37e8a1..e71fd8e8c 100644
--- a/src/org/thialfihar/android/apg/EditKeyActivity.java
+++ b/src/org/thialfihar/android/apg/EditKeyActivity.java
@@ -16,6 +16,7 @@
package org.thialfihar.android.apg;
+import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
@@ -24,6 +25,7 @@ import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
+import org.thialfihar.android.apg.provider.Database;
import org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.utils.IterableIterator;
@@ -69,7 +71,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
Intent intent = getIntent();
long keyId = 0;
if (intent.getExtras() != null) {
- keyId = intent.getExtras().getLong("keyId");
+ keyId = intent.getExtras().getLong(Apg.EXTRA_KEY_ID);
}
if (keyId != 0) {
@@ -115,7 +117,7 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
Toast.makeText(this, "Warning: Key editing is still kind of beta.", Toast.LENGTH_LONG).show();
}
- public long getMasterKeyId() {
+ private long getMasterKeyId() {
if (mKeys.getEditors().getChildCount() == 0) {
return 0;
}
@@ -243,22 +245,27 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
newPassPhrase = oldPassPhrase;
}
Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this);
+ Apg.setCachedPassPhrase(getMasterKeyId(), newPassPhrase);
} catch (NoSuchProviderException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (NoSuchAlgorithmException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (PGPException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (SignatureException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (Apg.GeneralException e) {
- error = e.getMessage();
+ error = "" + e;
+ } catch (Database.GeneralException e) {
+ error = "" + e;
+ } catch (IOException e) {
+ error = "" + e;
}
- data.putInt("type", Id.message.done);
+ data.putInt(Apg.EXTRA_STATUS, Id.message.done);
if (error != null) {
- data.putString("error", error);
+ data.putString(Apg.EXTRA_ERROR, error);
}
msg.setData(data);
@@ -272,11 +279,10 @@ public class EditKeyActivity extends BaseActivity implements OnClickListener {
Bundle data = msg.getData();
removeDialog(Id.dialog.saving);
- String error = data.getString("error");
+ String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(EditKeyActivity.this,
- getString(R.string.errorMessage, data.getString("error")),
- Toast.LENGTH_SHORT).show();
+ getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
diff --git a/src/org/thialfihar/android/apg/EncryptActivity.java b/src/org/thialfihar/android/apg/EncryptActivity.java
index 073e9dc9c..926d79599 100644
--- a/src/org/thialfihar/android/apg/EncryptActivity.java
+++ b/src/org/thialfihar/android/apg/EncryptActivity.java
@@ -35,7 +35,6 @@ import org.bouncycastle2.openpgp.PGPPublicKeyRing;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.bouncycastle2.util.Strings;
-import org.openintents.intents.FileManager;
import org.thialfihar.android.apg.Apg.GeneralException;
import org.thialfihar.android.apg.utils.Choice;
@@ -62,11 +61,13 @@ import android.widget.Toast;
import android.widget.ViewFlipper;
public class EncryptActivity extends BaseActivity {
+ private Intent mIntent = null;
private String mSubject = null;
private String mSendTo = null;
private long mEncryptionKeyIds[] = null;
+ private boolean mReturnResult = false;
private EditText mMessage = null;
private Button mSelectKeysButton = null;
private Button mEncryptButton = null;
@@ -265,21 +266,26 @@ public class EncryptActivity extends BaseActivity {
}
});
- Intent intent = getIntent();
- if (intent.getAction() != null &&
- (intent.getAction().equals(Apg.Intent.ENCRYPT) ||
- intent.getAction().equals(Apg.Intent.ENCRYPT_FILE))) {
- Bundle extras = intent.getExtras();
+ mIntent = getIntent();
+ if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) ||
+ Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction()) ||
+ Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
+ Bundle extras = mIntent.getExtras();
if (extras == null) {
extras = new Bundle();
}
- String data = extras.getString("data");
- mSendTo = extras.getString("sendTo");
- mSubject = extras.getString("subject");
- long signatureKeyId = extras.getLong("signatureKeyId");
- long encryptionKeyIds[] = extras.getLongArray("encryptionKeyIds");
+
+ if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
+ mReturnResult = true;
+ }
+
+ String data = extras.getString(Apg.EXTRA_DATA);
+ mSendTo = extras.getString(Apg.EXTRA_SEND_TO);
+ mSubject = extras.getString(Apg.EXTRA_SUBJECT);
+ long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
+ long encryptionKeyIds[] = extras.getLongArray(Apg.EXTRA_ENCRYPTION_KEY_IDS);
if (signatureKeyId != 0) {
- PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId);
+ PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId);
PGPSecretKey masterKey = null;
if (keyRing != null) {
masterKey = Apg.getMasterKey(keyRing);
@@ -295,7 +301,7 @@ public class EncryptActivity extends BaseActivity {
if (encryptionKeyIds != null) {
Vector<Long> goodIds = new Vector<Long>();
for (int i = 0; i < encryptionKeyIds.length; ++i) {
- PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]);
+ PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]);
PGPPublicKey masterKey = null;
if (keyRing == null) {
continue;
@@ -318,7 +324,8 @@ public class EncryptActivity extends BaseActivity {
}
}
- if (intent.getAction().equals(Apg.Intent.ENCRYPT)) {
+ if (Apg.Intent.ENCRYPT.equals(mIntent.getAction()) ||
+ Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())) {
if (data != null) {
mMessage.setText(data);
}
@@ -327,7 +334,7 @@ public class EncryptActivity extends BaseActivity {
while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
mSource.showNext();
}
- } else if (intent.getAction().equals(Apg.Intent.ENCRYPT_FILE)) {
+ } else if (Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())) {
mSource.setInAnimation(null);
mSource.setOutAnimation(null);
while (mSource.getCurrentView().getId() != R.id.sourceFile) {
@@ -339,23 +346,47 @@ public class EncryptActivity extends BaseActivity {
updateView();
updateSource();
updateMode();
+
+ if (mReturnResult) {
+ mSourcePrevious.setClickable(false);
+ mSourcePrevious.setEnabled(false);
+ mSourcePrevious.setVisibility(View.INVISIBLE);
+
+ mSourceNext.setClickable(false);
+ mSourceNext.setEnabled(false);
+ mSourceNext.setVisibility(View.INVISIBLE);
+
+ mSourceLabel.setClickable(false);
+ mSourceLabel.setEnabled(false);
+
+ mEncryptToClipboardButton.setEnabled(false);
+ mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
+ mEncryptButton.setText(R.string.btn_encrypt);
+ }
+
+ if (mReturnResult &&
+ mMessage.getText().length() > 0 &&
+ ((mEncryptionKeyIds != null &&
+ mEncryptionKeyIds.length > 0) ||
+ getSecretKeyId() != 0)) {
+ encryptClicked();
+ }
}
private void openFile() {
String filename = mFilename.getText().toString();
- Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setData(Uri.parse("file://" + filename));
-
- intent.putExtra(FileManager.EXTRA_TITLE, R.string.filemanager_titleEncrypt);
- intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, R.string.filemanager_btnOpen);
+ intent.setType("*/*");
try {
startActivityForResult(intent, Id.request.filename);
} catch (ActivityNotFoundException e) {
// No compatible file manager was found.
- Toast.makeText(this, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
}
}
@@ -457,7 +488,7 @@ public class EncryptActivity extends BaseActivity {
return;
}
} else {
- boolean encryptIt = mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0;
+ boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0);
// for now require at least one form of encryption for files
if (!encryptIt && mEncryptTarget == Id.target.file) {
Toast.makeText(this, R.string.selectEncryptionKey, Toast.LENGTH_SHORT).show();
@@ -527,7 +558,7 @@ public class EncryptActivity extends BaseActivity {
} else {
encryptionKeyIds = mEncryptionKeyIds;
signatureKeyId = getSecretKeyId();
- signOnly = mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0;
+ signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);
}
if (mEncryptTarget == Id.target.file) {
@@ -548,7 +579,7 @@ public class EncryptActivity extends BaseActivity {
} else {
String message = mMessage.getText().toString();
- if (signOnly) {
+ if (signOnly && mReturnResult) {
// fix the message a bit, trailing spaces and newlines break stuff,
// because GMail sends as HTML and such things fuck up the signature,
// TODO: things like "<" and ">" also fuck up the signature
@@ -582,26 +613,27 @@ public class EncryptActivity extends BaseActivity {
out.close();
if (mEncryptTarget != Id.target.file) {
- data.putString("message", new String(((ByteArrayOutputStream)out).toByteArray()));
+ data.putByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE,
+ ((ByteArrayOutputStream)out).toByteArray());
}
} catch (IOException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (PGPException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (NoSuchProviderException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (NoSuchAlgorithmException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (SignatureException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (Apg.GeneralException e) {
- error = e.getMessage();
+ error = "" + e;
}
- data.putInt("type", Id.message.done);
+ data.putInt(Apg.EXTRA_STATUS, Id.message.done);
if (error != null) {
- data.putString("error", error);
+ data.putString(Apg.EXTRA_ERROR, error);
}
msg.setData(data);
@@ -645,7 +677,7 @@ public class EncryptActivity extends BaseActivity {
private void selectPublicKeys() {
Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
- intent.putExtra("selection", mEncryptionKeyIds);
+ intent.putExtra(Apg.EXTRA_SELECTION, mEncryptionKeyIds);
startActivityForResult(intent, Id.request.public_keys);
}
@@ -702,7 +734,7 @@ public class EncryptActivity extends BaseActivity {
case Id.request.public_keys: {
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
- mEncryptionKeyIds = bundle.getLongArray("selection");
+ mEncryptionKeyIds = bundle.getLongArray(Apg.EXTRA_SELECTION);
}
updateView();
break;
@@ -723,53 +755,61 @@ public class EncryptActivity extends BaseActivity {
removeDialog(Id.dialog.encrypting);
Bundle data = msg.getData();
- String error = data.getString("error");
+ String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(EncryptActivity.this,
- getString(R.string.errorMessage, data.getString("error")),
- Toast.LENGTH_SHORT).show();
+ getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
return;
- } else {
- String message = data.getString("message");
- switch (mEncryptTarget) {
- case Id.target.clipboard: {
- ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- clip.setText(message);
- Toast.makeText(this, R.string.encryptionToClipboardSuccessful,
- Toast.LENGTH_SHORT).show();
- break;
- }
+ }
+ switch (mEncryptTarget) {
+ case Id.target.clipboard: {
+ String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE));
+ ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ clip.setText(message);
+ Toast.makeText(this, R.string.encryptionToClipboardSuccessful,
+ Toast.LENGTH_SHORT).show();
+ break;
+ }
- case Id.target.email: {
- Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
- emailIntent.setType("text/plain; charset=utf-8");
- emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
- if (mSubject != null) {
- emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
- mSubject);
- }
- if (mSendTo != null) {
- emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
- new String[] { mSendTo });
- }
- EncryptActivity.this.
- startActivity(Intent.createChooser(emailIntent,
- getString(R.string.title_sendEmail)));
+ case Id.target.email: {
+ if (mReturnResult) {
+ Intent intent = new Intent();
+ intent.putExtras(data);
+ setResult(RESULT_OK, intent);
+ finish();
+ return;
}
- case Id.target.file: {
- Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show();
- if (mDeleteAfter.isChecked()) {
- setDeleteFile(mInputFilename);
- showDialog(Id.dialog.delete_file);
- }
- break;
+ String message = new String(data.getByteArray(Apg.EXTRA_ENCRYPTED_MESSAGE));
+ Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
+ emailIntent.setType("text/plain; charset=utf-8");
+ emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
+ if (mSubject != null) {
+ emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
+ mSubject);
+ }
+ if (mSendTo != null) {
+ emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
+ new String[] { mSendTo });
}
+ EncryptActivity.this.
+ startActivity(Intent.createChooser(emailIntent,
+ getString(R.string.title_sendEmail)));
+ break;
+ }
- default: {
- // shouldn't happen
- break;
+ case Id.target.file: {
+ Toast.makeText(this, R.string.encryptionSuccessful, Toast.LENGTH_SHORT).show();
+ if (mDeleteAfter.isChecked()) {
+ setDeleteFile(mInputFilename);
+ showDialog(Id.dialog.delete_file);
}
+ break;
+ }
+
+ default: {
+ // shouldn't happen
+ break;
}
}
}
diff --git a/src/org/thialfihar/android/apg/FileDialog.java b/src/org/thialfihar/android/apg/FileDialog.java
index 22d64fc84..b6bbbf3f1 100644
--- a/src/org/thialfihar/android/apg/FileDialog.java
+++ b/src/org/thialfihar/android/apg/FileDialog.java
@@ -16,8 +16,6 @@
package org.thialfihar.android.apg;
-import org.openintents.intents.FileManager;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
@@ -48,6 +46,8 @@ public class FileDialog {
String defaultFile, OnClickListener onClickListener,
String fileManagerTitle, String fileManagerButton,
int requestCode) {
+ // TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now,
+ // but maybe the Intent now used will someday support them again, so leaving them in
LayoutInflater inflater =
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
@@ -102,18 +102,17 @@ public class FileDialog {
private static void openFile() {
String filename = mFilename.getText().toString();
- Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setData(Uri.parse("file://" + filename));
-
- intent.putExtra(FileManager.EXTRA_TITLE, mFileManagerTitle);
- intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
+ intent.setType("*/*");
try {
mActivity.startActivityForResult(intent, mRequestCode);
} catch (ActivityNotFoundException e) {
// No compatible file manager was found.
- Toast.makeText(mActivity, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show();
+ Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
}
}
}
diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java
index 47cd0a890..4567f937d 100644
--- a/src/org/thialfihar/android/apg/Id.java
+++ b/src/org/thialfihar/android/apg/Id.java
@@ -80,6 +80,11 @@ public final class Id {
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 int public_key = 0x21070001;
public static final int secret_key = 0x21070002;
diff --git a/src/org/thialfihar/android/apg/KeyListActivity.java b/src/org/thialfihar/android/apg/KeyListActivity.java
new file mode 100644
index 000000000..7e86504b3
--- /dev/null
+++ b/src/org/thialfihar/android/apg/KeyListActivity.java
@@ -0,0 +1,642 @@
+/*
+ * 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.bouncycastle2.openpgp.PGPPublicKeyRing;
+import org.bouncycastle2.openpgp.PGPSecretKeyRing;
+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 KeyListAdapter mListAdapter;
+
+ 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);
+ mListAdapter = new KeyListAdapter(this);
+ mList.setAdapter(mListAdapter);
+ 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 int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
+ mSelectedItem = -1;
+ // TODO: better way to do this?
+ String userId = "<unknown>";
+ Object keyRing = Apg.getKeyRing(keyRingId);
+ if (keyRing != null) {
+ if (keyRing instanceof PGPPublicKeyRing) {
+ userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPPublicKeyRing) keyRing));
+ } else {
+ userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey((PGPSecretKeyRing) keyRing));
+ }
+ }
+
+ 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(keyRingId);
+ 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(mKeyType == Id.type.public_key ?
+ R.string.specifyFileToExportTo :
+ 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);
+ }
+
+ 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<Integer> keyRingIds = new Vector<Integer>();
+ if (mSelectedItem == -1) {
+ keyRingIds = Apg.getKeyRingIds(mKeyType == Id.type.public_key ?
+ Id.database.type_public :
+ Id.database.type_secret);
+ } else {
+ int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
+ keyRingIds.add(keyRingId);
+ mSelectedItem = -1;
+ }
+ data = Apg.exportKeyRings(this, keyRingIds, 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(Apg.EXTRA_STATUS, Id.message.import_done);
+ } else {
+ data.putInt(Apg.EXTRA_STATUS, Id.message.export_done);
+ }
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ protected void deleteKey(int keyRingId) {
+ Apg.deleteKey(keyRingId);
+ refreshList();
+ }
+
+ protected void refreshList() {
+ mListAdapter.rebuild(true);
+ mListAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ Bundle data = msg.getData();
+ if (data != null) {
+ int type = data.getInt(Apg.EXTRA_STATUS);
+ switch (type) {
+ case Id.message.import_done: {
+ removeDialog(Id.dialog.importing);
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(KeyListActivity.this,
+ getString(R.string.errorMessage, 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(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(KeyListActivity.this,
+ getString(R.string.errorMessage, 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, exported);
+ } 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 = Apg.getDatabase().db();
+ 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, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
+
+ startManagingCursor(mCursor);
+ rebuild(false);
+ }
+
+ public void rebuild(boolean requery) {
+ if (requery) {
+ mCursor.requery();
+ }
+ mChildren = new Vector<Vector<KeyChild>>();
+ for (int i = 0; i < mCursor.getCount(); ++i) {
+ mChildren.add(null);
+ }
+ }
+
+ 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 + " = ? AND " + UserIds.RANK + " > 0",
+ 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 int getKeyRingId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(0); // _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);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/MailListActivity.java b/src/org/thialfihar/android/apg/MailListActivity.java
index f0abe8f45..d166feede 100644
--- a/src/org/thialfihar/android/apg/MailListActivity.java
+++ b/src/org/thialfihar/android/apg/MailListActivity.java
@@ -87,7 +87,7 @@ public class MailListActivity extends ListActivity {
mconversations = new Vector<Conversation>();
mmessages = new Vector<Message>();
- String account = getIntent().getExtras().getString("account");
+ String account = getIntent().getExtras().getString(Apg.EXTRA_ACCOUNT);
// TODO: what if account is null?
Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
Cursor cursor =
@@ -153,9 +153,9 @@ public class MailListActivity extends ListActivity {
Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
intent.setAction(Apg.Intent.DECRYPT);
Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
- intent.putExtra("data", message.data);
- intent.putExtra("subject", message.subject);
- intent.putExtra("replyTo", message.replyTo);
+ intent.putExtra(Apg.EXTRA_DATA, message.data);
+ intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
+ intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
startActivity(intent);
}
});
diff --git a/src/org/thialfihar/android/apg/MainActivity.java b/src/org/thialfihar/android/apg/MainActivity.java
index a4d584304..65dd20d95 100644
--- a/src/org/thialfihar/android/apg/MainActivity.java
+++ b/src/org/thialfihar/android/apg/MainActivity.java
@@ -47,6 +47,8 @@ import android.widget.AdapterView.OnItemClickListener;
public class MainActivity extends BaseActivity {
private ListView mAccounts = null;
+ private AccountListAdapter mListAdapter = null;
+ private Cursor mAccountCursor;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -95,22 +97,22 @@ public class MainActivity extends BaseActivity {
}
});
- Cursor accountCursor = managedQuery(Accounts.CONTENT_URI, null, null, null, null);
+ mAccountCursor =
+ Apg.getDatabase().db().query(Accounts.TABLE_NAME,
+ new String[] {
+ Accounts._ID,
+ Accounts.NAME,
+ }, null, null, null, null, Accounts.NAME + " ASC");
+ startManagingCursor(mAccountCursor);
- mAccounts.setAdapter(new AccountListAdapter(this, accountCursor));
+ mListAdapter = new AccountListAdapter(this, mAccountCursor);
+ mAccounts.setAdapter(mListAdapter);
mAccounts.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View view, int index, long id) {
- Cursor cursor =
- managedQuery(Uri.withAppendedPath(Accounts.CONTENT_URI, "" + id), null,
- null, null, null);
- if (cursor != null && cursor.getCount() > 0) {
- cursor.moveToFirst();
- int nameIndex = cursor.getColumnIndex(Accounts.NAME);
- String accountName = cursor.getString(nameIndex);
- startActivity(new Intent(MainActivity.this, MailListActivity.class)
- .putExtra("account", accountName));
- }
+ String accountName = (String) mAccounts.getItemAtPosition(index);
+ startActivity(new Intent(MainActivity.this, MailListActivity.class)
+ .putExtra(Apg.EXTRA_ACCOUNT, accountName));
}
});
registerForContextMenu(mAccounts);
@@ -154,9 +156,10 @@ public class MainActivity extends BaseActivity {
ContentValues values = new ContentValues();
values.put(Accounts.NAME, accountName);
try {
- MainActivity.this.getContentResolver()
- .insert(Accounts.CONTENT_URI,
- values);
+ Apg.getDatabase().db().insert(Accounts.TABLE_NAME,
+ Accounts.NAME, values);
+ mAccountCursor.requery();
+ mListAdapter.notifyDataSetChanged();
} catch (SQLException e) {
Toast.makeText(MainActivity.this,
getString(R.string.errorMessage,
@@ -188,6 +191,12 @@ public class MainActivity extends BaseActivity {
message.setText("Read the warnings!\n\n" +
"Changes:\n" +
+ "* k9mail integration, k9mail beta build is available on the k9mail website\n" +
+ "* support of other file managers (e.g. ASTRO)\n" +
+ "* Slovenian translation (thanks, 359)\n" +
+ "* new database, much faster, less memory usage\n" +
+ "* defined Intents and content provider for other apps\n" +
+ "* bugfixes\n" +
"\n" +
"WARNING: be careful editing your existing keys, as they " +
"WILL be stripped of certificates right now.\n" +
@@ -277,8 +286,11 @@ public class MainActivity extends BaseActivity {
switch (menuItem.getItemId()) {
case Id.menu.delete: {
- Uri uri = Uri.withAppendedPath(Accounts.CONTENT_URI, "" + info.id);
- this.getContentResolver().delete(uri, null, null);
+ Apg.getDatabase().db().delete(Accounts.TABLE_NAME,
+ Accounts._ID + " = ?",
+ new String[] { "" + info.id });
+ mAccountCursor.requery();
+ mListAdapter.notifyDataSetChanged();
return true;
}
@@ -298,6 +310,13 @@ public class MainActivity extends BaseActivity {
}
@Override
+ public Object getItem(int position) {
+ Cursor c = getCursor();
+ c.moveToPosition(position);
+ return c.getString(c.getColumnIndex(Accounts.NAME));
+ }
+
+ @Override
public int getCount() {
return super.getCount();
}
diff --git a/src/org/thialfihar/android/apg/PreferencesActivity.java b/src/org/thialfihar/android/apg/PreferencesActivity.java
index fae63d63b..e80e1ad5f 100644
--- a/src/org/thialfihar/android/apg/PreferencesActivity.java
+++ b/src/org/thialfihar/android/apg/PreferencesActivity.java
@@ -50,7 +50,6 @@ public class PreferencesActivity extends BaseActivity {
new Choice(180, getString(R.string.choice_3mins)),
new Choice(300, getString(R.string.choice_5mins)),
new Choice(600, getString(R.string.choice_10mins)),
- new Choice(0, getString(R.string.choice_untilQuit)),
};
ArrayAdapter<Choice> adapter =
new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
diff --git a/src/org/thialfihar/android/apg/PublicKeyListActivity.java b/src/org/thialfihar/android/apg/PublicKeyListActivity.java
index 67bc608ad..4997f60b7 100644
--- a/src/org/thialfihar/android/apg/PublicKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/PublicKeyListActivity.java
@@ -16,54 +16,19 @@
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.Message;
import android.view.ContextMenu;
-import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
-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 PublicKeyListActivity extends BaseActivity {
- ExpandableListView mList;
-
- protected int mSelectedItem = -1;
- protected int mTask = 0;
-
- private String mImportFilename = Constants.path.app_dir + "/pubring.gpg";
- private String mExportFilename = Constants.path.app_dir + "/pubexport.asc";
+public class PublicKeyListActivity extends KeyListActivity {
@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);
- setContentView(R.layout.key_list);
-
- mList = (ExpandableListView) findViewById(R.id.list);
- mList.setAdapter(new PublicKeyListAdapter(this));
- registerForContextMenu(mList);
}
@Override
@@ -80,506 +45,16 @@ public class PublicKeyListActivity extends BaseActivity {
}
@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 void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
- String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
- menu.setHeaderTitle(userId);
+ // TODO: user id? menu.setHeaderTitle("Key");
menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
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.getMessage();
- } catch (PGPException e) {
- error = e.getMessage();
- } catch (Apg.GeneralException e) {
- error = e.getMessage();
- }
-
- 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);
- }
}
diff --git a/src/org/thialfihar/android/apg/SecretKeyListActivity.java b/src/org/thialfihar/android/apg/SecretKeyListActivity.java
index a69fc5b9c..0252c46d2 100644
--- a/src/org/thialfihar/android/apg/SecretKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/SecretKeyListActivity.java
@@ -16,55 +16,24 @@
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.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.net.Uri;
import android.os.Bundle;
-import android.os.Message;
import android.view.ContextMenu;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
-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;
import android.widget.ExpandableListView.OnChildClickListener;
-public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener {
- ExpandableListView mList;
-
- protected int mSelectedItem = -1;
- protected int mTask = 0;
-
- private String mImportFilename = Constants.path.app_dir + "/secring.gpg";
- private String mExportFilename = Constants.path.app_dir + "/secexport.asc";
-
+public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
@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);
- setContentView(R.layout.key_list);
-
- mList = (ExpandableListView) findViewById(R.id.list);
- mList.setAdapter(new SecretKeyListAdapter(this));
- registerForContextMenu(mList);
mList.setOnChildClickListener(this);
}
@@ -86,16 +55,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
@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;
- }
-
case Id.menu.option.create: {
createKey();
return true;
@@ -113,12 +72,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
- String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
- menu.setHeaderTitle(userId);
+ // TODO: user id? menu.setHeaderTitle("Key");
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.delete, 2, R.string.menu_deleteKey);
@@ -142,18 +98,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
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: {
return super.onContextItemSelected(menuItem);
}
@@ -170,96 +114,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
@Override
protected Dialog onCreateDialog(int id) {
- boolean singleKeyExport = false;
-
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: {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
- long keyId = keyRing.getSecretKey().getKeyID();
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
}
@@ -270,8 +127,7 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
}
public void checkPassPhraseAndEdit() {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
- long keyId = keyRing.getSecretKey().getKeyID();
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
String passPhrase = Apg.getCachedPassPhrase(keyId);
if (passPhrase == null) {
showDialog(Id.dialog.pass_phrase);
@@ -295,10 +151,9 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
}
private void editKey() {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
- long keyId = keyRing.getSecretKey().getKeyID();
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
Intent intent = new Intent(this, EditKeyActivity.class);
- intent.putExtra("keyId", keyId);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
startActivityForResult(intent, Id.message.edit_key);
}
@@ -313,24 +168,6 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
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: {
break;
}
@@ -338,320 +175,4 @@ public class SecretKeyListActivity extends BaseActivity implements OnChildClickL
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.getMessage();
- } catch (PGPException e) {
- error = e.getMessage();
- } catch (Apg.GeneralException e) {
- error = e.getMessage();
- }
-
- 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;
- }
- }
}
diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java
index fbb0b6fe0..aeb6d59a3 100644
--- a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java
@@ -16,12 +16,8 @@
package org.thialfihar.android.apg;
-import java.util.Collections;
import java.util.Vector;
-import org.bouncycastle2.openpgp.PGPPublicKey;
-import org.bouncycastle2.openpgp.PGPPublicKeyRing;
-
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
@@ -42,27 +38,21 @@ public class SelectPublicKeyListActivity extends BaseActivity {
mIntent = getIntent();
long selectedKeyIds[] = null;
if (mIntent.getExtras() != null) {
- selectedKeyIds = mIntent.getExtras().getLongArray("selection");
+ selectedKeyIds = mIntent.getExtras().getLongArray(Apg.EXTRA_SELECTION);
}
mList = (ListView) findViewById(R.id.list);
// needed in Android 1.5, where the XML attribute gets ignored
mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- Vector<PGPPublicKeyRing> keyRings =
- (Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
- Collections.sort(keyRings, new Apg.PublicKeySorter());
- mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings));
+ SelectPublicKeyListAdapter adapter = new SelectPublicKeyListAdapter(this, mList);
+ mList.setAdapter(adapter);
if (selectedKeyIds != null) {
- for (int i = 0; i < keyRings.size(); ++i) {
- PGPPublicKeyRing keyRing = keyRings.get(i);
- PGPPublicKey key = Apg.getMasterKey(keyRing);
- if (key == null) {
- continue;
- }
+ for (int i = 0; i < adapter.getCount(); ++i) {
+ long keyId = adapter.getItemId(i);
for (int j = 0; j < selectedKeyIds.length; ++j) {
- if (key.getKeyID() == selectedKeyIds[j]) {
+ if (keyId == selectedKeyIds[j]) {
mList.setItemChecked(i, true);
break;
}
@@ -106,8 +96,8 @@ public class SelectPublicKeyListActivity extends BaseActivity {
for (int i = 0; i < vector.size(); ++i) {
selectedKeyIds[i] = vector.get(i);
}
- data.putExtra("selection", selectedKeyIds);
+ data.putExtra(Apg.EXTRA_SELECTION, selectedKeyIds);
setResult(RESULT_OK, data);
finish();
}
-} \ No newline at end of file
+}
diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
index 1b0b82fd8..ffc344ead 100644
--- a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
+++ b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
@@ -16,15 +16,16 @@
package org.thialfihar.android.apg;
-import java.text.DateFormat;
import java.util.Date;
-import java.util.Vector;
-import org.bouncycastle2.openpgp.PGPPublicKey;
-import org.bouncycastle2.openpgp.PGPPublicKeyRing;
-import org.thialfihar.android.apg.utils.IterableIterator;
+import org.thialfihar.android.apg.provider.KeyRings;
+import org.thialfihar.android.apg.provider.Keys;
+import org.thialfihar.android.apg.provider.UserIds;
+import android.app.Activity;
import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -34,40 +35,55 @@ import android.widget.ListView;
import android.widget.TextView;
public class SelectPublicKeyListAdapter extends BaseAdapter {
- protected Vector<PGPPublicKeyRing> mKeyRings;
protected LayoutInflater mInflater;
protected ListView mParent;
+ protected SQLiteDatabase mDatabase;
+ protected Cursor mCursor;
- public SelectPublicKeyListAdapter(ListView parent,
- Vector<PGPPublicKeyRing> keyRings) {
- setKeyRings(keyRings);
+ public SelectPublicKeyListAdapter(Activity activity, ListView parent) {
mParent = parent;
+ mDatabase = Apg.getDatabase().db();
mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- public void setKeyRings(Vector<PGPPublicKeyRing> keyRings) {
- mKeyRings = keyRings;
- notifyDataSetChanged();
- }
-
- public Vector<PGPPublicKeyRing> getKeyRings() {
- return mKeyRings;
+ long now = new Date().getTime() / 1000;
+ 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
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_ENCRYPT + " = '1')", // 3
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_ENCRYPT + " = '1' AND " +
+ "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
+ "(tmp." + Keys.EXPIRY + " IS NULL OR " +
+ "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
+ },
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] { "" + Id.database.type_public },
+ null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
+
+ activity.startManagingCursor(mCursor);
}
@Override
public boolean isEnabled(int position) {
- PGPPublicKeyRing keyRing = mKeyRings.get(position);
-
- if (Apg.getMasterKey(keyRing) == null) {
- return false;
- }
-
- Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- return false;
- }
-
- return true;
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
}
@Override
@@ -77,93 +93,62 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
@Override
public int getCount() {
- return mKeyRings.size();
+ return mCursor.getCount();
}
@Override
public Object getItem(int position) {
- return mKeyRings.get(position);
+ return position;
}
@Override
public long getItemId(int position) {
- PGPPublicKeyRing keyRing = mKeyRings.get(position);
- PGPPublicKey key = Apg.getMasterKey(keyRing);
- if (key != null) {
- return key.getKeyID();
- }
-
- return 0;
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
+ mCursor.moveToPosition(position);
+
View view = mInflater.inflate(R.layout.select_public_key_item, null);
boolean enabled = isEnabled(position);
- PGPPublicKeyRing keyRing = mKeyRings.get(position);
- PGPPublicKey key = null;
- for (PGPPublicKey tKey : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (tKey.isMasterKey()) {
- key = tKey;
- break;
- }
- }
-
- Vector<PGPPublicKey> encryptKeys = Apg.getEncryptKeys(keyRing);
- Vector<PGPPublicKey> usableKeys = Apg.getUsableEncryptKeys(keyRing);
-
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText(R.string.unknownUserId);
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
TextView keyId = (TextView) view.findViewById(R.id.keyId);
keyId.setText(R.string.noKey);
- TextView creation = (TextView) view.findViewById(R.id.creation);
- creation.setText(R.string.noDate);
- TextView expiry = (TextView) view.findViewById(R.id.expiry);
- expiry.setText(R.string.noExpiry);
TextView status = (TextView) view.findViewById(R.id.status);
status.setText(R.string.unknownStatus);
- if (key != null) {
- 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);
+ 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]);
}
-
- keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
+ mainUserId.setText(userId);
}
+ long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
+ keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
+
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
}
- PGPPublicKey timespanKey = key;
- if (usableKeys.size() > 0) {
- timespanKey = usableKeys.get(0);
+ if (enabled) {
status.setText(R.string.canEncrypt);
- } else if (encryptKeys.size() > 0) {
- timespanKey = encryptKeys.get(0);
- Date now = new Date();
- if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
- status.setText(R.string.notValid);
- } else {
+ } else {
+ if (mCursor.getInt(3) > 0) {
+ // has some CAN_ENCRYPT keys, but col(4) = 0, so must be revoked or expired
status.setText(R.string.expired);
+ } else {
+ status.setText(R.string.noKey);
}
- } else {
- status.setText(R.string.noKey);
- }
-
- creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
- Date expiryDate = Apg.getExpiryDate(timespanKey);
- if (expiryDate != null) {
- expiry.setText(DateFormat.getDateInstance().format(expiryDate));
}
status.setText(status.getText() + " ");
@@ -176,8 +161,6 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
mainUserId.setEnabled(enabled);
mainUserIdRest.setEnabled(enabled);
keyId.setEnabled(enabled);
- creation.setEnabled(enabled);
- expiry.setEnabled(enabled);
selected.setEnabled(enabled);
status.setEnabled(enabled);
diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java
index b6811d6e3..cd87a94b6 100644
--- a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java
@@ -16,32 +16,16 @@
package org.thialfihar.android.apg;
-import java.text.DateFormat;
-import java.util.Collections;
-import java.util.Date;
-import java.util.Vector;
-
-import org.bouncycastle2.openpgp.PGPSecretKey;
-import org.bouncycastle2.openpgp.PGPSecretKeyRing;
-import org.thialfihar.android.apg.utils.IterableIterator;
-
-import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.AdapterView;
-import android.widget.BaseAdapter;
import android.widget.ListView;
-import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class SelectSecretKeyListActivity extends BaseActivity {
- protected Vector<PGPSecretKeyRing> mKeyRings;
- protected LayoutInflater mInflater;
- protected Intent mIntent;
protected ListView mList;
+ protected SelectSecretKeyListAdapter mListAdapter;
protected long mSelectedKeyId = 0;
@@ -49,158 +33,20 @@ public class SelectSecretKeyListActivity extends BaseActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- // fill things
- mIntent = getIntent();
-
- mKeyRings = (Vector<PGPSecretKeyRing>) Apg.getSecretKeyRings().clone();
- Collections.sort(mKeyRings, new Apg.SecretKeySorter());
-
setContentView(R.layout.select_secret_key);
mList = (ListView) findViewById(R.id.list);
- mList.setAdapter(new SecretKeyListAdapter(this));
+ mListAdapter = new SelectSecretKeyListAdapter(this, mList);
+ mList.setAdapter(mListAdapter);
mList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Intent data = new Intent();
- data.putExtra("selectedKeyId", id);
+ data.putExtra(Apg.EXTRA_KEY_ID, id);
setResult(RESULT_OK, data);
finish();
}
});
}
-
- private class SecretKeyListAdapter extends BaseAdapter {
-
- public SecretKeyListAdapter(Context context) {
- }
-
- @Override
- public boolean isEnabled(int position) {
- PGPSecretKeyRing keyRing = mKeyRings.get(position);
-
- if (Apg.getMasterKey(keyRing) == null) {
- return false;
- }
-
- Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing);
- if (usableKeys.size() == 0) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- @Override
- public int getCount() {
- return mKeyRings.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mKeyRings.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- PGPSecretKeyRing keyRing = mKeyRings.get(position);
- PGPSecretKey key = Apg.getMasterKey(keyRing);
- if (key != null) {
- return key.getKeyID();
- }
-
- return 0;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View view = mInflater.inflate(R.layout.select_secret_key_item, null);
- boolean enabled = isEnabled(position);
-
- PGPSecretKeyRing keyRing = mKeyRings.get(position);
- PGPSecretKey key = null;
- for (PGPSecretKey tKey : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (tKey.isMasterKey()) {
- key = tKey;
- break;
- }
- }
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.unknownUserId);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- keyId.setText(R.string.noKey);
- TextView creation = (TextView) view.findViewById(R.id.creation);
- creation.setText(R.string.noDate);
- TextView expiry = (TextView) view.findViewById(R.id.expiry);
- expiry.setText(R.string.noExpiry);
- TextView status = (TextView) view.findViewById(R.id.status);
- status.setText(R.string.unknownStatus);
-
- if (key != null) {
- 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);
- }
-
- keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
- }
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- }
-
- Vector<PGPSecretKey> signingKeys = Apg.getSigningKeys(keyRing);
- Vector<PGPSecretKey> usableKeys = Apg.getUsableSigningKeys(keyRing);
-
- PGPSecretKey timespanKey = key;
- if (usableKeys.size() > 0) {
- timespanKey = usableKeys.get(0);
- status.setText(R.string.canSign);
- } else if (signingKeys.size() > 0) {
- timespanKey = signingKeys.get(0);
- Date now = new Date();
- if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
- status.setText(R.string.notValid);
- } else {
- status.setText(R.string.expired);
- }
- } else {
- status.setText(R.string.noKey);
- }
-
- creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
- Date expiryDate = Apg.getExpiryDate(timespanKey);
- if (expiryDate != null) {
- expiry.setText(DateFormat.getDateInstance().format(expiryDate));
- }
-
- status.setText(status.getText() + " ");
-
- view.setEnabled(enabled);
- mainUserId.setEnabled(enabled);
- mainUserIdRest.setEnabled(enabled);
- keyId.setEnabled(enabled);
- creation.setEnabled(enabled);
- expiry.setEnabled(enabled);
- status.setEnabled(enabled);
-
- return view;
- }
- }
-} \ No newline at end of file
+}
diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
new file mode 100644
index 000000000..33cd15b40
--- /dev/null
+++ b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
@@ -0,0 +1,147 @@
+package org.thialfihar.android.apg;
+
+import java.util.Date;
+
+import org.thialfihar.android.apg.provider.KeyRings;
+import org.thialfihar.android.apg.provider.Keys;
+import org.thialfihar.android.apg.provider.UserIds;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SelectSecretKeyListAdapter extends BaseAdapter {
+ protected LayoutInflater mInflater;
+ protected ListView mParent;
+ protected SQLiteDatabase mDatabase;
+ protected Cursor mCursor;
+
+ public SelectSecretKeyListAdapter(Activity activity, ListView parent) {
+ mParent = parent;
+ mDatabase = Apg.getDatabase().db();
+ mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ long now = new Date().getTime() / 1000;
+ 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
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_SIGN + " = '1')", // 3,
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_SIGN + " = '1' AND " +
+ "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
+ "(tmp." + Keys.EXPIRY + " IS NULL OR " +
+ "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
+ },
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] { "" + Id.database.type_secret },
+ null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
+
+ activity.startManagingCursor(mCursor);
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(4) > 0; // valid CAN_SIGN
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return position;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ mCursor.moveToPosition(position);
+
+ View view = mInflater.inflate(R.layout.select_secret_key_item, null);
+ boolean enabled = isEnabled(position);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknownUserId);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ keyId.setText(R.string.noKey);
+ TextView status = (TextView) view.findViewById(R.id.status);
+ status.setText(R.string.unknownStatus);
+
+ 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);
+ }
+
+ long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
+ keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+
+ if (enabled) {
+ status.setText(R.string.canSign);
+ } else {
+ if (mCursor.getInt(3) > 0) {
+ // has some CAN_SIGN keys, but col(4) = 0, so must be revoked or expired
+ status.setText(R.string.expired);
+ } else {
+ status.setText(R.string.noKey);
+ }
+ }
+
+ status.setText(status.getText() + " ");
+
+ view.setEnabled(enabled);
+ mainUserId.setEnabled(enabled);
+ mainUserIdRest.setEnabled(enabled);
+ keyId.setEnabled(enabled);
+ status.setEnabled(enabled);
+
+ return view;
+ }
+} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/Service.java b/src/org/thialfihar/android/apg/Service.java
new file mode 100644
index 000000000..4457274ff
--- /dev/null
+++ b/src/org/thialfihar/android/apg/Service.java
@@ -0,0 +1,81 @@
+package org.thialfihar.android.apg;
+
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+
+public class Service extends android.app.Service {
+ private final IBinder mBinder = new LocalBinder();
+
+ public static final String EXTRA_TTL = "ttl";
+
+ private int mPassPhraseCacheTtl = 15;
+ private Handler mCacheHandler = new Handler();
+ private Runnable mCacheTask = new Runnable() {
+ public void run() {
+ // TODO: I suppose we could read out the time left until the first cache entry
+ // expiration, then use that for the timer...
+
+ // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
+ // and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl
+ int delay = mPassPhraseCacheTtl * 1000 / 2;
+ // also make sure the delay is not longer than one minute
+ if (delay > 60000) {
+ delay = 60000;
+ }
+
+ delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay);
+ // don't check too often, even if we were close
+ if (delay < 5000) {
+ delay = 5000;
+ }
+
+ mCacheHandler.postDelayed(this, delay);
+ }
+ };
+
+ static private boolean mIsRunning = false;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mIsRunning = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mIsRunning = false;
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+
+ if (intent != null) {
+ mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
+ }
+ if (mPassPhraseCacheTtl < 15) {
+ mPassPhraseCacheTtl = 15;
+ }
+ mCacheHandler.removeCallbacks(mCacheTask);
+ mCacheHandler.postDelayed(mCacheTask, 1000);
+ }
+
+ static public boolean isRunning() {
+ return mIsRunning;
+ }
+
+ public class LocalBinder extends Binder {
+ Service getService() {
+ return Service.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/provider/Accounts.java b/src/org/thialfihar/android/apg/provider/Accounts.java
index 4fce2b607..8162472ec 100644
--- a/src/org/thialfihar/android/apg/provider/Accounts.java
+++ b/src/org/thialfihar/android/apg/provider/Accounts.java
@@ -16,7 +16,12 @@
package org.thialfihar.android.apg.provider;
-public class Accounts extends Accounts1 {
- private Accounts() {
- }
-} \ No newline at end of file
+import android.provider.BaseColumns;
+
+public class Accounts implements BaseColumns {
+ public static final String TABLE_NAME = "accounts";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String NAME = "c_name";
+ public static final String NAME_type = "TEXT";
+}
diff --git a/src/org/thialfihar/android/apg/provider/DataProvider.java b/src/org/thialfihar/android/apg/provider/DataProvider.java
index fbc1be047..8a3fefdff 100644
--- a/src/org/thialfihar/android/apg/provider/DataProvider.java
+++ b/src/org/thialfihar/android/apg/provider/DataProvider.java
@@ -18,15 +18,12 @@ package org.thialfihar.android.apg.provider;
import java.util.HashMap;
+import org.thialfihar.android.apg.Id;
+
import android.content.ContentProvider;
-import android.content.ContentUris;
import android.content.ContentValues;
-import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
@@ -34,153 +31,190 @@ import android.text.TextUtils;
public class DataProvider extends ContentProvider {
public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
- private static final String DATABASE_NAME = "apg";
- private static final int DATABASE_VERSION = 1;
-
- private static final int PUBLIC_KEYS = 101;
- private static final int PUBLIC_KEY_ID = 102;
- private static final int PUBLIC_KEY_BY_KEY_ID = 103;
-
- private static final int SECRET_KEYS = 201;
- private static final int SECRET_KEY_ID = 202;
- private static final int SECRET_KEY_BY_KEY_ID = 203;
-
- private static final int ACCOUNTS = 301;
- private static final int ACCOUNT_ID = 302;
+ private static final int PUBLIC_KEY_RINGS = 101;
+ private static final int PUBLIC_KEY_RING_ID = 102;
+ private static final int PUBLIC_KEY_RING_BY_KEY_ID = 103;
+ private static final int PUBLIC_KEY_RING_KEYS = 111;
+ private static final int PUBLIC_KEY_RING_KEY_RANK = 112;
+ private static final int PUBLIC_KEY_RING_USER_IDS = 121;
+ private static final int PUBLIC_KEY_RING_USER_ID_RANK = 122;
+
+ private static final int SECRET_KEY_RINGS = 201;
+ private static final int SECRET_KEY_RING_ID = 202;
+ private static final int SECRET_KEY_RING_BY_KEY_ID = 203;
+ private static final int SECRET_KEY_RING_KEYS = 211;
+ private static final int SECRET_KEY_RING_KEY_RANK = 212;
+ private static final int SECRET_KEY_RING_USER_IDS = 221;
+ private static final int SECRET_KEY_RING_USER_ID_RANK = 222;
+
+ private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring";
+ private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.public.key_ring";
+
+ private static final String PUBLIC_KEY_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key";
+ private static final String PUBLIC_KEY_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.public.key";
+
+ private static final String SECRET_KEY_RING_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key_ring";
+ private static final String SECRET_KEY_RING_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key_ring";
+
+ private static final String SECRET_KEY_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key";
+ private static final String SECRET_KEY_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key";
+
+ private static final String USER_ID_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
+ private static final String USER_ID_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
+
+ public static final String MASTER_KEY_ID = "master_key_id";
+ public static final String KEY_ID = "key_id";
+ public static final String USER_ID = "user_id";
private static final UriMatcher mUriMatcher;
- private static final HashMap<String, String> mPublicKeysProjectionMap;
- private static final HashMap<String, String> mSecretKeysProjectionMap;
- private static final HashMap<String, String> mAccountsProjectionMap;
- private DatabaseHelper mdbHelper;
+ private Database mDb;
static {
mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys", PUBLIC_KEYS);
- mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/#", PUBLIC_KEY_ID);
- mUriMatcher.addURI(DataProvider.AUTHORITY, "public_keys/key_id/*", PUBLIC_KEY_BY_KEY_ID);
-
- mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys", SECRET_KEYS);
- mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/#", SECRET_KEY_ID);
- mUriMatcher.addURI(DataProvider.AUTHORITY, "secret_keys/key_id/*", SECRET_KEY_BY_KEY_ID);
-
- mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts", ACCOUNTS);
- mUriMatcher.addURI(DataProvider.AUTHORITY, "accounts/#", ACCOUNT_ID);
-
- mPublicKeysProjectionMap = new HashMap<String, String>();
- mPublicKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID);
- mPublicKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID);
- mPublicKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA);
- mPublicKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID);
-
- mSecretKeysProjectionMap = new HashMap<String, String>();
- mSecretKeysProjectionMap.put(PublicKeys._ID, PublicKeys._ID);
- mSecretKeysProjectionMap.put(PublicKeys.KEY_ID, PublicKeys.KEY_ID);
- mSecretKeysProjectionMap.put(PublicKeys.KEY_DATA, PublicKeys.KEY_DATA);
- mSecretKeysProjectionMap.put(PublicKeys.WHO_ID, PublicKeys.WHO_ID);
-
- mAccountsProjectionMap = new HashMap<String, String>();
- mAccountsProjectionMap.put(Accounts._ID, Accounts._ID);
- mAccountsProjectionMap.put(Accounts.NAME, Accounts.NAME);
- }
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID);
- /**
- * This class helps open, create, and upgrade the database file.
- */
- private static class DatabaseHelper extends SQLiteOpenHelper {
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEYS);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK);
- DatabaseHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- }
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_IDS);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK);
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + PublicKeys.TABLE_NAME + " (" +
- PublicKeys._ID + " " + PublicKeys._ID_type + "," +
- PublicKeys.KEY_ID + " " + PublicKeys.KEY_ID_type + ", " +
- PublicKeys.KEY_DATA + " " + PublicKeys.KEY_DATA_type + ", " +
- PublicKeys.WHO_ID + " " + PublicKeys.WHO_ID_type + ");");
-
- db.execSQL("CREATE TABLE " + SecretKeys.TABLE_NAME + " (" +
- SecretKeys._ID + " " + SecretKeys._ID_type + "," +
- SecretKeys.KEY_ID + " " + SecretKeys.KEY_ID_type + ", " +
- SecretKeys.KEY_DATA + " " + SecretKeys.KEY_DATA_type + ", " +
- SecretKeys.WHO_ID + " " + SecretKeys.WHO_ID_type + ");");
-
- db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
- Accounts._ID + " " + Accounts._ID_type + "," +
- Accounts.NAME + " " + Accounts.NAME_type + ");");
- }
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RINGS);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID);
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- // TODO: upgrade db if necessary, and do that in a clever way
- }
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEYS);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_IDS);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RINGS);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID);
}
@Override
public boolean onCreate() {
- mdbHelper = new DatabaseHelper(getContext());
+ mDb = new Database(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
+ // TODO: implement the others, then use them for the lists
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-
- switch (mUriMatcher.match(uri)) {
- case PUBLIC_KEYS: {
- qb.setTables(PublicKeys.TABLE_NAME);
- qb.setProjectionMap(mPublicKeysProjectionMap);
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ int match = mUriMatcher.match(uri);
+ int type;
+ switch (match) {
+ case PUBLIC_KEY_RINGS:
+ case PUBLIC_KEY_RING_ID:
+ case PUBLIC_KEY_RING_BY_KEY_ID:
+ case PUBLIC_KEY_RING_KEYS:
+ case PUBLIC_KEY_RING_KEY_RANK:
+ case PUBLIC_KEY_RING_USER_IDS:
+ case PUBLIC_KEY_RING_USER_ID_RANK:
+ type = Id.database.type_public;
break;
- }
- case PUBLIC_KEY_ID: {
- qb.setTables(PublicKeys.TABLE_NAME);
- qb.setProjectionMap(mPublicKeysProjectionMap);
- qb.appendWhere(PublicKeys._ID + "=" + uri.getPathSegments().get(1));
+ case SECRET_KEY_RINGS:
+ case SECRET_KEY_RING_ID:
+ case SECRET_KEY_RING_BY_KEY_ID:
+ case SECRET_KEY_RING_KEYS:
+ case SECRET_KEY_RING_KEY_RANK:
+ case SECRET_KEY_RING_USER_IDS:
+ case SECRET_KEY_RING_USER_ID_RANK:
+ type = Id.database.type_secret;
break;
- }
- case PUBLIC_KEY_BY_KEY_ID: {
- qb.setTables(PublicKeys.TABLE_NAME);
- qb.setProjectionMap(mPublicKeysProjectionMap);
- qb.appendWhere(PublicKeys.KEY_ID + "=" + uri.getPathSegments().get(2));
- break;
+ default: {
+ throw new IllegalArgumentException("Unknown URI " + uri);
}
+ }
- case SECRET_KEYS: {
- qb.setTables(SecretKeys.TABLE_NAME);
- qb.setProjectionMap(mSecretKeysProjectionMap);
- break;
- }
+ qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type);
+
+ switch (match) {
+ case PUBLIC_KEY_RINGS:
+ case SECRET_KEY_RINGS: {
+ qb.setTables(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') ");
+
+ projectionMap.put(MASTER_KEY_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
+ projectionMap.put(USER_ID,
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID);
- case SECRET_KEY_ID: {
- qb.setTables(SecretKeys.TABLE_NAME);
- qb.setProjectionMap(mSecretKeysProjectionMap);
- qb.appendWhere(SecretKeys._ID + "=" + uri.getPathSegments().get(1));
break;
}
- case SECRET_KEY_BY_KEY_ID: {
- qb.setTables(SecretKeys.TABLE_NAME);
- qb.setProjectionMap(mSecretKeysProjectionMap);
- qb.appendWhere(SecretKeys.KEY_ID + "=" + uri.getPathSegments().get(2));
+ case PUBLIC_KEY_RING_ID:
+ case SECRET_KEY_RING_ID: {
+ qb.setTables(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') ");
+
+ projectionMap.put(MASTER_KEY_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
+ projectionMap.put(USER_ID,
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID);
+
+ qb.appendWhere(" AND " +
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
break;
}
- case ACCOUNTS: {
- qb.setTables(Accounts.TABLE_NAME);
- qb.setProjectionMap(mAccountsProjectionMap);
- break;
- }
+ case SECRET_KEY_RING_BY_KEY_ID:
+ case PUBLIC_KEY_RING_BY_KEY_ID: {
+ qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " +
+ KeyRings.TABLE_NAME + " ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ "tmp." + Keys.KEY_RING_ID + ")" +
+ " 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') ");
+
+ projectionMap.put(MASTER_KEY_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
+ projectionMap.put(USER_ID,
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID);
+
+ qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(3));
- case ACCOUNT_ID: {
- qb.setTables(Accounts.TABLE_NAME);
- qb.setProjectionMap(mAccountsProjectionMap);
- qb.appendWhere(Accounts._ID + "=" + uri.getPathSegments().get(1));
break;
}
@@ -189,20 +223,20 @@ public class DataProvider extends ContentProvider {
}
}
+ qb.setProjectionMap(projectionMap);
+
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
- orderBy = PublicKeys.DEFAULT_SORT_ORDER;
+ orderBy = null;
} else {
orderBy = sortOrder;
}
- // Get the database and run the query
- SQLiteDatabase db = mdbHelper.getReadableDatabase();
- Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
+ //System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n"));
+ Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy);
- // Tell the cursor what uri to watch, so it knows when its source data
- // changes
+ // Tell the cursor what uri to watch, so it knows when its source data changes
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
@@ -210,278 +244,68 @@ public class DataProvider extends ContentProvider {
@Override
public String getType(Uri uri) {
switch (mUriMatcher.match(uri)) {
- case PUBLIC_KEYS: {
- return PublicKeys.CONTENT_TYPE;
- }
+ case PUBLIC_KEY_RINGS:
+ return PUBLIC_KEY_RING_CONTENT_DIR_TYPE;
- case PUBLIC_KEY_ID: {
- return PublicKeys.CONTENT_ITEM_TYPE;
- }
+ case PUBLIC_KEY_RING_ID:
+ return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
- case PUBLIC_KEY_BY_KEY_ID: {
- return PublicKeys.CONTENT_ITEM_TYPE;
- }
+ case PUBLIC_KEY_RING_BY_KEY_ID:
+ return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
- case SECRET_KEYS: {
- return SecretKeys.CONTENT_TYPE;
- }
+ case PUBLIC_KEY_RING_KEYS:
+ return PUBLIC_KEY_CONTENT_DIR_TYPE;
- case SECRET_KEY_ID: {
- return SecretKeys.CONTENT_ITEM_TYPE;
- }
+ case PUBLIC_KEY_RING_KEY_RANK:
+ return PUBLIC_KEY_CONTENT_ITEM_TYPE;
- case SECRET_KEY_BY_KEY_ID: {
- return SecretKeys.CONTENT_ITEM_TYPE;
- }
+ case PUBLIC_KEY_RING_USER_IDS:
+ return USER_ID_CONTENT_DIR_TYPE;
- case ACCOUNTS: {
- return Accounts.CONTENT_TYPE;
- }
+ case PUBLIC_KEY_RING_USER_ID_RANK:
+ return USER_ID_CONTENT_ITEM_TYPE;
- case ACCOUNT_ID: {
- return Accounts.CONTENT_ITEM_TYPE;
- }
+ case SECRET_KEY_RINGS:
+ return SECRET_KEY_RING_CONTENT_DIR_TYPE;
- default: {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
- }
+ case SECRET_KEY_RING_ID:
+ return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
- @Override
- public Uri insert(Uri uri, ContentValues initialValues) {
- switch (mUriMatcher.match(uri)) {
- case PUBLIC_KEYS: {
- ContentValues values;
- if (initialValues != null) {
- values = new ContentValues(initialValues);
- } else {
- values = new ContentValues();
- }
-
- if (!values.containsKey(PublicKeys.WHO_ID)) {
- values.put(PublicKeys.WHO_ID, "");
- }
-
- SQLiteDatabase db = mdbHelper.getWritableDatabase();
- long rowId = db.insert(PublicKeys.TABLE_NAME, PublicKeys.WHO_ID, values);
- if (rowId > 0) {
- Uri transferUri = ContentUris.withAppendedId(PublicKeys.CONTENT_URI, rowId);
- getContext().getContentResolver().notifyChange(transferUri, null);
- return transferUri;
- }
-
- throw new SQLException("Failed to insert row into " + uri);
- }
+ case SECRET_KEY_RING_BY_KEY_ID:
+ return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
- case SECRET_KEYS: {
- ContentValues values;
- if (initialValues != null) {
- values = new ContentValues(initialValues);
- } else {
- values = new ContentValues();
- }
-
- if (!values.containsKey(SecretKeys.WHO_ID)) {
- values.put(SecretKeys.WHO_ID, "");
- }
-
- SQLiteDatabase db = mdbHelper.getWritableDatabase();
- long rowId = db.insert(SecretKeys.TABLE_NAME, SecretKeys.WHO_ID, values);
- if (rowId > 0) {
- Uri transferUri = ContentUris.withAppendedId(SecretKeys.CONTENT_URI, rowId);
- getContext().getContentResolver().notifyChange(transferUri, null);
- return transferUri;
- }
-
- throw new SQLException("Failed to insert row into " + uri);
- }
+ case SECRET_KEY_RING_KEYS:
+ return SECRET_KEY_CONTENT_DIR_TYPE;
- case ACCOUNTS: {
- ContentValues values;
- if (initialValues != null) {
- values = new ContentValues(initialValues);
- } else {
- values = new ContentValues();
- }
-
- SQLiteDatabase db = mdbHelper.getWritableDatabase();
- long rowId = db.insert(Accounts.TABLE_NAME, null, values);
- if (rowId > 0) {
- Uri transferUri = ContentUris.withAppendedId(Accounts.CONTENT_URI, rowId);
- getContext().getContentResolver().notifyChange(transferUri, null);
- return transferUri;
- }
-
- throw new SQLException("Failed to insert row into " + uri);
- }
+ case SECRET_KEY_RING_KEY_RANK:
+ return SECRET_KEY_CONTENT_ITEM_TYPE;
- default: {
+ case SECRET_KEY_RING_USER_IDS:
+ return USER_ID_CONTENT_DIR_TYPE;
+
+ case SECRET_KEY_RING_USER_ID_RANK:
+ return USER_ID_CONTENT_ITEM_TYPE;
+
+ default:
throw new IllegalArgumentException("Unknown URI " + uri);
- }
}
}
@Override
- public int delete(Uri uri, String where, String[] whereArgs) {
- SQLiteDatabase db = mdbHelper.getWritableDatabase();
- int count;
- switch (mUriMatcher.match(uri)) {
- case PUBLIC_KEYS: {
- count = db.delete(PublicKeys.TABLE_NAME, where, whereArgs);
- break;
- }
-
- case PUBLIC_KEY_ID: {
- String publicKeyId = uri.getPathSegments().get(1);
- count = db.delete(PublicKeys.TABLE_NAME,
- PublicKeys._ID + "=" + publicKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case PUBLIC_KEY_BY_KEY_ID: {
- String publicKeyKeyId = uri.getPathSegments().get(2);
- count = db.delete(PublicKeys.TABLE_NAME,
- PublicKeys.KEY_ID + "=" + publicKeyKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case SECRET_KEYS: {
- count = db.delete(SecretKeys.TABLE_NAME, where, whereArgs);
- break;
- }
-
- case SECRET_KEY_ID: {
- String secretKeyId = uri.getPathSegments().get(1);
- count = db.delete(SecretKeys.TABLE_NAME,
- SecretKeys._ID + "=" + secretKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case SECRET_KEY_BY_KEY_ID: {
- String secretKeyKeyId = uri.getPathSegments().get(2);
- count = db.delete(SecretKeys.TABLE_NAME,
- SecretKeys.KEY_ID + "=" + secretKeyKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case ACCOUNTS: {
- count = db.delete(Accounts.TABLE_NAME, where, whereArgs);
- break;
- }
-
- case ACCOUNT_ID: {
- String accountId = uri.getPathSegments().get(1);
- count = db.delete(Accounts.TABLE_NAME,
- Accounts._ID + "=" + accountId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- default: {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
+ public Uri insert(Uri uri, ContentValues initialValues) {
+ // not supported
+ return null;
+ }
- getContext().getContentResolver().notifyChange(uri, null);
- return count;
+ @Override
+ public int delete(Uri uri, String where, String[] whereArgs) {
+ // not supported
+ return 0;
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
- SQLiteDatabase db = mdbHelper.getWritableDatabase();
- int count;
- switch (mUriMatcher.match(uri)) {
- case PUBLIC_KEYS: {
- count = db.update(PublicKeys.TABLE_NAME, values, where, whereArgs);
- break;
- }
-
- case PUBLIC_KEY_ID: {
- String publicKeyId = uri.getPathSegments().get(1);
-
- count = db.update(PublicKeys.TABLE_NAME, values,
- PublicKeys._ID + "=" + publicKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case PUBLIC_KEY_BY_KEY_ID: {
- String publicKeyKeyId = uri.getPathSegments().get(2);
-
- count = db.update(PublicKeys.TABLE_NAME, values,
- PublicKeys.KEY_ID + "=" + publicKeyKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case SECRET_KEYS: {
- count = db.update(SecretKeys.TABLE_NAME, values, where, whereArgs);
- break;
- }
-
- case SECRET_KEY_ID: {
- String secretKeyId = uri.getPathSegments().get(1);
-
- count = db.update(SecretKeys.TABLE_NAME, values,
- SecretKeys._ID + "=" + secretKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case SECRET_KEY_BY_KEY_ID: {
- String secretKeyKeyId = uri.getPathSegments().get(2);
-
- count = db.update(SecretKeys.TABLE_NAME, values,
- SecretKeys.KEY_ID + "=" + secretKeyKeyId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- case ACCOUNTS: {
- count = db.update(Accounts.TABLE_NAME, values, where, whereArgs);
- break;
- }
-
- case ACCOUNT_ID: {
- String accountId = uri.getPathSegments().get(1);
-
- count = db.update(Accounts.TABLE_NAME, values,
- Accounts._ID + "=" + accountId +
- (!TextUtils.isEmpty(where) ?
- " AND (" + where + ')' : ""),
- whereArgs);
- break;
- }
-
- default: {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
-
- getContext().getContentResolver().notifyChange(uri, null);
- return count;
+ // not supported
+ return 0;
}
}
diff --git a/src/org/thialfihar/android/apg/provider/Database.java b/src/org/thialfihar/android/apg/provider/Database.java
new file mode 100644
index 000000000..810ebebbf
--- /dev/null
+++ b/src/org/thialfihar/android/apg/provider/Database.java
@@ -0,0 +1,605 @@
+package org.thialfihar.android.apg.provider;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Vector;
+
+import org.bouncycastle2.openpgp.PGPException;
+import org.bouncycastle2.openpgp.PGPPublicKey;
+import org.bouncycastle2.openpgp.PGPPublicKeyRing;
+import org.bouncycastle2.openpgp.PGPSecretKey;
+import org.bouncycastle2.openpgp.PGPSecretKeyRing;
+import org.thialfihar.android.apg.Apg;
+import org.thialfihar.android.apg.Id;
+import org.thialfihar.android.apg.utils.IterableIterator;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+public class Database extends SQLiteOpenHelper {
+ public static class GeneralException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public GeneralException(String message) {
+ super(message);
+ }
+ }
+
+ private static final String DATABASE_NAME = "apg";
+ private static final int DATABASE_VERSION = 2;
+
+ public static final String AUTHORITY = "org.thialfihar.android.apg.database";
+
+ public static HashMap<String, String> sKeyRingsProjection;
+ public static HashMap<String, String> sKeysProjection;
+ public static HashMap<String, String> sUserIdsProjection;
+
+ private SQLiteDatabase mDb = null;
+ private int mStatus = 0;
+
+ static {
+ sKeyRingsProjection = new HashMap<String, String>();
+ sKeyRingsProjection.put(KeyRings._ID, KeyRings._ID);
+ sKeyRingsProjection.put(KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID);
+ sKeyRingsProjection.put(KeyRings.TYPE, KeyRings.TYPE);
+ sKeyRingsProjection.put(KeyRings.WHO_ID, KeyRings.WHO_ID);
+ sKeyRingsProjection.put(KeyRings.KEY_RING_DATA, KeyRings.KEY_RING_DATA);
+
+ sKeysProjection = new HashMap<String, String>();
+ sKeysProjection.put(Keys._ID, Keys._ID);
+ sKeysProjection.put(Keys.KEY_ID, Keys.KEY_ID);
+ sKeysProjection.put(Keys.TYPE, Keys.TYPE);
+ sKeysProjection.put(Keys.IS_MASTER_KEY, Keys.IS_MASTER_KEY);
+ sKeysProjection.put(Keys.ALGORITHM, Keys.ALGORITHM);
+ sKeysProjection.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
+ sKeysProjection.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
+ sKeysProjection.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
+ sKeysProjection.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
+ sKeysProjection.put(Keys.CREATION, Keys.CREATION);
+ sKeysProjection.put(Keys.EXPIRY, Keys.EXPIRY);
+ sKeysProjection.put(Keys.KEY_DATA, Keys.KEY_DATA);
+ sKeysProjection.put(Keys.RANK, Keys.RANK);
+
+ sUserIdsProjection = new HashMap<String, String>();
+ sUserIdsProjection.put(UserIds._ID, UserIds._ID);
+ sUserIdsProjection.put(UserIds.KEY_ID, UserIds.KEY_ID);
+ sUserIdsProjection.put(UserIds.USER_ID, UserIds.USER_ID);
+ sUserIdsProjection.put(UserIds.RANK, UserIds.RANK);
+ }
+
+ public Database(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ // force upgrade to test things
+ //onUpgrade(getWritableDatabase(), 1, 2);
+ mDb = getWritableDatabase();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ mDb.close();
+ super.finalize();
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
+ KeyRings._ID + " " + KeyRings._ID_type + "," +
+ KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
+ KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
+ KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
+ KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
+
+ db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
+ Keys._ID + " " + Keys._ID_type + "," +
+ Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
+ Keys.TYPE + " " + Keys.TYPE_type + ", " +
+ Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
+ Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
+ Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
+ Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
+ Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
+ Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
+ Keys.CREATION + " " + Keys.CREATION_type + ", " +
+ Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
+ Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
+ Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
+ Keys.RANK + " " + Keys.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
+ UserIds._ID + " " + UserIds._ID_type + "," +
+ UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
+ UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
+ UserIds.RANK + " " + UserIds.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
+ Accounts._ID + " " + Accounts._ID_type + "," +
+ Accounts.NAME + " " + Accounts.NAME_type + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ mDb = db;
+ for (int version = oldVersion; version < newVersion; ++version) {
+ switch (version) {
+ case 1: { // upgrade 1 to 2
+ db.execSQL("DROP TABLE IF EXISTS " + KeyRings.TABLE_NAME + ";");
+ db.execSQL("DROP TABLE IF EXISTS " + Keys.TABLE_NAME + ";");
+ db.execSQL("DROP TABLE IF EXISTS " + UserIds.TABLE_NAME + ";");
+
+ db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
+ KeyRings._ID + " " + KeyRings._ID_type + "," +
+ KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
+ KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
+ KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
+ KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
+
+ db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
+ Keys._ID + " " + Keys._ID_type + "," +
+ Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
+ Keys.TYPE + " " + Keys.TYPE_type + ", " +
+ Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
+ Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
+ Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
+ Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
+ Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
+ Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
+ Keys.CREATION + " " + Keys.CREATION_type + ", " +
+ Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
+ Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
+ Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
+ Keys.RANK + " " + Keys.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
+ UserIds._ID + " " + UserIds._ID_type + "," +
+ UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
+ UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
+ UserIds.RANK + " " + UserIds.RANK_type + ");");
+
+ Cursor cursor = db.query("public_keys", new String[] { "c_key_data" },
+ null, null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ byte[] data = cursor.getBlob(0);
+ try {
+ PGPPublicKeyRing keyRing = new PGPPublicKeyRing(data);
+ saveKeyRing(keyRing);
+ } catch (IOException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (GeneralException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ }
+ } while (cursor.moveToNext());
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ cursor = db.query("secret_keys", new String[]{ "c_key_data" },
+ null, null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ byte[] data = cursor.getBlob(0);
+ try {
+ PGPSecretKeyRing keyRing = new PGPSecretKeyRing(data);
+ saveKeyRing(keyRing);
+ } catch (IOException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (PGPException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (GeneralException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ }
+ } while (cursor.moveToNext());
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ db.execSQL("DROP TABLE IF EXISTS public_keys;");
+ db.execSQL("DROP TABLE IF EXISTS secret_keys;");
+
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+ mDb = null;
+ }
+
+ public int saveKeyRing(PGPPublicKeyRing keyRing) throws IOException, GeneralException {
+ mDb.beginTransaction();
+ ContentValues values = new ContentValues();
+ PGPPublicKey masterKey = keyRing.getPublicKey();
+ long masterKeyId = masterKey.getKeyID();
+
+ values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRings.TYPE, Id.database.type_public);
+ values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+
+ long rowId = insertOrUpdateKeyRing(values);
+ int returnValue = mStatus;
+
+ if (rowId == -1) {
+ throw new GeneralException("saving public key ring " + masterKeyId + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int rank = 0;
+ for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
+ seenIds.add(saveKey(rowId, key, rank));
+ ++rank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(Keys.TABLE_NAME,
+ Keys.KEY_RING_ID + " = ? AND " +
+ Keys._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ return returnValue;
+ }
+
+ public int saveKeyRing(PGPSecretKeyRing keyRing) throws IOException, GeneralException {
+ mDb.beginTransaction();
+ ContentValues values = new ContentValues();
+ PGPSecretKey masterKey = keyRing.getSecretKey();
+ long masterKeyId = masterKey.getKeyID();
+
+ values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRings.TYPE, Id.database.type_secret);
+ values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+
+ long rowId = insertOrUpdateKeyRing(values);
+ int returnValue = mStatus;
+
+ if (rowId == -1) {
+ throw new GeneralException("saving secret key ring " + masterKeyId + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int rank = 0;
+ for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
+ seenIds.add(saveKey(rowId, key, rank));
+ ++rank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(Keys.TABLE_NAME,
+ Keys.KEY_RING_ID + " = ? AND " +
+ Keys._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ return returnValue;
+ }
+
+ private int saveKey(long keyRingId, PGPPublicKey key, int rank)
+ throws IOException, GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(Keys.KEY_ID, key.getKeyID());
+ values.put(Keys.TYPE, Id.database.type_public);
+ values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
+ values.put(Keys.ALGORITHM, key.getAlgorithm());
+ values.put(Keys.KEY_SIZE, key.getBitStrength());
+ values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
+ values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
+ values.put(Keys.IS_REVOKED, key.isRevoked());
+ values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
+ Date expiryDate = Apg.getExpiryDate(key);
+ if (expiryDate != null) {
+ values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
+ }
+ values.put(Keys.KEY_RING_ID, keyRingId);
+ values.put(Keys.KEY_DATA, key.getEncoded());
+ values.put(Keys.RANK, rank);
+
+ long rowId = insertOrUpdateKey(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving public key " + key.getKeyID() + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int userIdRank = 0;
+ for (String userId : new IterableIterator<String>(key.getUserIDs())) {
+ seenIds.add(saveUserId(rowId, userId, userIdRank));
+ ++userIdRank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ? AND " +
+ UserIds._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ return (int)rowId;
+ }
+
+ private int saveKey(long keyRingId, PGPSecretKey key, int rank)
+ throws IOException, GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(Keys.KEY_ID, key.getPublicKey().getKeyID());
+ values.put(Keys.TYPE, Id.database.type_secret);
+ values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
+ values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
+ values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
+ values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
+ values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
+ values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
+ values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
+ Date expiryDate = Apg.getExpiryDate(key);
+ if (expiryDate != null) {
+ values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
+ }
+ values.put(Keys.KEY_RING_ID, keyRingId);
+ values.put(Keys.KEY_DATA, key.getEncoded());
+ values.put(Keys.RANK, rank);
+
+ long rowId = insertOrUpdateKey(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving secret key " + key.getPublicKey().getKeyID() + " failed");
+ }
+
+ Vector<Integer> seenIds = new Vector<Integer>();
+ int userIdRank = 0;
+ for (String userId : new IterableIterator<String>(key.getUserIDs())) {
+ seenIds.add(saveUserId(rowId, userId, userIdRank));
+ ++userIdRank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ? AND " +
+ UserIds._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ return (int)rowId;
+ }
+
+ private int saveUserId(long keyId, String userId, int rank) throws GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(UserIds.KEY_ID, keyId);
+ values.put(UserIds.USER_ID, userId);
+ values.put(UserIds.RANK, rank);
+
+ long rowId = insertOrUpdateUserId(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving user id " + userId + " failed");
+ }
+
+ return (int)rowId;
+ }
+
+ private long insertOrUpdateKeyRing(ContentValues values) {
+ Cursor c = mDb.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID },
+ KeyRings.MASTER_KEY_ID + " = ? AND " + KeyRings.TYPE + " = ?",
+ new String[] {
+ values.getAsString(KeyRings.MASTER_KEY_ID),
+ values.getAsString(KeyRings.TYPE),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(KeyRings.TABLE_NAME, values,
+ KeyRings._ID + " = ?", new String[] { "" + rowId });
+ mStatus = Id.return_value.updated;
+ } else {
+ rowId = mDb.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values);
+ mStatus = Id.return_value.ok;
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ private long insertOrUpdateKey(ContentValues values) {
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
+ Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
+ new String[] {
+ values.getAsString(Keys.KEY_ID),
+ values.getAsString(Keys.TYPE),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(Keys.TABLE_NAME, values,
+ Keys._ID + " = ?", new String[] { "" + rowId });
+ } else {
+ rowId = mDb.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ private long insertOrUpdateUserId(ContentValues values) {
+ Cursor c = mDb.query(UserIds.TABLE_NAME, new String[] { UserIds._ID },
+ UserIds.KEY_ID + " = ? AND " + UserIds.USER_ID + " = ?",
+ new String[] {
+ values.getAsString(UserIds.KEY_ID),
+ values.getAsString(UserIds.USER_ID),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(UserIds.TABLE_NAME, values,
+ UserIds._ID + " = ?", new String[] { "" + rowId });
+ } else {
+ rowId = mDb.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ public Object getKeyRing(int keyRingId) {
+ Cursor c = mDb.query(KeyRings.TABLE_NAME,
+ new String[] { KeyRings.KEY_RING_DATA, KeyRings.TYPE },
+ KeyRings._ID + " = ?",
+ new String[] {
+ "" + keyRingId,
+ },
+ null, null, null);
+ byte[] data = null;
+ Object keyRing = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ if (data != null) {
+ try {
+ if (c.getInt(1) == Id.database.type_public) {
+ keyRing = new PGPPublicKeyRing(data);
+ } else {
+ keyRing = new PGPSecretKeyRing(data);
+ }
+ } catch (IOException e) {
+ // can't load it, then
+ } catch (PGPException e) {
+ // can't load it, then
+ }
+ }
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return keyRing;
+ }
+
+ public byte[] getKeyRingDataFromKeyId(int type, long keyId) {
+ Cursor c = mDb.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ")",
+ new String[] { KeyRings.TABLE_NAME + "." + KeyRings.KEY_RING_DATA },
+ Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+
+ byte[] data = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return data;
+ }
+
+ public byte[] getKeyDataFromKeyId(int type, long keyId) {
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys.KEY_DATA },
+ Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+ byte[] data = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return data;
+ }
+
+ public void deleteKeyRing(int keyRingId) {
+ mDb.beginTransaction();
+ mDb.delete(KeyRings.TABLE_NAME,
+ KeyRings._ID + " = ?", new String[] { "" + keyRingId });
+
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
+ Keys.KEY_RING_ID + " = ?",
+ new String[] {
+ "" + keyRingId,
+ },
+ null, null, null);
+ if (c != null && c.moveToFirst()) {
+ do {
+ int keyId = c.getInt(0);
+ deleteKey(keyId);
+ } while (c.moveToNext());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ }
+
+ private void deleteKey(int keyId) {
+ mDb.delete(Keys.TABLE_NAME,
+ Keys._ID + " = ?", new String[] { "" + keyId });
+
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ?", new String[] { "" + keyId });
+ }
+
+ public SQLiteDatabase db() {
+ return mDb;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/provider/Accounts1.java b/src/org/thialfihar/android/apg/provider/KeyRings.java
index 9009e4598..a4eae6695 100644
--- a/src/org/thialfihar/android/apg/provider/Accounts1.java
+++ b/src/org/thialfihar/android/apg/provider/KeyRings.java
@@ -1,36 +1,33 @@
-/*
- * 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.provider;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-class Accounts1 implements BaseColumns {
- public static final String TABLE_NAME = "accounts";
-
- public static final String _ID_type = "INTEGER PRIMARY KEY";
- public static final String NAME = "c_name";
- public static final String NAME_type = "TEXT";
-
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + DataProvider.AUTHORITY + "/accounts");
- public static final String CONTENT_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.account";
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.account";
- public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
-} \ No newline at end of file
+/*
+ * 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.provider;
+
+import android.provider.BaseColumns;
+
+public class KeyRings implements BaseColumns {
+ public static final String TABLE_NAME = "key_rings";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String MASTER_KEY_ID = "c_master_key_id";
+ public static final String MASTER_KEY_ID_type = "INT64";
+ public static final String TYPE = "c_type";
+ public static final String TYPE_type = "INTEGER";
+ public static final String WHO_ID = "c_who_id";
+ 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_type = "BLOB";
+}
diff --git a/src/org/thialfihar/android/apg/provider/Keys.java b/src/org/thialfihar/android/apg/provider/Keys.java
new file mode 100644
index 000000000..618c5e920
--- /dev/null
+++ b/src/org/thialfihar/android/apg/provider/Keys.java
@@ -0,0 +1,51 @@
+/*
+ * 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.provider;
+
+import android.provider.BaseColumns;
+
+public class Keys implements BaseColumns {
+ public static final String TABLE_NAME = "keys";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String KEY_ID = "c_key_id";
+ public static final String KEY_ID_type = "INT64";
+ public static final String TYPE = "c_type";
+ public static final String TYPE_type = "INTEGER";
+ public static final String IS_MASTER_KEY = "c_is_master_key";
+ public static final String IS_MASTER_KEY_type = "INTEGER";
+ public static final String ALGORITHM = "c_algorithm";
+ public static final String ALGORITHM_type = "INTEGER";
+ public static final String KEY_SIZE = "c_key_size";
+ public static final String KEY_SIZE_type = "INTEGER";
+ public static final String CAN_SIGN = "c_can_sign";
+ public static final String CAN_SIGN_type = "INTEGER";
+ public static final String CAN_ENCRYPT = "c_can_encrypt";
+ public static final String CAN_ENCRYPT_type = "INTEGER";
+ public static final String IS_REVOKED = "c_is_revoked";
+ public static final String IS_REVOKED_type = "INTEGER";
+ public static final String CREATION = "c_creation";
+ public static final String CREATION_type = "INTEGER";
+ public static final String EXPIRY = "c_expiry";
+ public static final String EXPIRY_type = "INTEGER";
+ public static final String KEY_RING_ID = "c_key_ring_id";
+ public static final String KEY_RING_ID_type = "INTEGER";
+ public static final String KEY_DATA = "c_key_data";
+ public static final String KEY_DATA_type = "BLOB";
+ public static final String RANK = "c_key_data";
+ public static final String RANK_type = "INTEGER";
+}
diff --git a/src/org/thialfihar/android/apg/provider/PublicKeys.java b/src/org/thialfihar/android/apg/provider/PublicKeys.java
deleted file mode 100644
index f15841fa5..000000000
--- a/src/org/thialfihar/android/apg/provider/PublicKeys.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.provider;
-
-public class PublicKeys extends PublicKeys1 {
- private PublicKeys() {
- }
-} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/provider/PublicKeys1.java b/src/org/thialfihar/android/apg/provider/PublicKeys1.java
deleted file mode 100644
index d12a67a17..000000000
--- a/src/org/thialfihar/android/apg/provider/PublicKeys1.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.provider;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-class PublicKeys1 implements BaseColumns {
- public static final String TABLE_NAME = "public_keys";
-
- public static final String _ID_type = "INTEGER PRIMARY KEY";
- public static final String KEY_ID = "c_key_id";
- public static final String KEY_ID_type = "INT64";
- public static final String KEY_DATA = "c_key_data";
- public static final String KEY_DATA_type = "BLOB";
- public static final String WHO_ID = "c_who_id";
- public static final String WHO_ID_type = "INTEGER";
-
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys");
- public static final Uri CONTENT_URI_BY_KEY_ID =
- Uri.parse("content://" + DataProvider.AUTHORITY + "/public_keys/key_id");
- public static final String CONTENT_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.public_key";
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.public_key";
- public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
-} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/provider/SecretKeys1.java b/src/org/thialfihar/android/apg/provider/SecretKeys1.java
deleted file mode 100644
index 3ca405f70..000000000
--- a/src/org/thialfihar/android/apg/provider/SecretKeys1.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.provider;
-
-import android.net.Uri;
-import android.provider.BaseColumns;
-
-class SecretKeys1 implements BaseColumns {
- public static final String TABLE_NAME = "secret_keys";
-
- public static final String _ID_type = "INTEGER PRIMARY KEY";
- public static final String KEY_ID = "c_key_id";
- public static final String KEY_ID_type = "INT64";
- public static final String KEY_DATA = "c_key_data";
- public static final String KEY_DATA_type = "BLOB";
- public static final String WHO_ID = "c_who_id";
- public static final String WHO_ID_type = "INTEGER";
-
- public static final Uri CONTENT_URI =
- Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys");
- public static final Uri CONTENT_URI_BY_KEY_ID =
- Uri.parse("content://" + DataProvider.AUTHORITY + "/secret_keys/key_id");
- public static final String CONTENT_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.secret_key";
- public static final String CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.secret_key";
- public static final String DEFAULT_SORT_ORDER = _ID + " DESC";
-} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/provider/SecretKeys.java b/src/org/thialfihar/android/apg/provider/UserIds.java
index d31f306ae..2b1162beb 100644
--- a/src/org/thialfihar/android/apg/provider/SecretKeys.java
+++ b/src/org/thialfihar/android/apg/provider/UserIds.java
@@ -16,7 +16,16 @@
package org.thialfihar.android.apg.provider;
-public class SecretKeys extends SecretKeys1 {
- private SecretKeys() {
- }
-} \ No newline at end of file
+import android.provider.BaseColumns;
+
+public class UserIds implements BaseColumns {
+ public static final String TABLE_NAME = "user_ids";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String KEY_ID = "c_key_id";
+ public static final String KEY_ID_type = "INTEGER";
+ public static final String USER_ID = "c_user_id";
+ public static final String USER_ID_type = "TEXT";
+ public static final String RANK = "c_rank";
+ public static final String RANK_type = "INTEGER";
+}
diff --git a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
index bc38fba4c..044da2db2 100644
--- a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
+++ b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
@@ -233,7 +233,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
public GregorianCalendar getExpiryDate() {
- return mExpiryDate;
+ return mExpiryDate;
}
public int getUsage() {
diff --git a/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/src/org/thialfihar/android/apg/ui/widget/SectionView.java
index cc1410c26..e5dcd4aca 100644
--- a/src/org/thialfihar/android/apg/ui/widget/SectionView.java
+++ b/src/org/thialfihar/android/apg/ui/widget/SectionView.java
@@ -76,7 +76,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
}
}
- String error = data.getString("error");
+ String error = data.getString(Apg.EXTRA_ERROR);
if (error != null) {
Toast.makeText(getContext(),
getContext().getString(R.string.errorMessage, error),
@@ -310,24 +310,24 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mNewKeySize, passPhrase,
masterKey);
} catch (NoSuchProviderException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (NoSuchAlgorithmException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (PGPException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (InvalidParameterException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (InvalidAlgorithmParameterException e) {
- error = e.getMessage();
+ error = "" + e;
} catch (Apg.GeneralException e) {
- error = e.getMessage();
+ error = "" + e;
}
Message message = new Message();
Bundle data = new Bundle();
data.putBoolean("closeProgressDialog", true);
if (error != null) {
- data.putString("error", error);
+ data.putString(Apg.EXTRA_ERROR, error);
} else {
data.putBoolean("gotNewKey", true);
}