aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml109
-rw-r--r--res/anim/push_left_in.xml20
-rw-r--r--res/anim/push_left_out.xml20
-rw-r--r--res/anim/push_right_in.xml20
-rw-r--r--res/anim/push_right_out.xml20
-rw-r--r--res/drawable-hdpi-finger-v4/btn_circle_disable.png (renamed from res/drawable-hdpi-finger/btn_circle_disable.png)bin2631 -> 2631 bytes
-rw-r--r--res/drawable-hdpi-finger-v4/btn_circle_disable_focused.png (renamed from res/drawable-hdpi-finger/btn_circle_disable_focused.png)bin3001 -> 3001 bytes
-rw-r--r--res/drawable-hdpi-finger-v4/btn_circle_normal.png (renamed from res/drawable-hdpi-finger/btn_circle_normal.png)bin1974 -> 1974 bytes
-rw-r--r--res/drawable-hdpi-finger-v4/btn_circle_pressed.png (renamed from res/drawable-hdpi-finger/btn_circle_pressed.png)bin2624 -> 2624 bytes
-rw-r--r--res/drawable-hdpi-finger-v4/btn_circle_selected.png (renamed from res/drawable-hdpi-finger/btn_circle_selected.png)bin2554 -> 2554 bytes
-rw-r--r--res/drawable-hdpi-finger-v4/ic_btn_round_minus.png (renamed from res/drawable-hdpi-finger/ic_btn_round_minus.png)bin536 -> 536 bytes
-rw-r--r--res/drawable-hdpi-finger-v4/ic_btn_round_plus.png (renamed from res/drawable-hdpi-finger/ic_btn_round_plus.png)bin1316 -> 1316 bytes
-rw-r--r--res/drawable-hdpi-v4/encrypted.png (renamed from res/drawable-hdpi/encrypted.png)bin3561 -> 3561 bytes
-rw-r--r--res/drawable-hdpi-v4/encrypted_large.png (renamed from res/drawable-hdpi/encrypted_large.png)bin5244 -> 5244 bytes
-rw-r--r--res/drawable-hdpi-v4/encrypted_small.png (renamed from res/drawable-hdpi/encrypted_small.png)bin2187 -> 2187 bytes
-rw-r--r--res/drawable-hdpi-v4/ic_next.pngbin0 -> 1722 bytes
-rw-r--r--res/drawable-hdpi-v4/ic_previous.pngbin0 -> 1712 bytes
-rw-r--r--res/drawable-hdpi-v4/icon.png (renamed from res/drawable-hdpi/icon.png)bin5563 -> 5563 bytes
-rw-r--r--res/drawable-hdpi-v4/key.png (renamed from res/drawable-hdpi/key.png)bin3675 -> 3675 bytes
-rw-r--r--res/drawable-hdpi-v4/key_large.png (renamed from res/drawable-hdpi/key_large.png)bin5550 -> 5550 bytes
-rw-r--r--res/drawable-hdpi-v4/key_small.png (renamed from res/drawable-hdpi/key_small.png)bin2088 -> 2088 bytes
-rw-r--r--res/drawable-hdpi-v4/overlay_error.png (renamed from res/drawable-hdpi/overlay_error.png)bin1986 -> 1986 bytes
-rw-r--r--res/drawable-hdpi-v4/overlay_ok.png (renamed from res/drawable-hdpi/overlay_ok.png)bin1702 -> 1702 bytes
-rw-r--r--res/drawable-hdpi-v4/signed.png (renamed from res/drawable-hdpi/signed.png)bin3858 -> 3858 bytes
-rw-r--r--res/drawable-hdpi-v4/signed_large.png (renamed from res/drawable-hdpi/signed_large.png)bin5928 -> 5928 bytes
-rw-r--r--res/drawable-hdpi-v4/signed_small.png (renamed from res/drawable-hdpi/signed_small.png)bin2219 -> 2219 bytes
-rw-r--r--res/drawable-ldpi-v4/encrypted.png (renamed from res/drawable-ldpi/encrypted.png)bin1513 -> 1513 bytes
-rw-r--r--res/drawable-ldpi-v4/encrypted_large.png (renamed from res/drawable-ldpi/encrypted_large.png)bin2486 -> 2486 bytes
-rw-r--r--res/drawable-ldpi-v4/encrypted_small.png (renamed from res/drawable-ldpi/encrypted_small.png)bin1176 -> 1176 bytes
-rw-r--r--res/drawable-ldpi-v4/ic_next.pngbin0 -> 916 bytes
-rw-r--r--res/drawable-ldpi-v4/ic_previous.pngbin0 -> 922 bytes
-rw-r--r--res/drawable-ldpi-v4/icon.png (renamed from res/drawable-ldpi/icon.png)bin1948 -> 1948 bytes
-rw-r--r--res/drawable-ldpi-v4/key.png (renamed from res/drawable-ldpi/key.png)bin1484 -> 1484 bytes
-rw-r--r--res/drawable-ldpi-v4/key_large.png (renamed from res/drawable-ldpi/key_large.png)bin2462 -> 2462 bytes
-rw-r--r--res/drawable-ldpi-v4/key_small.png (renamed from res/drawable-ldpi/key_small.png)bin1074 -> 1074 bytes
-rw-r--r--res/drawable-ldpi-v4/overlay_error.png (renamed from res/drawable-ldpi/overlay_error.png)bin1192 -> 1192 bytes
-rw-r--r--res/drawable-ldpi-v4/overlay_ok.png (renamed from res/drawable-ldpi/overlay_ok.png)bin1038 -> 1038 bytes
-rw-r--r--res/drawable-ldpi-v4/signed.png (renamed from res/drawable-ldpi/signed.png)bin1576 -> 1576 bytes
-rw-r--r--res/drawable-ldpi-v4/signed_large.png (renamed from res/drawable-ldpi/signed_large.png)bin2611 -> 2611 bytes
-rw-r--r--res/drawable-ldpi-v4/signed_small.png (renamed from res/drawable-ldpi/signed_small.png)bin1149 -> 1149 bytes
-rw-r--r--res/drawable-mdpi-finger-v4/btn_circle_disable.png (renamed from res/drawable-mdpi-finger/btn_circle_disable.png)bin938 -> 938 bytes
-rw-r--r--res/drawable-mdpi-finger-v4/btn_circle_disable_focused.png (renamed from res/drawable-mdpi-finger/btn_circle_disable_focused.png)bin1436 -> 1436 bytes
-rw-r--r--res/drawable-mdpi-finger-v4/btn_circle_normal.png (renamed from res/drawable-mdpi-finger/btn_circle_normal.png)bin1249 -> 1249 bytes
-rw-r--r--res/drawable-mdpi-finger-v4/btn_circle_pressed.png (renamed from res/drawable-mdpi-finger/btn_circle_pressed.png)bin1613 -> 1613 bytes
-rw-r--r--res/drawable-mdpi-finger-v4/btn_circle_selected.png (renamed from res/drawable-mdpi-finger/btn_circle_selected.png)bin1645 -> 1645 bytes
-rw-r--r--res/drawable-mdpi-finger-v4/ic_btn_round_minus.png (renamed from res/drawable-mdpi-finger/ic_btn_round_minus.png)bin288 -> 288 bytes
-rw-r--r--res/drawable-mdpi-finger-v4/ic_btn_round_plus.png (renamed from res/drawable-mdpi-finger/ic_btn_round_plus.png)bin526 -> 526 bytes
-rw-r--r--res/drawable-mdpi-v4/encrypted.png (renamed from res/drawable-mdpi/encrypted.png)bin2486 -> 2486 bytes
-rw-r--r--res/drawable-mdpi-v4/encrypted_large.png (renamed from res/drawable-mdpi/encrypted_large.png)bin3561 -> 3561 bytes
-rw-r--r--res/drawable-mdpi-v4/encrypted_small.png (renamed from res/drawable-mdpi/encrypted_small.png)bin1513 -> 1513 bytes
-rw-r--r--res/drawable-mdpi-v4/ic_next.pngbin0 -> 1360 bytes
-rw-r--r--res/drawable-mdpi-v4/ic_previous.pngbin0 -> 1352 bytes
-rw-r--r--res/drawable-mdpi-v4/icon.png (renamed from res/drawable-mdpi/icon.png)bin2947 -> 2947 bytes
-rw-r--r--res/drawable-mdpi-v4/key.png (renamed from res/drawable-mdpi/key.png)bin2462 -> 2462 bytes
-rw-r--r--res/drawable-mdpi-v4/key_large.png (renamed from res/drawable-mdpi/key_large.png)bin3675 -> 3675 bytes
-rw-r--r--res/drawable-mdpi-v4/key_small.png (renamed from res/drawable-mdpi/key_small.png)bin1484 -> 1484 bytes
-rw-r--r--res/drawable-mdpi-v4/overlay_error.png (renamed from res/drawable-mdpi/overlay_error.png)bin1539 -> 1539 bytes
-rw-r--r--res/drawable-mdpi-v4/overlay_ok.png (renamed from res/drawable-mdpi/overlay_ok.png)bin1305 -> 1305 bytes
-rw-r--r--res/drawable-mdpi-v4/signed.png (renamed from res/drawable-mdpi/signed.png)bin2611 -> 2611 bytes
-rw-r--r--res/drawable-mdpi-v4/signed_large.png (renamed from res/drawable-mdpi/signed_large.png)bin3858 -> 3858 bytes
-rw-r--r--res/drawable-mdpi-v4/signed_small.png (renamed from res/drawable-mdpi/signed_small.png)bin1576 -> 1576 bytes
-rw-r--r--res/drawable/btn_circle_disable.pngbin0 -> 938 bytes
-rw-r--r--res/drawable/btn_circle_disable_focused.pngbin0 -> 1436 bytes
-rw-r--r--res/drawable/btn_circle_normal.pngbin0 -> 1249 bytes
-rw-r--r--res/drawable/btn_circle_pressed.pngbin0 -> 1613 bytes
-rw-r--r--res/drawable/btn_circle_selected.pngbin0 -> 1645 bytes
-rw-r--r--res/drawable/encrypted.pngbin0 -> 2486 bytes
-rw-r--r--res/drawable/encrypted_large.pngbin0 -> 3561 bytes
-rw-r--r--res/drawable/encrypted_small.pngbin0 -> 1513 bytes
-rw-r--r--res/drawable/ic_btn_round_minus.pngbin0 -> 288 bytes
-rw-r--r--res/drawable/ic_btn_round_plus.pngbin0 -> 526 bytes
-rw-r--r--res/drawable/ic_launcher_folder.pngbin0 -> 2235 bytes
-rw-r--r--res/drawable/ic_launcher_folder_small.pngbin0 -> 1522 bytes
-rw-r--r--res/drawable/ic_next.pngbin0 -> 1360 bytes
-rw-r--r--res/drawable/ic_previous.pngbin0 -> 1352 bytes
-rw-r--r--res/drawable/icon.pngbin0 -> 2947 bytes
-rw-r--r--res/drawable/key.pngbin0 -> 2462 bytes
-rw-r--r--res/drawable/key_large.pngbin0 -> 3675 bytes
-rw-r--r--res/drawable/key_small.pngbin0 -> 1484 bytes
-rw-r--r--res/drawable/overlay_error.pngbin0 -> 1539 bytes
-rw-r--r--res/drawable/overlay_ok.pngbin0 -> 1305 bytes
-rw-r--r--res/drawable/signed.pngbin0 -> 2611 bytes
-rw-r--r--res/drawable/signed_large.pngbin0 -> 3858 bytes
-rw-r--r--res/drawable/signed_small.pngbin0 -> 1576 bytes
-rw-r--r--res/layout/account_item.xml14
-rw-r--r--res/layout/create_key.xml14
-rw-r--r--res/layout/decrypt.xml214
-rw-r--r--res/layout/decrypt_message.xml97
-rw-r--r--res/layout/edit_key.xml29
-rw-r--r--res/layout/edit_key_key_item.xml216
-rw-r--r--res/layout/edit_key_section.xml2
-rw-r--r--res/layout/edit_key_user_id_item.xml167
-rw-r--r--res/layout/encrypt.xml382
-rw-r--r--res/layout/encrypt_message.xml95
-rw-r--r--res/layout/file_dialog.xml37
-rw-r--r--res/layout/info.xml15
-rw-r--r--res/layout/key_list.xml28
-rw-r--r--res/layout/key_list_child_item_master_key.xml88
-rw-r--r--res/layout/key_list_child_item_sub_key.xml76
-rw-r--r--res/layout/key_list_child_item_user_id.xml14
-rw-r--r--res/layout/key_list_group_item.xml55
-rw-r--r--res/layout/mailbox_message_item.xml58
-rw-r--r--res/layout/main.xml92
-rw-r--r--res/layout/pass_phrase.xml22
-rw-r--r--res/layout/preferences.xml197
-rw-r--r--res/layout/select_public_key.xml57
-rw-r--r--res/layout/select_public_key_item.xml136
-rw-r--r--res/layout/select_secret_key.xml9
-rw-r--r--res/layout/select_secret_key_item.xml111
-rw-r--r--res/values-de/strings.xml230
-rw-r--r--res/values-ko/strings.xml230
-rw-r--r--res/values-ru/strings.xml230
-rw-r--r--res/values/strings.xml205
-rw-r--r--src/org/openintents/intents/FileManager.java78
-rw-r--r--src/org/thialfihar/android/apg/Apg.java930
-rw-r--r--src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java98
-rw-r--r--src/org/thialfihar/android/apg/BaseActivity.java467
-rw-r--r--src/org/thialfihar/android/apg/CachedPassPhrase.java39
-rw-r--r--src/org/thialfihar/android/apg/Constants.java35
-rw-r--r--src/org/thialfihar/android/apg/DecryptActivity.java647
-rw-r--r--src/org/thialfihar/android/apg/DecryptMessageActivity.java343
-rw-r--r--src/org/thialfihar/android/apg/EditKeyActivity.java255
-rw-r--r--src/org/thialfihar/android/apg/EncryptActivity.java810
-rw-r--r--src/org/thialfihar/android/apg/EncryptMessageActivity.java428
-rw-r--r--src/org/thialfihar/android/apg/FileDialog.java119
-rw-r--r--src/org/thialfihar/android/apg/Id.java129
-rw-r--r--src/org/thialfihar/android/apg/MailListActivity.java42
-rw-r--r--src/org/thialfihar/android/apg/MainActivity.java295
-rw-r--r--src/org/thialfihar/android/apg/PositionAwareInputStream.java67
-rw-r--r--src/org/thialfihar/android/apg/PreferencesActivity.java222
-rw-r--r--src/org/thialfihar/android/apg/Primes.java185
-rw-r--r--src/org/thialfihar/android/apg/ProgressDialogUpdater.java1
-rw-r--r--src/org/thialfihar/android/apg/PublicKeyListActivity.java459
-rw-r--r--src/org/thialfihar/android/apg/SecretKeyListActivity.java535
-rw-r--r--src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java168
-rw-r--r--src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java186
-rw-r--r--src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java29
-rw-r--r--src/org/thialfihar/android/apg/provider/DataProvider.java9
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/KeyEditor.java94
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/SectionView.java76
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java7
141 files changed, 7001 insertions, 3061 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5a41f262d..03a653c66 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -14,75 +14,104 @@
limitations under the License.
-->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.thialfihar.android.apg"
- android:versionCode="3" android:versionName="0.8.0">
- <application android:icon="@drawable/icon" android:label="@string/app_name">
- <activity android:name=".MainActivity"
- android:label="@string/app_name"
- android:configChanges="keyboardHidden|orientation|keyboard">
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.thialfihar.android.apg"
+ android:versionName="0.9.5" android:versionCode="10">
+
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name">
+
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name"
+ android:configChanges="keyboardHidden|orientation|keyboard">
+
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
- </activity>
- <activity android:name=".PublicKeyListActivity"
- android:label="@string/title_managePublicKeys"
- android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
- <activity android:name=".SecretKeyListActivity"
- android:label="@string/title_manageSecretKeys"
- android:configChanges="keyboardHidden|orientation|keyboard">
- </activity>
+ <activity
+ android:name=".PublicKeyListActivity"
+ android:label="@string/title_managePublicKeys"
+ android:configChanges="keyboardHidden|orientation|keyboard" />
- <activity android:name=".EditKeyActivity"
- android:label="@string/title_editKey"
- android:configChanges="keyboardHidden|orientation|keyboard">
- </activity>
+ <activity
+ android:name=".SecretKeyListActivity"
+ android:label="@string/title_manageSecretKeys"
+ android:configChanges="keyboardHidden|orientation|keyboard"/>
- <activity android:name=".SelectPublicKeyListActivity"
- android:label="@string/title_selectRecipients"
- android:configChanges="keyboardHidden|orientation|keyboard">
- </activity>
+ <activity
+ android:name=".EditKeyActivity"
+ android:label="@string/title_editKey"
+ android:configChanges="keyboardHidden|orientation|keyboard"/>
- <activity android:name=".SelectSecretKeyListActivity"
- android:label="@string/title_selectSignature"
- android:configChanges="keyboardHidden|orientation|keyboard">
- </activity>
+ <activity
+ android:name=".SelectPublicKeyListActivity"
+ android:label="@string/title_selectRecipients"
+ android:configChanges="keyboardHidden|orientation|keyboard"/>
+
+ <activity
+ android:name=".SelectSecretKeyListActivity"
+ android:label="@string/title_selectSignature"
+ android:configChanges="keyboardHidden|orientation|keyboard"/>
+
+ <activity
+ android:name=".EncryptActivity"
+ android:label="@string/title_encrypt"
+ android:configChanges="keyboardHidden|orientation|keyboard">
- <activity android:name=".EncryptMessageActivity"
- android:label="@string/title_encryptMessage"
- android:configChanges="keyboardHidden|orientation|keyboard">
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.ENCRYPT" />
+ <action android:name="org.thialfihar.android.apg.intent.ENCRYPT_FILE" />
</intent-filter>
+
</activity>
- <activity android:name=".DecryptMessageActivity"
- android:label="@string/title_decryptMessage"
- android:configChanges="keyboardHidden|orientation|keyboard">
+ <activity
+ android:name=".DecryptActivity"
+ android:label="@string/title_decrypt"
+ android:configChanges="keyboardHidden|orientation|keyboard">
+
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/*"/>
</intent-filter>
+
+ <intent-filter>
+ <action android:name="android.intent.action.SEND" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="text/*"/>
+ </intent-filter>
+
<intent-filter>
<action android:name="org.thialfihar.android.apg.intent.DECRYPT" />
+ <action android:name="org.thialfihar.android.apg.intent.DECRYPT_FILE" />
</intent-filter>
- </activity>
- <activity android:name=".MailListActivity"
- android:label="@string/title_mailInbox"
- android:configChanges="keyboardHidden|orientation|keyboard">
</activity>
- <provider android:name="org.thialfihar.android.apg.provider.DataProvider"
- android:authorities="org.thialfihar.android.apg.provider" />
+ <activity
+ android:name=".MailListActivity"
+ android:label="@string/title_mailInbox"
+ android:configChanges="keyboardHidden|orientation|keyboard"/>
+
+ <activity
+ android:name=".PreferencesActivity"
+ android:label="@string/title_preferences"
+ android:configChanges="keyboardHidden|orientation|keyboard"/>
+
+ <provider
+ android:name="org.thialfihar.android.apg.provider.DataProvider"
+ android:authorities="org.thialfihar.android.apg.provider" />
</application>
- <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="5" />
+ <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>
diff --git a/res/anim/push_left_in.xml b/res/anim/push_left_in.xml
new file mode 100644
index 000000000..b7d2d0da0
--- /dev/null
+++ b/res/anim/push_left_in.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="100%p" android:toXDelta="0" android:duration="500"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
+</set>
diff --git a/res/anim/push_left_out.xml b/res/anim/push_left_out.xml
new file mode 100644
index 000000000..6bdc0ce47
--- /dev/null
+++ b/res/anim/push_left_out.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="0" android:toXDelta="-100%p" android:duration="500"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
+</set> \ No newline at end of file
diff --git a/res/anim/push_right_in.xml b/res/anim/push_right_in.xml
new file mode 100644
index 000000000..89daae972
--- /dev/null
+++ b/res/anim/push_right_in.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="-100%p" android:toXDelta="0" android:duration="500"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
+</set>
diff --git a/res/anim/push_right_out.xml b/res/anim/push_right_out.xml
new file mode 100644
index 000000000..b4b796b52
--- /dev/null
+++ b/res/anim/push_right_out.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+ 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <translate android:fromXDelta="0" android:toXDelta="100%p" android:duration="500"/>
+ <alpha android:fromAlpha="1.0" android:toAlpha="1.0" android:duration="500" />
+</set> \ No newline at end of file
diff --git a/res/drawable-hdpi-finger/btn_circle_disable.png b/res/drawable-hdpi-finger-v4/btn_circle_disable.png
index ae063b545..ae063b545 100644
--- a/res/drawable-hdpi-finger/btn_circle_disable.png
+++ b/res/drawable-hdpi-finger-v4/btn_circle_disable.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_disable_focused.png b/res/drawable-hdpi-finger-v4/btn_circle_disable_focused.png
index 7a5d4fe4e..7a5d4fe4e 100644
--- a/res/drawable-hdpi-finger/btn_circle_disable_focused.png
+++ b/res/drawable-hdpi-finger-v4/btn_circle_disable_focused.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_normal.png b/res/drawable-hdpi-finger-v4/btn_circle_normal.png
index 5eda66883..5eda66883 100644
--- a/res/drawable-hdpi-finger/btn_circle_normal.png
+++ b/res/drawable-hdpi-finger-v4/btn_circle_normal.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_pressed.png b/res/drawable-hdpi-finger-v4/btn_circle_pressed.png
index 88848bac2..88848bac2 100644
--- a/res/drawable-hdpi-finger/btn_circle_pressed.png
+++ b/res/drawable-hdpi-finger-v4/btn_circle_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/btn_circle_selected.png b/res/drawable-hdpi-finger-v4/btn_circle_selected.png
index 74690705f..74690705f 100644
--- a/res/drawable-hdpi-finger/btn_circle_selected.png
+++ b/res/drawable-hdpi-finger-v4/btn_circle_selected.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_minus.png b/res/drawable-hdpi-finger-v4/ic_btn_round_minus.png
index 27af3faf4..27af3faf4 100644
--- a/res/drawable-hdpi-finger/ic_btn_round_minus.png
+++ b/res/drawable-hdpi-finger-v4/ic_btn_round_minus.png
Binary files differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_plus.png b/res/drawable-hdpi-finger-v4/ic_btn_round_plus.png
index b24168c32..b24168c32 100644
--- a/res/drawable-hdpi-finger/ic_btn_round_plus.png
+++ b/res/drawable-hdpi-finger-v4/ic_btn_round_plus.png
Binary files differ
diff --git a/res/drawable-hdpi/encrypted.png b/res/drawable-hdpi-v4/encrypted.png
index 6d7c616a4..6d7c616a4 100644
--- a/res/drawable-hdpi/encrypted.png
+++ b/res/drawable-hdpi-v4/encrypted.png
Binary files differ
diff --git a/res/drawable-hdpi/encrypted_large.png b/res/drawable-hdpi-v4/encrypted_large.png
index dc7466e45..dc7466e45 100644
--- a/res/drawable-hdpi/encrypted_large.png
+++ b/res/drawable-hdpi-v4/encrypted_large.png
Binary files differ
diff --git a/res/drawable-hdpi/encrypted_small.png b/res/drawable-hdpi-v4/encrypted_small.png
index 5ed9fe4b8..5ed9fe4b8 100644
--- a/res/drawable-hdpi/encrypted_small.png
+++ b/res/drawable-hdpi-v4/encrypted_small.png
Binary files differ
diff --git a/res/drawable-hdpi-v4/ic_next.png b/res/drawable-hdpi-v4/ic_next.png
new file mode 100644
index 000000000..d71058055
--- /dev/null
+++ b/res/drawable-hdpi-v4/ic_next.png
Binary files differ
diff --git a/res/drawable-hdpi-v4/ic_previous.png b/res/drawable-hdpi-v4/ic_previous.png
new file mode 100644
index 000000000..d610e4667
--- /dev/null
+++ b/res/drawable-hdpi-v4/ic_previous.png
Binary files differ
diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi-v4/icon.png
index 9e2e7c0e4..9e2e7c0e4 100644
--- a/res/drawable-hdpi/icon.png
+++ b/res/drawable-hdpi-v4/icon.png
Binary files differ
diff --git a/res/drawable-hdpi/key.png b/res/drawable-hdpi-v4/key.png
index 6f18c0240..6f18c0240 100644
--- a/res/drawable-hdpi/key.png
+++ b/res/drawable-hdpi-v4/key.png
Binary files differ
diff --git a/res/drawable-hdpi/key_large.png b/res/drawable-hdpi-v4/key_large.png
index 81816835d..81816835d 100644
--- a/res/drawable-hdpi/key_large.png
+++ b/res/drawable-hdpi-v4/key_large.png
Binary files differ
diff --git a/res/drawable-hdpi/key_small.png b/res/drawable-hdpi-v4/key_small.png
index 3f42a0d9b..3f42a0d9b 100644
--- a/res/drawable-hdpi/key_small.png
+++ b/res/drawable-hdpi-v4/key_small.png
Binary files differ
diff --git a/res/drawable-hdpi/overlay_error.png b/res/drawable-hdpi-v4/overlay_error.png
index db6a08329..db6a08329 100644
--- a/res/drawable-hdpi/overlay_error.png
+++ b/res/drawable-hdpi-v4/overlay_error.png
Binary files differ
diff --git a/res/drawable-hdpi/overlay_ok.png b/res/drawable-hdpi-v4/overlay_ok.png
index 33dc08094..33dc08094 100644
--- a/res/drawable-hdpi/overlay_ok.png
+++ b/res/drawable-hdpi-v4/overlay_ok.png
Binary files differ
diff --git a/res/drawable-hdpi/signed.png b/res/drawable-hdpi-v4/signed.png
index 92e64dc51..92e64dc51 100644
--- a/res/drawable-hdpi/signed.png
+++ b/res/drawable-hdpi-v4/signed.png
Binary files differ
diff --git a/res/drawable-hdpi/signed_large.png b/res/drawable-hdpi-v4/signed_large.png
index 53d8ac991..53d8ac991 100644
--- a/res/drawable-hdpi/signed_large.png
+++ b/res/drawable-hdpi-v4/signed_large.png
Binary files differ
diff --git a/res/drawable-hdpi/signed_small.png b/res/drawable-hdpi-v4/signed_small.png
index d7f147f05..d7f147f05 100644
--- a/res/drawable-hdpi/signed_small.png
+++ b/res/drawable-hdpi-v4/signed_small.png
Binary files differ
diff --git a/res/drawable-ldpi/encrypted.png b/res/drawable-ldpi-v4/encrypted.png
index 7f4ab803f..7f4ab803f 100644
--- a/res/drawable-ldpi/encrypted.png
+++ b/res/drawable-ldpi-v4/encrypted.png
Binary files differ
diff --git a/res/drawable-ldpi/encrypted_large.png b/res/drawable-ldpi-v4/encrypted_large.png
index 2783804bc..2783804bc 100644
--- a/res/drawable-ldpi/encrypted_large.png
+++ b/res/drawable-ldpi-v4/encrypted_large.png
Binary files differ
diff --git a/res/drawable-ldpi/encrypted_small.png b/res/drawable-ldpi-v4/encrypted_small.png
index 0ffedf2dd..0ffedf2dd 100644
--- a/res/drawable-ldpi/encrypted_small.png
+++ b/res/drawable-ldpi-v4/encrypted_small.png
Binary files differ
diff --git a/res/drawable-ldpi-v4/ic_next.png b/res/drawable-ldpi-v4/ic_next.png
new file mode 100644
index 000000000..474ed8faa
--- /dev/null
+++ b/res/drawable-ldpi-v4/ic_next.png
Binary files differ
diff --git a/res/drawable-ldpi-v4/ic_previous.png b/res/drawable-ldpi-v4/ic_previous.png
new file mode 100644
index 000000000..6fd885e6b
--- /dev/null
+++ b/res/drawable-ldpi-v4/ic_previous.png
Binary files differ
diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi-v4/icon.png
index 9d44341f1..9d44341f1 100644
--- a/res/drawable-ldpi/icon.png
+++ b/res/drawable-ldpi-v4/icon.png
Binary files differ
diff --git a/res/drawable-ldpi/key.png b/res/drawable-ldpi-v4/key.png
index 121803508..121803508 100644
--- a/res/drawable-ldpi/key.png
+++ b/res/drawable-ldpi-v4/key.png
Binary files differ
diff --git a/res/drawable-ldpi/key_large.png b/res/drawable-ldpi-v4/key_large.png
index de7e72524..de7e72524 100644
--- a/res/drawable-ldpi/key_large.png
+++ b/res/drawable-ldpi-v4/key_large.png
Binary files differ
diff --git a/res/drawable-ldpi/key_small.png b/res/drawable-ldpi-v4/key_small.png
index 1763c4256..1763c4256 100644
--- a/res/drawable-ldpi/key_small.png
+++ b/res/drawable-ldpi-v4/key_small.png
Binary files differ
diff --git a/res/drawable-ldpi/overlay_error.png b/res/drawable-ldpi-v4/overlay_error.png
index 568f2b1ee..568f2b1ee 100644
--- a/res/drawable-ldpi/overlay_error.png
+++ b/res/drawable-ldpi-v4/overlay_error.png
Binary files differ
diff --git a/res/drawable-ldpi/overlay_ok.png b/res/drawable-ldpi-v4/overlay_ok.png
index db415a846..db415a846 100644
--- a/res/drawable-ldpi/overlay_ok.png
+++ b/res/drawable-ldpi-v4/overlay_ok.png
Binary files differ
diff --git a/res/drawable-ldpi/signed.png b/res/drawable-ldpi-v4/signed.png
index 590220281..590220281 100644
--- a/res/drawable-ldpi/signed.png
+++ b/res/drawable-ldpi-v4/signed.png
Binary files differ
diff --git a/res/drawable-ldpi/signed_large.png b/res/drawable-ldpi-v4/signed_large.png
index 490e94fbd..490e94fbd 100644
--- a/res/drawable-ldpi/signed_large.png
+++ b/res/drawable-ldpi-v4/signed_large.png
Binary files differ
diff --git a/res/drawable-ldpi/signed_small.png b/res/drawable-ldpi-v4/signed_small.png
index ca33fc1f7..ca33fc1f7 100644
--- a/res/drawable-ldpi/signed_small.png
+++ b/res/drawable-ldpi-v4/signed_small.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_disable.png b/res/drawable-mdpi-finger-v4/btn_circle_disable.png
index 33b74a66c..33b74a66c 100644
--- a/res/drawable-mdpi-finger/btn_circle_disable.png
+++ b/res/drawable-mdpi-finger-v4/btn_circle_disable.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_disable_focused.png b/res/drawable-mdpi-finger-v4/btn_circle_disable_focused.png
index 005ad8dca..005ad8dca 100644
--- a/res/drawable-mdpi-finger/btn_circle_disable_focused.png
+++ b/res/drawable-mdpi-finger-v4/btn_circle_disable_focused.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_normal.png b/res/drawable-mdpi-finger-v4/btn_circle_normal.png
index fc5af1c9f..fc5af1c9f 100644
--- a/res/drawable-mdpi-finger/btn_circle_normal.png
+++ b/res/drawable-mdpi-finger-v4/btn_circle_normal.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_pressed.png b/res/drawable-mdpi-finger-v4/btn_circle_pressed.png
index 8f40afdfc..8f40afdfc 100644
--- a/res/drawable-mdpi-finger/btn_circle_pressed.png
+++ b/res/drawable-mdpi-finger-v4/btn_circle_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/btn_circle_selected.png b/res/drawable-mdpi-finger-v4/btn_circle_selected.png
index c74fac227..c74fac227 100644
--- a/res/drawable-mdpi-finger/btn_circle_selected.png
+++ b/res/drawable-mdpi-finger-v4/btn_circle_selected.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_minus.png b/res/drawable-mdpi-finger-v4/ic_btn_round_minus.png
index 96dbb17d2..96dbb17d2 100644
--- a/res/drawable-mdpi-finger/ic_btn_round_minus.png
+++ b/res/drawable-mdpi-finger-v4/ic_btn_round_minus.png
Binary files differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_plus.png b/res/drawable-mdpi-finger-v4/ic_btn_round_plus.png
index 1ec8a956a..1ec8a956a 100644
--- a/res/drawable-mdpi-finger/ic_btn_round_plus.png
+++ b/res/drawable-mdpi-finger-v4/ic_btn_round_plus.png
Binary files differ
diff --git a/res/drawable-mdpi/encrypted.png b/res/drawable-mdpi-v4/encrypted.png
index 2783804bc..2783804bc 100644
--- a/res/drawable-mdpi/encrypted.png
+++ b/res/drawable-mdpi-v4/encrypted.png
Binary files differ
diff --git a/res/drawable-mdpi/encrypted_large.png b/res/drawable-mdpi-v4/encrypted_large.png
index 6d7c616a4..6d7c616a4 100644
--- a/res/drawable-mdpi/encrypted_large.png
+++ b/res/drawable-mdpi-v4/encrypted_large.png
Binary files differ
diff --git a/res/drawable-mdpi/encrypted_small.png b/res/drawable-mdpi-v4/encrypted_small.png
index 7f4ab803f..7f4ab803f 100644
--- a/res/drawable-mdpi/encrypted_small.png
+++ b/res/drawable-mdpi-v4/encrypted_small.png
Binary files differ
diff --git a/res/drawable-mdpi-v4/ic_next.png b/res/drawable-mdpi-v4/ic_next.png
new file mode 100644
index 000000000..8271c1380
--- /dev/null
+++ b/res/drawable-mdpi-v4/ic_next.png
Binary files differ
diff --git a/res/drawable-mdpi-v4/ic_previous.png b/res/drawable-mdpi-v4/ic_previous.png
new file mode 100644
index 000000000..ef90db972
--- /dev/null
+++ b/res/drawable-mdpi-v4/ic_previous.png
Binary files differ
diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi-v4/icon.png
index b4e4db40e..b4e4db40e 100644
--- a/res/drawable-mdpi/icon.png
+++ b/res/drawable-mdpi-v4/icon.png
Binary files differ
diff --git a/res/drawable-mdpi/key.png b/res/drawable-mdpi-v4/key.png
index de7e72524..de7e72524 100644
--- a/res/drawable-mdpi/key.png
+++ b/res/drawable-mdpi-v4/key.png
Binary files differ
diff --git a/res/drawable-mdpi/key_large.png b/res/drawable-mdpi-v4/key_large.png
index 6f18c0240..6f18c0240 100644
--- a/res/drawable-mdpi/key_large.png
+++ b/res/drawable-mdpi-v4/key_large.png
Binary files differ
diff --git a/res/drawable-mdpi/key_small.png b/res/drawable-mdpi-v4/key_small.png
index 121803508..121803508 100644
--- a/res/drawable-mdpi/key_small.png
+++ b/res/drawable-mdpi-v4/key_small.png
Binary files differ
diff --git a/res/drawable-mdpi/overlay_error.png b/res/drawable-mdpi-v4/overlay_error.png
index 2372de59e..2372de59e 100644
--- a/res/drawable-mdpi/overlay_error.png
+++ b/res/drawable-mdpi-v4/overlay_error.png
Binary files differ
diff --git a/res/drawable-mdpi/overlay_ok.png b/res/drawable-mdpi-v4/overlay_ok.png
index 2f0005898..2f0005898 100644
--- a/res/drawable-mdpi/overlay_ok.png
+++ b/res/drawable-mdpi-v4/overlay_ok.png
Binary files differ
diff --git a/res/drawable-mdpi/signed.png b/res/drawable-mdpi-v4/signed.png
index 490e94fbd..490e94fbd 100644
--- a/res/drawable-mdpi/signed.png
+++ b/res/drawable-mdpi-v4/signed.png
Binary files differ
diff --git a/res/drawable-mdpi/signed_large.png b/res/drawable-mdpi-v4/signed_large.png
index 92e64dc51..92e64dc51 100644
--- a/res/drawable-mdpi/signed_large.png
+++ b/res/drawable-mdpi-v4/signed_large.png
Binary files differ
diff --git a/res/drawable-mdpi/signed_small.png b/res/drawable-mdpi-v4/signed_small.png
index 590220281..590220281 100644
--- a/res/drawable-mdpi/signed_small.png
+++ b/res/drawable-mdpi-v4/signed_small.png
Binary files differ
diff --git a/res/drawable/btn_circle_disable.png b/res/drawable/btn_circle_disable.png
new file mode 100644
index 000000000..33b74a66c
--- /dev/null
+++ b/res/drawable/btn_circle_disable.png
Binary files differ
diff --git a/res/drawable/btn_circle_disable_focused.png b/res/drawable/btn_circle_disable_focused.png
new file mode 100644
index 000000000..005ad8dca
--- /dev/null
+++ b/res/drawable/btn_circle_disable_focused.png
Binary files differ
diff --git a/res/drawable/btn_circle_normal.png b/res/drawable/btn_circle_normal.png
new file mode 100644
index 000000000..fc5af1c9f
--- /dev/null
+++ b/res/drawable/btn_circle_normal.png
Binary files differ
diff --git a/res/drawable/btn_circle_pressed.png b/res/drawable/btn_circle_pressed.png
new file mode 100644
index 000000000..8f40afdfc
--- /dev/null
+++ b/res/drawable/btn_circle_pressed.png
Binary files differ
diff --git a/res/drawable/btn_circle_selected.png b/res/drawable/btn_circle_selected.png
new file mode 100644
index 000000000..c74fac227
--- /dev/null
+++ b/res/drawable/btn_circle_selected.png
Binary files differ
diff --git a/res/drawable/encrypted.png b/res/drawable/encrypted.png
new file mode 100644
index 000000000..2783804bc
--- /dev/null
+++ b/res/drawable/encrypted.png
Binary files differ
diff --git a/res/drawable/encrypted_large.png b/res/drawable/encrypted_large.png
new file mode 100644
index 000000000..6d7c616a4
--- /dev/null
+++ b/res/drawable/encrypted_large.png
Binary files differ
diff --git a/res/drawable/encrypted_small.png b/res/drawable/encrypted_small.png
new file mode 100644
index 000000000..7f4ab803f
--- /dev/null
+++ b/res/drawable/encrypted_small.png
Binary files differ
diff --git a/res/drawable/ic_btn_round_minus.png b/res/drawable/ic_btn_round_minus.png
new file mode 100644
index 000000000..96dbb17d2
--- /dev/null
+++ b/res/drawable/ic_btn_round_minus.png
Binary files differ
diff --git a/res/drawable/ic_btn_round_plus.png b/res/drawable/ic_btn_round_plus.png
new file mode 100644
index 000000000..1ec8a956a
--- /dev/null
+++ b/res/drawable/ic_btn_round_plus.png
Binary files differ
diff --git a/res/drawable/ic_launcher_folder.png b/res/drawable/ic_launcher_folder.png
new file mode 100644
index 000000000..ed31ba580
--- /dev/null
+++ b/res/drawable/ic_launcher_folder.png
Binary files differ
diff --git a/res/drawable/ic_launcher_folder_small.png b/res/drawable/ic_launcher_folder_small.png
new file mode 100644
index 000000000..5df8d60f0
--- /dev/null
+++ b/res/drawable/ic_launcher_folder_small.png
Binary files differ
diff --git a/res/drawable/ic_next.png b/res/drawable/ic_next.png
new file mode 100644
index 000000000..8271c1380
--- /dev/null
+++ b/res/drawable/ic_next.png
Binary files differ
diff --git a/res/drawable/ic_previous.png b/res/drawable/ic_previous.png
new file mode 100644
index 000000000..ef90db972
--- /dev/null
+++ b/res/drawable/ic_previous.png
Binary files differ
diff --git a/res/drawable/icon.png b/res/drawable/icon.png
new file mode 100644
index 000000000..b4e4db40e
--- /dev/null
+++ b/res/drawable/icon.png
Binary files differ
diff --git a/res/drawable/key.png b/res/drawable/key.png
new file mode 100644
index 000000000..de7e72524
--- /dev/null
+++ b/res/drawable/key.png
Binary files differ
diff --git a/res/drawable/key_large.png b/res/drawable/key_large.png
new file mode 100644
index 000000000..6f18c0240
--- /dev/null
+++ b/res/drawable/key_large.png
Binary files differ
diff --git a/res/drawable/key_small.png b/res/drawable/key_small.png
new file mode 100644
index 000000000..121803508
--- /dev/null
+++ b/res/drawable/key_small.png
Binary files differ
diff --git a/res/drawable/overlay_error.png b/res/drawable/overlay_error.png
new file mode 100644
index 000000000..2372de59e
--- /dev/null
+++ b/res/drawable/overlay_error.png
Binary files differ
diff --git a/res/drawable/overlay_ok.png b/res/drawable/overlay_ok.png
new file mode 100644
index 000000000..2f0005898
--- /dev/null
+++ b/res/drawable/overlay_ok.png
Binary files differ
diff --git a/res/drawable/signed.png b/res/drawable/signed.png
new file mode 100644
index 000000000..490e94fbd
--- /dev/null
+++ b/res/drawable/signed.png
Binary files differ
diff --git a/res/drawable/signed_large.png b/res/drawable/signed_large.png
new file mode 100644
index 000000000..92e64dc51
--- /dev/null
+++ b/res/drawable/signed_large.png
Binary files differ
diff --git a/res/drawable/signed_small.png b/res/drawable/signed_small.png
new file mode 100644
index 000000000..590220281
--- /dev/null
+++ b/res/drawable/signed_small.png
Binary files differ
diff --git a/res/layout/account_item.xml b/res/layout/account_item.xml
index e37000ff0..0aa76719a 100644
--- a/res/layout/account_item.xml
+++ b/res/layout/account_item.xml
@@ -24,12 +24,12 @@
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight">
-<TextView
- android:id="@+id/account_name"
- android:text="someone@gmail.com"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"/>
+ <TextView
+ android:id="@+id/accountName"
+ android:text="someone@gmail.com"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
</LinearLayout>
diff --git a/res/layout/create_key.xml b/res/layout/create_key.xml
index 569b703f5..ab327ffeb 100644
--- a/res/layout/create_key.xml
+++ b/res/layout/create_key.xml
@@ -18,35 +18,47 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
+
<TableLayout
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:stretchColumns="1"
android:layout_marginRight="?android:attr/scrollbarSize"
android:paddingLeft="6dip">
+
<TableRow>
+
<TextView android:id="@+id/label_algorithm"
android:text="Algorithm"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
+
<Spinner
android:id="@+id/algorithm"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
+
</TableRow>
+
<TableRow>
+
<TextView android:id="@+id/label_size"
android:text="Key Size"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"/>
+
<EditText android:id="@+id/size"
android:text="1024"
android:layout_height="wrap_content"
- android:layout_width="fill_parent" android:gravity="right" android:numeric="integer"/>
+ android:layout_width="fill_parent"
+ android:gravity="right"
+ android:numeric="integer"/>
</TableRow>
+
</TableLayout>
+
</ScrollView>
diff --git a/res/layout/decrypt.xml b/res/layout/decrypt.xml
new file mode 100644
index 000000000..03e2f6311
--- /dev/null
+++ b/res/layout/decrypt.xml
@@ -0,0 +1,214 @@
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:fillViewport="true">
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:layout_marginLeft="5dip">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/sourcePrevious"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous"/>
+
+ <TextView
+ android:id="@+id/sourceLabel"
+ android:layout_width="0dip"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:text="@string/label_message"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_horizontal|center_vertical"
+ android:textColor="#ffffffff"/>
+
+ <ImageView
+ android:id="@+id/sourceNext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next"/>
+
+ </LinearLayout>
+
+ <ViewFlipper
+ android:id="@+id/source"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:id="@+id/sourceMessage"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <EditText
+ android:id="@+id/message"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="top"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sourceFile"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <EditText
+ android:id="@+id/filename"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/btn_browse"
+ android:src="@drawable/ic_launcher_folder_small"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_deleteAfterDecryption"
+ android:text="@string/label_deleteAfterDecryption"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"/>
+
+ <CheckBox
+ android:id="@+id/deleteAfterDecryption"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </ViewFlipper>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ <LinearLayout
+ android:id="@+id/signature"
+ android:orientation="horizontal"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent">
+
+ <RelativeLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content">
+
+ <ImageView
+ android:id="@+id/ic_signature"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/signed_large"/>
+
+ <ImageView
+ android:id="@+id/ic_signature_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/overlay_error"/>
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingLeft="5dip">
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:text="Main User Id"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"/>
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:text="Main User Id Rest"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/ButtonBar">
+
+ <Button
+ android:id="@+id/btn_reply"
+ android:text="@string/btn_reply"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:id="@+id/btn_decrypt"
+ android:text="@string/btn_decrypt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/decrypt_message.xml b/res/layout/decrypt_message.xml
deleted file mode 100644
index 2a0aa153d..000000000
--- a/res/layout/decrypt_message.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:fillViewport="true">
-
-<EditText
- android:id="@+id/message"
- android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:gravity="top"/>
-
-<LinearLayout
- android:orientation="horizontal"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:paddingLeft="5dip"
- android:paddingRight="5dip">
-
-<LinearLayout
- android:id="@+id/layout_signature"
- android:orientation="horizontal"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_weight="2">
-
-<RelativeLayout
- android:layout_height="wrap_content"
- android:layout_width="wrap_content">
-<ImageView
- android:id="@+id/ic_signature"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/signed_large"/>
-
-<ImageView
- android:id="@+id/ic_signature_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/overlay_error"/>
-
-</RelativeLayout>
-
-<LinearLayout
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingLeft="5dip">
-
-<TextView
- android:id="@+id/main_user_id"
- android:text="Main User Id"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"/>
-
-<TextView
- android:id="@+id/main_user_id_rest"
- android:text="Main User Id Rest"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:layout_gravity="left"/>
-
-</LinearLayout>
-</LinearLayout>
-
-<Button
- android:id="@+id/btn_decrypt"
- android:text="@string/btn_decrypt"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
-</LinearLayout>
-
-</LinearLayout>
-
diff --git a/res/layout/edit_key.xml b/res/layout/edit_key.xml
index 2fceeb5a3..bf994fa44 100644
--- a/res/layout/edit_key.xml
+++ b/res/layout/edit_key.xml
@@ -22,19 +22,21 @@
android:paddingTop="5dip"
android:fillViewport="true">
-<ScrollView
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-<LinearLayout
- android:id="@+id/container"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:layout_marginRight="?android:attr/scrollbarSize"/>
-</ScrollView>
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
-<LinearLayout
+ <LinearLayout
+ android:id="@+id/container"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:layout_marginRight="?android:attr/scrollbarSize"/>
+
+ </ScrollView>
+
+ <LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@@ -53,6 +55,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_doNotSave"/>
-</LinearLayout>
+
+ </LinearLayout>
</LinearLayout>
diff --git a/res/layout/edit_key_key_item.xml b/res/layout/edit_key_key_item.xml
index 46de4a977..ba428938e 100644
--- a/res/layout/edit_key_key_item.xml
+++ b/res/layout/edit_key_key_item.xml
@@ -15,99 +15,129 @@
-->
<org.thialfihar.android.apg.ui.widget.KeyEditor
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="5dip">
-
-<View
- android:id="@+id/separator"
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"/>
-
-<LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:orientation="horizontal">
- <TableLayout
- android:layout_height="wrap_content"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_marginLeft="16dip"
- android:stretchColumns="1">
- <TableRow>
- <TextView android:id="@+id/label_key_id" android:text="Key ID"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"/>
- <TextView
- android:id="@+id/key_id"
- android:text="00000000 00000000"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingRight="5dip"
- android:typeface="monospace"/>
- </TableRow>
- <TableRow>
- <TextView android:id="@+id/label_algorithm"
- android:text="Algorithm"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"/>
- <TextView android:id="@+id/algorithm"
- android:text="Name"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingRight="5dip"/>
- </TableRow>
- <TableRow>
- <TextView android:id="@+id/label_creation"
- android:text="Creation"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"/>
- <TextView
- android:id="@+id/creation"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- </TableRow>
- <TableRow>
- <TextView android:id="@+id/label_expiry"
- android:text="Expiry"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"/>
- <Button
- android:id="@+id/expiry"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- </TableRow>
- <TableRow>
- <TextView android:id="@+id/label_usage"
- android:text="Usage"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"/>
- <Spinner
- android:id="@+id/usage"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- </TableRow>
- </TableLayout>
-
- <ImageButton
- android:id="@+id/edit_delete"
- android:layout_width="wrap_content"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="5dip">
+
+ <View
+ android:id="@+id/separator"
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"/>
+
+ <LinearLayout
android:layout_height="wrap_content"
- style="@style/MinusButton"
- android:layout_gravity="center_vertical"/>
+ android:layout_width="fill_parent"
+ android:orientation="horizontal">
+
+ <TableLayout
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_marginLeft="16dip"
+ android:stretchColumns="1">
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_keyId"
+ android:text="Key ID"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <TextView
+ android:id="@+id/keyId"
+ android:text="00000000 00000000"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingRight="5dip"
+ android:typeface="monospace"/>
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_algorithm"
+ android:text="Algorithm"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <TextView
+ android:id="@+id/algorithm"
+ android:text="Name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingRight="5dip"/>
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_creation"
+ android:text="Creation"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <TextView
+ android:id="@+id/creation"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_expiry"
+ android:text="Expiry"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Button
+ android:id="@+id/expiry"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_usage"
+ android:text="Usage"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Spinner
+ android:id="@+id/usage"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+ </TableRow>
+
+ </TableLayout>
+
+ <ImageButton
+ android:id="@+id/delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/MinusButton"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
-</LinearLayout>
</org.thialfihar.android.apg.ui.widget.KeyEditor>
diff --git a/res/layout/edit_key_section.xml b/res/layout/edit_key_section.xml
index b3a48f87e..e57b3b8df 100644
--- a/res/layout/edit_key_section.xml
+++ b/res/layout/edit_key_section.xml
@@ -15,7 +15,7 @@
-->
<org.thialfihar.android.apg.ui.widget.SectionView
- xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
diff --git a/res/layout/edit_key_user_id_item.xml b/res/layout/edit_key_user_id_item.xml
index 998c436cb..6164cf767 100644
--- a/res/layout/edit_key_user_id_item.xml
+++ b/res/layout/edit_key_user_id_item.xml
@@ -15,82 +15,99 @@
-->
<org.thialfihar.android.apg.ui.widget.UserIdEditor
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="5dip">
-
-<View
- android:id="@+id/separator"
- android:layout_width="fill_parent"
- android:layout_height="1dip"
- android:background="?android:attr/listDivider"/>
-
-<RadioButton
- android:id="@+id/is_main_user_id" android:text="Main User ID"
- android:layout_height="wrap_content" android:layout_width="wrap_content"
- android:layout_marginLeft="20dip"/>
-
-<LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:orientation="horizontal">
- <TableLayout
- android:layout_height="wrap_content"
- android:layout_width="0dip"
- android:layout_weight="1"
- android:layout_marginLeft="16dip">
- <TableRow>
- <TextView
- android:id="@+id/name_label"
- android:text="Name"
- android:layout_gravity="center_vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingRight="5dip"/>
- <EditText
- android:id="@+id/name"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"/>
- </TableRow>
- <TableRow>
- <TextView
- android:id="@+id/email_label"
- android:text="Email"
- android:layout_gravity="center_vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingRight="5dip"/>
- <EditText
- android:id="@+id/email"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"/>
- </TableRow>
- <TableRow>
- <TextView
- android:id="@+id/comment_label"
- android:text="Comment"
- android:layout_gravity="center_vertical"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:paddingRight="5dip"/>
- <EditText
- android:id="@+id/comment"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"/>
- </TableRow>
- </TableLayout>
-
- <ImageButton
- android:id="@+id/edit_delete"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="5dip">
+
+ <View
+ android:id="@+id/separator"
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"/>
+
+ <RadioButton
+ android:id="@+id/isMainUserId"
+ android:text="Main User ID"
+ android:layout_height="wrap_content"
android:layout_width="wrap_content"
+ android:layout_marginLeft="20dip"/>
+
+ <LinearLayout
android:layout_height="wrap_content"
- style="@style/MinusButton"
- android:layout_gravity="center_vertical"/>
+ android:layout_width="fill_parent"
+ android:orientation="horizontal">
+
+ <TableLayout
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_marginLeft="16dip">
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_name"
+ android:text="Name"
+ android:layout_gravity="center_vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingRight="5dip"/>
+
+ <EditText
+ android:id="@+id/name"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_email"
+ android:text="Email"
+ android:layout_gravity="center_vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingRight="5dip"/>
+
+ <EditText
+ android:id="@+id/email"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_comment"
+ android:text="Comment"
+ android:layout_gravity="center_vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingRight="5dip"/>
+
+ <EditText
+ android:id="@+id/comment"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"/>
+
+ </TableRow>
+
+ </TableLayout>
+
+ <ImageButton
+ android:id="@+id/delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/MinusButton"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
-</LinearLayout>
</org.thialfihar.android.apg.ui.widget.UserIdEditor>
diff --git a/res/layout/encrypt.xml b/res/layout/encrypt.xml
new file mode 100644
index 000000000..dd51318c2
--- /dev/null
+++ b/res/layout/encrypt.xml
@@ -0,0 +1,382 @@
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:paddingTop="5dip">
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:layout_marginLeft="5dip">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/sourcePrevious"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous"/>
+
+ <TextView
+ android:id="@+id/sourceLabel"
+ android:layout_width="0dip"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:text="@string/label_message"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_horizontal|center_vertical"
+ android:textColor="#ffffffff"/>
+
+ <ImageView
+ android:id="@+id/sourceNext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next"/>
+
+ </LinearLayout>
+
+ <ViewFlipper
+ android:id="@+id/source"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1">
+
+ <LinearLayout
+ android:id="@+id/sourceMessage"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <EditText
+ android:id="@+id/message"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="top"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sourceFile"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <EditText
+ android:id="@+id/filename"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/btn_browse"
+ android:src="@drawable/ic_launcher_folder_small"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_fileCompression"
+ android:text="@string/label_fileCompression"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Spinner
+ android:id="@+id/fileCompression"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_deleteAfterEncryption"
+ android:text="@string/label_deleteAfterEncryption"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"/>
+
+ <CheckBox
+ android:id="@+id/deleteAfterEncryption"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_asciiArmour"
+ android:text="@string/label_asciiArmour"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"/>
+
+ <CheckBox
+ android:id="@+id/asciiArmour"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </ViewFlipper>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="5dip"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/modePrevious"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous"/>
+
+ <TextView
+ android:id="@+id/modeLabel"
+ android:layout_width="0dip"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:text="@string/label_asymmetric"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:gravity="center_horizontal|center_vertical"
+ android:textColor="#ffffffff"/>
+
+ <ImageView
+ android:id="@+id/modeNext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next"/>
+
+ </LinearLayout>
+
+ <ViewFlipper
+ android:id="@+id/mode"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:id="@+id/modeAsymmetric"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_sign"
+ android:text="@string/label_sign"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="5dip">
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:text="Main User Id"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"/>
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:text="Main User Id Rest"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"/>
+
+ </LinearLayout>
+
+ <CheckBox
+ android:id="@+id/sign"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="3dip"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_selectPublicKeys"
+ android:text="@string/label_selectPublicKeys"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"/>
+
+ <Button
+ android:text="@string/btn_selectEncryptKeys"
+ android:id="@+id/btn_selectEncryptKeys"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <TableLayout
+ android:id="@+id/modeSymmetric"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:stretchColumns="1">
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_passPhrase"
+ android:text="@string/label_passPhrase"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <EditText
+ android:id="@+id/passPhrase"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:inputType="textPassword"/>
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_passPhraseAgain"
+ android:text="@string/label_passPhraseAgain"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <EditText
+ android:id="@+id/passPhraseAgain"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:inputType="textPassword"/>
+
+ </TableRow>
+
+ </TableLayout>
+
+ </ViewFlipper>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="5dip"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/ButtonBar">
+
+ <Button
+ android:id="@+id/btn_encryptToClipboard"
+ android:text="@string/btn_encryptToClipboard"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/btn_encrypt"
+ android:text="@string/btn_encrypt"
+ android:layout_weight="1"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/encrypt_message.xml b/res/layout/encrypt_message.xml
deleted file mode 100644
index 254552e03..000000000
--- a/res/layout/encrypt_message.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-<?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.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:fillViewport="true">
-
-<EditText
- android:id="@+id/message"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:gravity="top"
- android:inputType="text|textCapSentences|textMultiLine|textLongMessage">
-</EditText>
-
-<LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="fill_parent" android:paddingBottom="3dip">
-
-<CheckBox
- android:text="@string/sign"
- android:id="@+id/sign"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"/>
-
-<LinearLayout
- android:orientation="vertical"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:layout_gravity="center_vertical"
- android:paddingRight="5dip">
-
-<TextView
- android:id="@+id/main_user_id"
- android:text="Main User Id"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right">
-</TextView>
-
-<TextView
- android:id="@+id/main_user_id_rest"
- android:text="Main User Id Rest"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:layout_gravity="right">
-</TextView>
-
-</LinearLayout>
-</LinearLayout>
-
-<LinearLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- style="@android:style/ButtonBar">
-
-<Button
- android:text="@string/btn_selectEncryptKeys"
- android:id="@+id/btn_selectEncryptKeys"
- android:layout_weight="1"
- android:layout_width="0dip"
- android:layout_height="wrap_content"/>
-
-<Button
- android:id="@+id/btn_send"
- android:text="@string/btn_send"
- android:layout_weight="1"
- android:layout_width="0dip"
- android:layout_height="wrap_content"/>
-
-</LinearLayout>
-
-</LinearLayout>
-
-
diff --git a/res/layout/file_dialog.xml b/res/layout/file_dialog.xml
new file mode 100644
index 000000000..c42d2636e
--- /dev/null
+++ b/res/layout/file_dialog.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip">
+
+ <EditText
+ android:id="@+id/input"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <ImageButton
+ android:id="@+id/btn_browse"
+ android:src="@drawable/ic_launcher_folder_small"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"/>
+
+</LinearLayout>
diff --git a/res/layout/info.xml b/res/layout/info.xml
new file mode 100644
index 000000000..ab9e99a5b
--- /dev/null
+++ b/res/layout/info.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fillViewport="true"
+ android:padding="5dip">
+
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:textColor="#ffffffff"
+ android:autoLink="all"/>
+</ScrollView>
diff --git a/res/layout/key_list.xml b/res/layout/key_list.xml
new file mode 100644
index 000000000..f08495368
--- /dev/null
+++ b/res/layout/key_list.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:fillViewport="true">
+
+ <ExpandableListView
+ android:id="@+id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
+
+</ScrollView>
diff --git a/res/layout/key_list_child_item_master_key.xml b/res/layout/key_list_child_item_master_key.xml
index 47eba65b5..790fd42f4 100644
--- a/res/layout/key_list_child_item_master_key.xml
+++ b/res/layout/key_list_child_item_master_key.xml
@@ -19,54 +19,62 @@
android:singleLine="true"
android:paddingLeft="10dip"
android:layout_marginRight="?android:attr/scrollbarSize"
- android:layout_height="?android:attr/listPreferredItemHeight" android:layout_width="fill_parent">
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:layout_width="fill_parent">
-<LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:orientation="horizontal"
- android:paddingRight="3dip">
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:orientation="horizontal"
+ android:paddingRight="3dip">
-<ImageView
- android:id="@+id/ic_master_key"
- android:src="@drawable/key_small"
- android:paddingRight="6dip"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:layout_gravity="center_vertical"/>
+ <ImageView
+ android:id="@+id/ic_masterKey"
+ android:src="@drawable/key_small"
+ android:paddingRight="6dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
-<TextView
- android:id="@+id/key_id"
- android:text="Key ID"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:paddingRight="5dip" android:typeface="monospace"/>
+ <TextView
+ android:id="@+id/keyId"
+ android:text="Key ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:typeface="monospace"/>
-<TextView
- android:id="@+id/key_details"
- android:text="(RSA, 1024bit)"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/keyDetails"
+ android:text="(RSA, 1024bit)"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <LinearLayout
+ android:gravity="right"
+ android:orientation="horizontal"
+ android:paddingBottom="2dip"
+ android:paddingTop="2dip"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical">
-<LinearLayout
- android:gravity="right"
- android:orientation="horizontal"
- android:layout_width="fill_parent" android:paddingBottom="2dip" android:paddingTop="2dip" android:layout_height="fill_parent" android:layout_gravity="center_vertical">
+ <ImageView
+ android:id="@+id/ic_encryptKey"
+ android:src="@drawable/encrypted_small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-<ImageView
- android:id="@+id/ic_encrypt_key"
- android:src="@drawable/encrypted_small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <ImageView
+ android:id="@+id/ic_signKey"
+ android:src="@drawable/signed_small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-<ImageView
- android:id="@+id/ic_sign_key"
- android:src="@drawable/signed_small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ </LinearLayout>
-</LinearLayout>
+ </LinearLayout>
</LinearLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/res/layout/key_list_child_item_sub_key.xml b/res/layout/key_list_child_item_sub_key.xml
index 085d78f05..ac7c217a6 100644
--- a/res/layout/key_list_child_item_sub_key.xml
+++ b/res/layout/key_list_child_item_sub_key.xml
@@ -22,49 +22,51 @@
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_width="fill_parent">
-<LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:orientation="horizontal"
- android:paddingRight="3dip">
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:orientation="horizontal"
+ android:paddingRight="3dip">
-<TextView
- android:id="@+id/key_id"
- android:text="Key ID"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:paddingRight="5dip" android:typeface="monospace"/>
+ <TextView
+ android:id="@+id/keyId"
+ android:text="Key ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:typeface="monospace"/>
-<TextView
- android:id="@+id/key_details"
- android:text="(RSA, 1024bit)"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/keyDetails"
+ android:text="(RSA, 1024bit)"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <LinearLayout
+ android:gravity="right"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:paddingBottom="2dip"
+ android:paddingTop="2dip"
+ android:layout_gravity="center_vertical">
-<LinearLayout
- android:gravity="right"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:paddingBottom="2dip"
- android:paddingTop="2dip"
- android:layout_gravity="center_vertical">
+ <ImageView
+ android:id="@+id/ic_encryptKey"
+ android:src="@drawable/encrypted_small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-<ImageView
- android:id="@+id/ic_encrypt_key"
- android:src="@drawable/encrypted_small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <ImageView
+ android:id="@+id/ic_signKey"
+ android:src="@drawable/signed_small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-<ImageView
- android:id="@+id/ic_sign_key"
- android:src="@drawable/signed_small"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ </LinearLayout>
-</LinearLayout>
+ </LinearLayout>
</LinearLayout>
-</LinearLayout> \ No newline at end of file
diff --git a/res/layout/key_list_child_item_user_id.xml b/res/layout/key_list_child_item_user_id.xml
index 80cdd2867..3226dd58a 100644
--- a/res/layout/key_list_child_item_user_id.xml
+++ b/res/layout/key_list_child_item_user_id.xml
@@ -22,12 +22,12 @@
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight">
-<TextView
- android:id="@+id/user_id"
- android:text="User ID"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="3dip"/>
+ <TextView
+ android:id="@+id/userId"
+ android:text="User ID"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="3dip"/>
</LinearLayout>
diff --git a/res/layout/key_list_group_item.xml b/res/layout/key_list_group_item.xml
index aaada82e3..35d0ab367 100644
--- a/res/layout/key_list_group_item.xml
+++ b/res/layout/key_list_group_item.xml
@@ -21,32 +21,33 @@
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight">
-<LinearLayout
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:paddingLeft="36dip">
-
-<TextView
- android:id="@+id/main_user_id"
- android:text="Main User ID"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
-<TextView
- android:id="@+id/main_user_id_rest"
- android:text="&lt;user@somewhere.com&gt;"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
-</LinearLayout>
-
-<LinearLayout
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="36dip">
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:text="Main User ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:text="&lt;user@somewhere.com&gt;"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ </LinearLayout>
</LinearLayout>
-
-</LinearLayout> \ No newline at end of file
diff --git a/res/layout/mailbox_message_item.xml b/res/layout/mailbox_message_item.xml
index b2b5e91d4..05a267fb6 100644
--- a/res/layout/mailbox_message_item.xml
+++ b/res/layout/mailbox_message_item.xml
@@ -24,34 +24,34 @@
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_width="fill_parent">
-<ImageView
- android:id="@+id/ic_encrypted"
- android:src="@drawable/encrypted"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"/>
-
-<LinearLayout
- android:orientation="vertical"
- android:paddingLeft="5dip"
- android:layout_weight="1"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
-<TextView
- android:id="@+id/subject"
- android:text="Subject"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
-<TextView
- android:id="@+id/email_address"
- android:text="user@somewhere.com"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <ImageView
+ android:id="@+id/ic_status"
+ android:src="@drawable/encrypted"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:paddingLeft="5dip"
+ android:layout_weight="1"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/subject"
+ android:text="Subject"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/emailAddress"
+ android:text="user@somewhere.com"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
</LinearLayout>
-
-</LinearLayout> \ No newline at end of file
diff --git a/res/layout/main.xml b/res/layout/main.xml
index 81c5f224b..f88c393db 100644
--- a/res/layout/main.xml
+++ b/res/layout/main.xml
@@ -22,40 +22,68 @@
android:paddingTop="5dip"
android:fillViewport="true">
-<ScrollView
- android:layout_marginTop="10dip"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
+ <ScrollView
+ android:layout_marginTop="10dip"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:fillViewport="true">
-<ListView
- android:id="@+id/account_list"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-</ListView>
+ <ListView
+ android:id="@+id/accounts"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
-</ScrollView>
+ </ScrollView>
-<LinearLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- style="@android:style/ButtonBar">
-
-<Button
- android:id="@+id/btn_encryptMessage"
- android:text="@string/btn_encryptMessage"
- android:layout_width="wrap_content"
- android:layout_weight="1"
- android:layout_height="wrap_content"/>
-
-<Button
- android:id="@+id/btn_decryptMessage"
- android:text="@string/btn_decryptMessage"
- android:layout_width="wrap_content"
- android:layout_weight="1"
- android:layout_height="wrap_content"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/ButtonBar">
-</LinearLayout>
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <Button
+ android:id="@+id/btn_encryptFile"
+ android:text="@string/btn_encryptFile"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/btn_decryptFile"
+ android:text="@string/btn_decryptFile"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
-</LinearLayout> \ No newline at end of file
+ <Button
+ android:id="@+id/btn_encryptMessage"
+ android:text="@string/btn_encryptMessage"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/btn_decryptMessage"
+ android:text="@string/btn_decryptMessage"
+ android:layout_width="wrap_content"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/pass_phrase.xml b/res/layout/pass_phrase.xml
new file mode 100644
index 000000000..f377e5bf7
--- /dev/null
+++ b/res/layout/pass_phrase.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip">
+
+ <EditText
+ android:id="@+id/passPhrase"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"/>
+
+ <EditText
+ android:id="@+id/passPhraseAgain"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"/>
+
+</LinearLayout>
diff --git a/res/layout/preferences.xml b/res/layout/preferences.xml
new file mode 100644
index 000000000..1ff97a0e4
--- /dev/null
+++ b/res/layout/preferences.xml
@@ -0,0 +1,197 @@
+<?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.
+-->
+
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:paddingTop="5dip"
+ android:layout_marginRight="?android:attr/scrollbarSize">
+
+ <TextView
+ android:id="@+id/section_general"
+ android:text="@string/section_general"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="5dip"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_passPhraseCacheTtl"
+ android:text="@string/label_passPhraseCacheTtl"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Spinner
+ android:id="@+id/passPhraseCacheTtl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/section_defaults"
+ android:text="@string/section_defaults"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dip"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+ <View
+ android:layout_width="fill_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_marginBottom="5dip"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_encryptionAlgorithm"
+ android:text="@string/label_encryptionAlgorithm"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Spinner
+ android:id="@+id/encryptionAlgorithm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_hashAlgorithm"
+ android:text="@string/label_hashAlgorithm"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Spinner
+ android:id="@+id/hashAlgorithm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_messageCompression"
+ android:text="@string/label_messageCompression"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Spinner
+ android:id="@+id/messageCompression"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_fileCompression"
+ android:text="@string/label_fileCompression"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"/>
+
+ <Spinner
+ android:id="@+id/fileCompression"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_asciiArmour"
+ android:text="@string/label_asciiArmour"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:layout_height="wrap_content"
+ android:layout_width="0dip"
+ android:layout_weight="1"/>
+
+ <CheckBox
+ android:id="@+id/asciiArmour"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center_vertical"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/select_public_key.xml b/res/layout/select_public_key.xml
index 9a2d9f578..3c7c6534a 100644
--- a/res/layout/select_public_key.xml
+++ b/res/layout/select_public_key.xml
@@ -21,34 +21,33 @@
android:layout_height="fill_parent"
android:fillViewport="true">
-<ListView
- android:id="@+id/list"
- android:choiceMode="multipleChoice"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-</ListView>
-
-<LinearLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- style="@android:style/ButtonBar">
-
-<Button
- android:text="@android:string/ok"
- android:id="@+id/btn_ok"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
-<Button
- android:text="@android:string/cancel"
- android:id="@+id/btn_cancel"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
-
-</LinearLayout>
+ <ListView
+ android:id="@+id/list"
+ android:choiceMode="multipleChoice"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"/>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/ButtonBar">
+
+ <Button
+ android:text="@android:string/ok"
+ android:id="@+id/btn_ok"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ <Button
+ android:text="@android:string/cancel"
+ android:id="@+id/btn_cancel"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+
+ </LinearLayout>
</LinearLayout>
diff --git a/res/layout/select_public_key_item.xml b/res/layout/select_public_key_item.xml
index aba0c09b9..bb0dd30a4 100644
--- a/res/layout/select_public_key_item.xml
+++ b/res/layout/select_public_key_item.xml
@@ -22,75 +22,75 @@
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_width="fill_parent">
-<CheckBox
- android:id="@+id/selected"
- android:focusable="false"
- android:focusableInTouchMode="false"
- android:clickable="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <CheckBox
+ android:id="@+id/selected"
+ android:focusable="false"
+ android:focusableInTouchMode="false"
+ android:clickable="false"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-<LinearLayout
- android:orientation="vertical"
- android:paddingLeft="5dip"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1">
-
-<TextView
- android:id="@+id/main_user_id"
- android:text="Main User ID"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
-<TextView
- android:id="@+id/main_user_id_rest"
- android:text="&lt;user@somewhere.com&gt;"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-</LinearLayout>
+ <LinearLayout
+ android:orientation="vertical"
+ android:paddingLeft="5dip"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
-<LinearLayout
- android:id="@+id/right_column"
- android:orientation="vertical"
- android:minWidth="90dip"
- android:paddingLeft="3dip"
- android:gravity="right"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
-<TextView
- android:id="@+id/key_id"
- android:text="BBBBBBBB"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:typeface="monospace"
- android:layout_width="wrap_content"
- 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"
- android:textStyle="italic"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/mainUserId"
+ android:text="Main User ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-</LinearLayout>
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:text="&lt;user@somewhere.com&gt;"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:minWidth="90dip"
+ android:paddingLeft="3dip"
+ android:gravity="right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
-</LinearLayout> \ No newline at end of file
+ <TextView
+ android:id="@+id/keyId"
+ android:text="BBBBBBBB"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:typeface="monospace"
+ android:layout_width="wrap_content"
+ 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"
+ android:textStyle="italic"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/res/layout/select_secret_key.xml b/res/layout/select_secret_key.xml
index 64967ace6..f252f56e5 100644
--- a/res/layout/select_secret_key.xml
+++ b/res/layout/select_secret_key.xml
@@ -21,10 +21,9 @@
android:layout_height="fill_parent"
android:fillViewport="true">
-<ListView
- android:id="@+id/list"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-</ListView>
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"/>
</LinearLayout>
diff --git a/res/layout/select_secret_key_item.xml b/res/layout/select_secret_key_item.xml
index 0b0475c37..35bf58e23 100644
--- a/res/layout/select_secret_key_item.xml
+++ b/res/layout/select_secret_key_item.xml
@@ -22,67 +22,68 @@
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_width="fill_parent">
-<LinearLayout
- android:orientation="vertical"
- android:paddingLeft="5dip"
- android:paddingRight="5dip"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1">
+ <LinearLayout
+ android:orientation="vertical"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
-<TextView
- android:id="@+id/main_user_id"
- android:text="Main User ID"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/mainUserId"
+ android:text="Main User ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-<TextView
- android:id="@+id/main_user_id_rest"
- android:text="&lt;user@somewhere.com&gt;"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-</LinearLayout>
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:text="&lt;user@somewhere.com&gt;"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
-<LinearLayout
- android:orientation="vertical"
- android:minWidth="90dip"
- android:paddingLeft="3dip"
- android:gravity="right"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ </LinearLayout>
-<TextView
- android:id="@+id/key_id"
- android:text="BBBBBBBB"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:typeface="monospace"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:minWidth="90dip"
+ android:paddingLeft="3dip"
+ android:gravity="right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
-<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/keyId"
+ android:text="BBBBBBBB"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:typeface="monospace"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"/>
-<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/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/status"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="expired"
- android:textStyle="italic"
- 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"/>
-</LinearLayout>
+ <TextView
+ android:id="@+id/status"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:text="expired"
+ android:textStyle="italic"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ </LinearLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
new file mode 100644
index 000000000..5bb4dc2ba
--- /dev/null
+++ b/res/values-de/strings.xml
@@ -0,0 +1,230 @@
+<?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">Mail Inbox</string>
+ <string name="title_managePublicKeys">Manage Public Keys</string>
+ <string name="title_manageSecretKeys">Manage Secret Keys</string>
+ <string name="title_selectRecipients">Select Recipients</string>
+ <string name="title_selectSignature">Select Signature</string>
+ <string name="title_encrypt">Encrypt</string>
+ <string name="title_decrypt">Decrypt</string>
+ <string name="title_authentification">Authentification</string>
+ <string name="title_createKey">Create Key</string>
+ <string name="title_editKey">Edit Key</string>
+ <string name="title_preferences">Preferences</string>
+ <string name="title_changePassPhrase">Change Pass Phrase</string>
+ <string name="title_setPassPhrase">Set Pass Phrase</string>
+ <string name="title_sendEmail">"Send Mail..."</string>
+ <string name="title_encryptToFile">Encrypt To File</string>
+ <string name="title_decryptToFile">Decrypt To File</string>
+ <string name="title_addAccount">Add Account</string>
+ <string name="title_importKeys">Import Keys</string>
+ <string name="title_exportKey">Export Key</string>
+ <string name="title_exportKeys">Export Keys</string>
+
+ <!-- section_lowerCase: capitalized words, no punctuation -->
+ <string name="section_userIds">User IDs</string>
+ <string name="section_keys">Keys</string>
+ <string name="section_defaults">Defaults</string>
+
+ <!-- btn_lowerCase: capitalized words, no punctuation -->
+ <string name="btn_encryptToClipboard">Encrypt To Clipboard</string>
+ <string name="btn_send">Encrypt And Email</string>
+ <string name="btn_encrypt">Encrypt</string>
+ <string name="btn_decrypt">Decrypt</string>
+ <string name="btn_verify">Verify</string>
+ <string name="btn_selectEncryptKeys">Select Recipients</string>
+ <string name="btn_reply">Reply</string>
+ <string name="btn_encryptMessage">Encrypt Message</string>
+ <string name="btn_decryptMessage">Decrypt Message</string>
+ <string name="btn_encryptFile">Encrypt File</string>
+ <string name="btn_decryptFile">Decrypt File</string>
+ <string name="btn_save">Save</string>
+ <string name="btn_doNotSave">Cancel</string>
+ <string name="btn_delete">Delete</string>
+ <string name="btn_noDate">None</string>
+
+ <!-- menu_lowerCase: capitalized words, no punctuation -->
+ <string name="menu_about">About</string>
+ <string name="menu_addAccount">Add GMail Account</string>
+ <string name="menu_deleteAccount">Delete Account</string>
+ <string name="menu_managePublicKeys">Manage Public Keys</string>
+ <string name="menu_manageSecretKeys">Manage Secret Keys</string>
+ <string name="menu_preferences">Settings</string>
+ <string name="menu_changePassPhrase">Change Pass Phrase</string>
+ <string name="menu_setPassPhrase">Set Pass Phrase</string>
+ <string name="menu_importKeys">Import Keys</string>
+ <string name="menu_exportKeys">Export Keys</string>
+ <string name="menu_exportKey">Export Key</string>
+ <string name="menu_deleteKey">Delete Key</string>
+ <string name="menu_createKey">Create Key</string>
+ <string name="menu_editKey">Edit Key</string>
+
+ <!-- label_lowerCase: capitalized words, no punctuation -->
+ <string name="label_sign">Sign</string>
+ <string name="label_message">Message</string>
+ <string name="label_file">File</string>
+ <string name="label_passPhrase">Pass Phrase</string>
+ <string name="label_passPhraseAgain">Again</string>
+ <string name="label_algorithm">Algorithm</string>
+ <string name="label_asciiArmour">ASCII Armour</string>
+ <string name="label_selectPublicKeys">Public Key(s)</string>
+ <string name="label_deleteAfterEncryption">Delete After Encryption</string>
+ <string name="label_deleteAfterDecryption">Delete After Decryption</string>
+ <string name="label_encryptionAlgorithm">Encryption Algorithm</string>
+ <string name="label_hashAlgorithm">Hash Algorithm</string>
+ <string name="label_asymmetric">Public Key</string>
+ <string name="label_symmetric">Pass Phrase</string>
+
+ <string name="noKeysSelected">Select</string>
+ <string name="oneKeySelected">1 Selected</string>
+ <string name="nKeysSelected">Selected</string>
+ <string name="unknownUserId">&lt;unknown&gt;</string>
+ <string name="none">&lt;none&gt;</string>
+ <string name="noKey">&lt;no key&gt;</string>
+ <string name="noDate">-</string>
+ <string name="noExpiry">&lt;no expiry&gt;</string>
+ <string name="unknownStatus"></string>
+ <string name="canEncrypt">can encrypt</string>
+ <string name="canSign">can sign</string>
+ <string name="expired">expired</string>
+ <string name="notValid">not valid</string>
+
+ <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_signOnly">Sign only</string>
+ <string name="choice_encryptOnly">Encrypt only</string>
+ <string name="choice_signAndEncrypt">Sign and Encrypt</string>
+
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+
+ <string name="filemanager_titleOpen">Open...</string>
+ <string name="filemanager_titleSave">Save As...</string>
+ <string name="filemanager_titleEncrypt">Select File To Encrypt...</string>
+ <string name="filemanager_titleDecrypt">Select File To Decrypt...</string>
+ <string name="filemanager_btnOpen">Open</string>
+ <string name="filemanager_btnSave">Save</string>
+
+ <string name="warning">Warning</string>
+ <string name="error">Error</string>
+ <string name="warningMessage">Warning: %s</string>
+ <string name="errorMessage">Error: %s</string>
+
+ <!-- sentences -->
+ <string name="wrongPassPhrase">Wrong pass phrase.</string>
+ <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="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>
+ <string name="passPhraseFor">Pass phrase for %s:</string>
+ <string name="fileDeleteConfirmation">Are you sure you want to delete\n%s?</string>
+ <string name="fileDeleteSuccessful">Successfully deleted.</string>
+ <string name="noFileSelected">Select a file first.</string>
+ <string name="decryptionSuccessful">Successfully decrypted.</string>
+ <string name="encryptionSuccessful">Successfully encrypted.</string>
+ <string name="encryptionToClipboardSuccessful">Successfully encrypted to clipboard.</string>
+ <string name="enterPassPhraseTwice">Enter the pass phrase twice.</string>
+ <string name="selectEncryptionKey">Select at least one encryption key.</string>
+ <string name="selectEncryptionOrSignatureKey">Select at least one encryption key or a signature key.</string>
+ <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="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="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>
+
+ <!-- error_lowerCase: phrases, no punctuation, all lowercase,
+ they will be put after "errorMessage", e.g. "Error: file not found" -->
+ <string name="error_fileDeleteFailed">deleting '%s' failed</string>
+ <string name="error_fileNotFound">file not found</string>
+ <string name="error_noSecretKeyFound">no suitable secret key found</string>
+ <string name="error_noKnownEncryptionFound">no known kind of encryption found</string>
+ <string name="error_externalStorageNotReady">external storage not ready</string>
+ <string name="error_accountNotFound">account '%s' not found</string>
+ <string name="error_addingAccountFailed">adding account '%s' failed</string>
+ <string name="error_invalidEmail">invalid email '%s'</string>
+ <string name="error_keySizeMinimum512bit">key size must be at least 512bit</string>
+ <string name="error_masterKeyMustNotBeElGamal">the master key cannot be an ElGamal key</string>
+ <string name="error_unknownAlgorithmChoice">unknown algorithm choice</string>
+ <string name="error_userIdNeedsAName">you need to specify a name</string>
+ <string name="error_userIdNeedsAnEmailAddress">you need to specify an email address</string>
+ <string name="error_keyNeedsAUserId">need at least one user id</string>
+ <string name="error_mainUserIdMustNotBeEmpty">main user id must not be empty</string>
+ <string name="error_keyNeedsMasterKey">need at least a master key</string>
+ <string name="error_expiryMustComeAfterCreation">expiry date must come after creation date</string>
+ <string name="error_noEncryptionKeysOrPassPhrase">no encryption key(s) or pass phrase given</string>
+ <string name="error_signatureFailed">signature failed</string>
+ <string name="error_noSignaturePassPhrase">no pass phrase given</string>
+ <string name="error_noSignatureKey">no signature key given</string>
+ <string name="error_invalidData">not valid encryption data</string>
+ <string name="error_corruptData">corrupt data</string>
+ <string name="error_noSymmetricEncryptionPacket">couldn't find a packet with symmetric encryption</string>
+ <string name="error_wrongPassPhrase">wrong pass phrase</string>
+ <string name="error_savingKeys">error saving some key(s)</string>
+
+ <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
+ <string name="progress_done">done.</string>
+ <string name="progress_initializing">initializing...</string>
+ <string name="progress_saving">saving...</string>
+ <string name="progress_importing">importing...</string>
+ <string name="progress_exporting">exporting...</string>
+ <string name="progress_generating">generating key, this can take a while...</string>
+ <string name="progress_buildingKey">building key...</string>
+ <string name="progress_preparingMasterKey">preparing master key...</string>
+ <string name="progress_certifyingMasterKey">certifying master key...</string>
+ <string name="progress_buildingMasterKeyRing">building master key ring...</string>
+ <string name="progress_addingSubKeys">adding sub keys...</string>
+ <string name="progress_savingKeyRing">saving key ring...</string>
+ <string name="progress_importingSecretKeys">importing secret keys...</string>
+ <string name="progress_importingPublicKeys">importing public keys...</string>
+ <string name="progress_reloadingKeys">reloading keys...</string>
+ <string name="progress_exportingKey">exporting key...</string>
+ <string name="progress_exportingKeys">exporting keys...</string>
+ <string name="progress_extractingSignatureKey">extracting signature key...</string>
+ <string name="progress_extractingKey">extracting key...</string>
+ <string name="progress_preparingStreams">preparing streams...</string>
+ <string name="progress_encrypting">encrypting data...</string>
+ <string name="progress_decrypting">decrypting data...</string>
+ <string name="progress_preparingSignature">preparing signature...</string>
+ <string name="progress_generatingSignature">generating signature...</string>
+ <string name="progress_processingSignature">processing signature...</string>
+ <string name="progress_verifyingSignature">verifying signature...</string>
+ <string name="progress_signing">signing...</string>
+ <string name="progress_readingData">reading data...</string>
+ <string name="progress_findingKey">finding key...</string>
+ <string name="progress_decompressingData">decompressing data...</string>
+ <string name="progress_verifyingIntegrity">verifying integrity...</string>
+
+</resources>
+
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
new file mode 100644
index 000000000..5bb4dc2ba
--- /dev/null
+++ b/res/values-ko/strings.xml
@@ -0,0 +1,230 @@
+<?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">Mail Inbox</string>
+ <string name="title_managePublicKeys">Manage Public Keys</string>
+ <string name="title_manageSecretKeys">Manage Secret Keys</string>
+ <string name="title_selectRecipients">Select Recipients</string>
+ <string name="title_selectSignature">Select Signature</string>
+ <string name="title_encrypt">Encrypt</string>
+ <string name="title_decrypt">Decrypt</string>
+ <string name="title_authentification">Authentification</string>
+ <string name="title_createKey">Create Key</string>
+ <string name="title_editKey">Edit Key</string>
+ <string name="title_preferences">Preferences</string>
+ <string name="title_changePassPhrase">Change Pass Phrase</string>
+ <string name="title_setPassPhrase">Set Pass Phrase</string>
+ <string name="title_sendEmail">"Send Mail..."</string>
+ <string name="title_encryptToFile">Encrypt To File</string>
+ <string name="title_decryptToFile">Decrypt To File</string>
+ <string name="title_addAccount">Add Account</string>
+ <string name="title_importKeys">Import Keys</string>
+ <string name="title_exportKey">Export Key</string>
+ <string name="title_exportKeys">Export Keys</string>
+
+ <!-- section_lowerCase: capitalized words, no punctuation -->
+ <string name="section_userIds">User IDs</string>
+ <string name="section_keys">Keys</string>
+ <string name="section_defaults">Defaults</string>
+
+ <!-- btn_lowerCase: capitalized words, no punctuation -->
+ <string name="btn_encryptToClipboard">Encrypt To Clipboard</string>
+ <string name="btn_send">Encrypt And Email</string>
+ <string name="btn_encrypt">Encrypt</string>
+ <string name="btn_decrypt">Decrypt</string>
+ <string name="btn_verify">Verify</string>
+ <string name="btn_selectEncryptKeys">Select Recipients</string>
+ <string name="btn_reply">Reply</string>
+ <string name="btn_encryptMessage">Encrypt Message</string>
+ <string name="btn_decryptMessage">Decrypt Message</string>
+ <string name="btn_encryptFile">Encrypt File</string>
+ <string name="btn_decryptFile">Decrypt File</string>
+ <string name="btn_save">Save</string>
+ <string name="btn_doNotSave">Cancel</string>
+ <string name="btn_delete">Delete</string>
+ <string name="btn_noDate">None</string>
+
+ <!-- menu_lowerCase: capitalized words, no punctuation -->
+ <string name="menu_about">About</string>
+ <string name="menu_addAccount">Add GMail Account</string>
+ <string name="menu_deleteAccount">Delete Account</string>
+ <string name="menu_managePublicKeys">Manage Public Keys</string>
+ <string name="menu_manageSecretKeys">Manage Secret Keys</string>
+ <string name="menu_preferences">Settings</string>
+ <string name="menu_changePassPhrase">Change Pass Phrase</string>
+ <string name="menu_setPassPhrase">Set Pass Phrase</string>
+ <string name="menu_importKeys">Import Keys</string>
+ <string name="menu_exportKeys">Export Keys</string>
+ <string name="menu_exportKey">Export Key</string>
+ <string name="menu_deleteKey">Delete Key</string>
+ <string name="menu_createKey">Create Key</string>
+ <string name="menu_editKey">Edit Key</string>
+
+ <!-- label_lowerCase: capitalized words, no punctuation -->
+ <string name="label_sign">Sign</string>
+ <string name="label_message">Message</string>
+ <string name="label_file">File</string>
+ <string name="label_passPhrase">Pass Phrase</string>
+ <string name="label_passPhraseAgain">Again</string>
+ <string name="label_algorithm">Algorithm</string>
+ <string name="label_asciiArmour">ASCII Armour</string>
+ <string name="label_selectPublicKeys">Public Key(s)</string>
+ <string name="label_deleteAfterEncryption">Delete After Encryption</string>
+ <string name="label_deleteAfterDecryption">Delete After Decryption</string>
+ <string name="label_encryptionAlgorithm">Encryption Algorithm</string>
+ <string name="label_hashAlgorithm">Hash Algorithm</string>
+ <string name="label_asymmetric">Public Key</string>
+ <string name="label_symmetric">Pass Phrase</string>
+
+ <string name="noKeysSelected">Select</string>
+ <string name="oneKeySelected">1 Selected</string>
+ <string name="nKeysSelected">Selected</string>
+ <string name="unknownUserId">&lt;unknown&gt;</string>
+ <string name="none">&lt;none&gt;</string>
+ <string name="noKey">&lt;no key&gt;</string>
+ <string name="noDate">-</string>
+ <string name="noExpiry">&lt;no expiry&gt;</string>
+ <string name="unknownStatus"></string>
+ <string name="canEncrypt">can encrypt</string>
+ <string name="canSign">can sign</string>
+ <string name="expired">expired</string>
+ <string name="notValid">not valid</string>
+
+ <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_signOnly">Sign only</string>
+ <string name="choice_encryptOnly">Encrypt only</string>
+ <string name="choice_signAndEncrypt">Sign and Encrypt</string>
+
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+
+ <string name="filemanager_titleOpen">Open...</string>
+ <string name="filemanager_titleSave">Save As...</string>
+ <string name="filemanager_titleEncrypt">Select File To Encrypt...</string>
+ <string name="filemanager_titleDecrypt">Select File To Decrypt...</string>
+ <string name="filemanager_btnOpen">Open</string>
+ <string name="filemanager_btnSave">Save</string>
+
+ <string name="warning">Warning</string>
+ <string name="error">Error</string>
+ <string name="warningMessage">Warning: %s</string>
+ <string name="errorMessage">Error: %s</string>
+
+ <!-- sentences -->
+ <string name="wrongPassPhrase">Wrong pass phrase.</string>
+ <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="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>
+ <string name="passPhraseFor">Pass phrase for %s:</string>
+ <string name="fileDeleteConfirmation">Are you sure you want to delete\n%s?</string>
+ <string name="fileDeleteSuccessful">Successfully deleted.</string>
+ <string name="noFileSelected">Select a file first.</string>
+ <string name="decryptionSuccessful">Successfully decrypted.</string>
+ <string name="encryptionSuccessful">Successfully encrypted.</string>
+ <string name="encryptionToClipboardSuccessful">Successfully encrypted to clipboard.</string>
+ <string name="enterPassPhraseTwice">Enter the pass phrase twice.</string>
+ <string name="selectEncryptionKey">Select at least one encryption key.</string>
+ <string name="selectEncryptionOrSignatureKey">Select at least one encryption key or a signature key.</string>
+ <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="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="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>
+
+ <!-- error_lowerCase: phrases, no punctuation, all lowercase,
+ they will be put after "errorMessage", e.g. "Error: file not found" -->
+ <string name="error_fileDeleteFailed">deleting '%s' failed</string>
+ <string name="error_fileNotFound">file not found</string>
+ <string name="error_noSecretKeyFound">no suitable secret key found</string>
+ <string name="error_noKnownEncryptionFound">no known kind of encryption found</string>
+ <string name="error_externalStorageNotReady">external storage not ready</string>
+ <string name="error_accountNotFound">account '%s' not found</string>
+ <string name="error_addingAccountFailed">adding account '%s' failed</string>
+ <string name="error_invalidEmail">invalid email '%s'</string>
+ <string name="error_keySizeMinimum512bit">key size must be at least 512bit</string>
+ <string name="error_masterKeyMustNotBeElGamal">the master key cannot be an ElGamal key</string>
+ <string name="error_unknownAlgorithmChoice">unknown algorithm choice</string>
+ <string name="error_userIdNeedsAName">you need to specify a name</string>
+ <string name="error_userIdNeedsAnEmailAddress">you need to specify an email address</string>
+ <string name="error_keyNeedsAUserId">need at least one user id</string>
+ <string name="error_mainUserIdMustNotBeEmpty">main user id must not be empty</string>
+ <string name="error_keyNeedsMasterKey">need at least a master key</string>
+ <string name="error_expiryMustComeAfterCreation">expiry date must come after creation date</string>
+ <string name="error_noEncryptionKeysOrPassPhrase">no encryption key(s) or pass phrase given</string>
+ <string name="error_signatureFailed">signature failed</string>
+ <string name="error_noSignaturePassPhrase">no pass phrase given</string>
+ <string name="error_noSignatureKey">no signature key given</string>
+ <string name="error_invalidData">not valid encryption data</string>
+ <string name="error_corruptData">corrupt data</string>
+ <string name="error_noSymmetricEncryptionPacket">couldn't find a packet with symmetric encryption</string>
+ <string name="error_wrongPassPhrase">wrong pass phrase</string>
+ <string name="error_savingKeys">error saving some key(s)</string>
+
+ <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
+ <string name="progress_done">done.</string>
+ <string name="progress_initializing">initializing...</string>
+ <string name="progress_saving">saving...</string>
+ <string name="progress_importing">importing...</string>
+ <string name="progress_exporting">exporting...</string>
+ <string name="progress_generating">generating key, this can take a while...</string>
+ <string name="progress_buildingKey">building key...</string>
+ <string name="progress_preparingMasterKey">preparing master key...</string>
+ <string name="progress_certifyingMasterKey">certifying master key...</string>
+ <string name="progress_buildingMasterKeyRing">building master key ring...</string>
+ <string name="progress_addingSubKeys">adding sub keys...</string>
+ <string name="progress_savingKeyRing">saving key ring...</string>
+ <string name="progress_importingSecretKeys">importing secret keys...</string>
+ <string name="progress_importingPublicKeys">importing public keys...</string>
+ <string name="progress_reloadingKeys">reloading keys...</string>
+ <string name="progress_exportingKey">exporting key...</string>
+ <string name="progress_exportingKeys">exporting keys...</string>
+ <string name="progress_extractingSignatureKey">extracting signature key...</string>
+ <string name="progress_extractingKey">extracting key...</string>
+ <string name="progress_preparingStreams">preparing streams...</string>
+ <string name="progress_encrypting">encrypting data...</string>
+ <string name="progress_decrypting">decrypting data...</string>
+ <string name="progress_preparingSignature">preparing signature...</string>
+ <string name="progress_generatingSignature">generating signature...</string>
+ <string name="progress_processingSignature">processing signature...</string>
+ <string name="progress_verifyingSignature">verifying signature...</string>
+ <string name="progress_signing">signing...</string>
+ <string name="progress_readingData">reading data...</string>
+ <string name="progress_findingKey">finding key...</string>
+ <string name="progress_decompressingData">decompressing data...</string>
+ <string name="progress_verifyingIntegrity">verifying integrity...</string>
+
+</resources>
+
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
new file mode 100644
index 000000000..5bb4dc2ba
--- /dev/null
+++ b/res/values-ru/strings.xml
@@ -0,0 +1,230 @@
+<?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">Mail Inbox</string>
+ <string name="title_managePublicKeys">Manage Public Keys</string>
+ <string name="title_manageSecretKeys">Manage Secret Keys</string>
+ <string name="title_selectRecipients">Select Recipients</string>
+ <string name="title_selectSignature">Select Signature</string>
+ <string name="title_encrypt">Encrypt</string>
+ <string name="title_decrypt">Decrypt</string>
+ <string name="title_authentification">Authentification</string>
+ <string name="title_createKey">Create Key</string>
+ <string name="title_editKey">Edit Key</string>
+ <string name="title_preferences">Preferences</string>
+ <string name="title_changePassPhrase">Change Pass Phrase</string>
+ <string name="title_setPassPhrase">Set Pass Phrase</string>
+ <string name="title_sendEmail">"Send Mail..."</string>
+ <string name="title_encryptToFile">Encrypt To File</string>
+ <string name="title_decryptToFile">Decrypt To File</string>
+ <string name="title_addAccount">Add Account</string>
+ <string name="title_importKeys">Import Keys</string>
+ <string name="title_exportKey">Export Key</string>
+ <string name="title_exportKeys">Export Keys</string>
+
+ <!-- section_lowerCase: capitalized words, no punctuation -->
+ <string name="section_userIds">User IDs</string>
+ <string name="section_keys">Keys</string>
+ <string name="section_defaults">Defaults</string>
+
+ <!-- btn_lowerCase: capitalized words, no punctuation -->
+ <string name="btn_encryptToClipboard">Encrypt To Clipboard</string>
+ <string name="btn_send">Encrypt And Email</string>
+ <string name="btn_encrypt">Encrypt</string>
+ <string name="btn_decrypt">Decrypt</string>
+ <string name="btn_verify">Verify</string>
+ <string name="btn_selectEncryptKeys">Select Recipients</string>
+ <string name="btn_reply">Reply</string>
+ <string name="btn_encryptMessage">Encrypt Message</string>
+ <string name="btn_decryptMessage">Decrypt Message</string>
+ <string name="btn_encryptFile">Encrypt File</string>
+ <string name="btn_decryptFile">Decrypt File</string>
+ <string name="btn_save">Save</string>
+ <string name="btn_doNotSave">Cancel</string>
+ <string name="btn_delete">Delete</string>
+ <string name="btn_noDate">None</string>
+
+ <!-- menu_lowerCase: capitalized words, no punctuation -->
+ <string name="menu_about">About</string>
+ <string name="menu_addAccount">Add GMail Account</string>
+ <string name="menu_deleteAccount">Delete Account</string>
+ <string name="menu_managePublicKeys">Manage Public Keys</string>
+ <string name="menu_manageSecretKeys">Manage Secret Keys</string>
+ <string name="menu_preferences">Settings</string>
+ <string name="menu_changePassPhrase">Change Pass Phrase</string>
+ <string name="menu_setPassPhrase">Set Pass Phrase</string>
+ <string name="menu_importKeys">Import Keys</string>
+ <string name="menu_exportKeys">Export Keys</string>
+ <string name="menu_exportKey">Export Key</string>
+ <string name="menu_deleteKey">Delete Key</string>
+ <string name="menu_createKey">Create Key</string>
+ <string name="menu_editKey">Edit Key</string>
+
+ <!-- label_lowerCase: capitalized words, no punctuation -->
+ <string name="label_sign">Sign</string>
+ <string name="label_message">Message</string>
+ <string name="label_file">File</string>
+ <string name="label_passPhrase">Pass Phrase</string>
+ <string name="label_passPhraseAgain">Again</string>
+ <string name="label_algorithm">Algorithm</string>
+ <string name="label_asciiArmour">ASCII Armour</string>
+ <string name="label_selectPublicKeys">Public Key(s)</string>
+ <string name="label_deleteAfterEncryption">Delete After Encryption</string>
+ <string name="label_deleteAfterDecryption">Delete After Decryption</string>
+ <string name="label_encryptionAlgorithm">Encryption Algorithm</string>
+ <string name="label_hashAlgorithm">Hash Algorithm</string>
+ <string name="label_asymmetric">Public Key</string>
+ <string name="label_symmetric">Pass Phrase</string>
+
+ <string name="noKeysSelected">Select</string>
+ <string name="oneKeySelected">1 Selected</string>
+ <string name="nKeysSelected">Selected</string>
+ <string name="unknownUserId">&lt;unknown&gt;</string>
+ <string name="none">&lt;none&gt;</string>
+ <string name="noKey">&lt;no key&gt;</string>
+ <string name="noDate">-</string>
+ <string name="noExpiry">&lt;no expiry&gt;</string>
+ <string name="unknownStatus"></string>
+ <string name="canEncrypt">can encrypt</string>
+ <string name="canSign">can sign</string>
+ <string name="expired">expired</string>
+ <string name="notValid">not valid</string>
+
+ <!-- choice_lowerCase: capitalized firwst word, no punctuation -->
+ <string name="choice_signOnly">Sign only</string>
+ <string name="choice_encryptOnly">Encrypt only</string>
+ <string name="choice_signAndEncrypt">Sign and Encrypt</string>
+
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+
+ <string name="filemanager_titleOpen">Open...</string>
+ <string name="filemanager_titleSave">Save As...</string>
+ <string name="filemanager_titleEncrypt">Select File To Encrypt...</string>
+ <string name="filemanager_titleDecrypt">Select File To Decrypt...</string>
+ <string name="filemanager_btnOpen">Open</string>
+ <string name="filemanager_btnSave">Save</string>
+
+ <string name="warning">Warning</string>
+ <string name="error">Error</string>
+ <string name="warningMessage">Warning: %s</string>
+ <string name="errorMessage">Error: %s</string>
+
+ <!-- sentences -->
+ <string name="wrongPassPhrase">Wrong pass phrase.</string>
+ <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="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>
+ <string name="passPhraseFor">Pass phrase for %s:</string>
+ <string name="fileDeleteConfirmation">Are you sure you want to delete\n%s?</string>
+ <string name="fileDeleteSuccessful">Successfully deleted.</string>
+ <string name="noFileSelected">Select a file first.</string>
+ <string name="decryptionSuccessful">Successfully decrypted.</string>
+ <string name="encryptionSuccessful">Successfully encrypted.</string>
+ <string name="encryptionToClipboardSuccessful">Successfully encrypted to clipboard.</string>
+ <string name="enterPassPhraseTwice">Enter the pass phrase twice.</string>
+ <string name="selectEncryptionKey">Select at least one encryption key.</string>
+ <string name="selectEncryptionOrSignatureKey">Select at least one encryption key or a signature key.</string>
+ <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="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="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>
+
+ <!-- error_lowerCase: phrases, no punctuation, all lowercase,
+ they will be put after "errorMessage", e.g. "Error: file not found" -->
+ <string name="error_fileDeleteFailed">deleting '%s' failed</string>
+ <string name="error_fileNotFound">file not found</string>
+ <string name="error_noSecretKeyFound">no suitable secret key found</string>
+ <string name="error_noKnownEncryptionFound">no known kind of encryption found</string>
+ <string name="error_externalStorageNotReady">external storage not ready</string>
+ <string name="error_accountNotFound">account '%s' not found</string>
+ <string name="error_addingAccountFailed">adding account '%s' failed</string>
+ <string name="error_invalidEmail">invalid email '%s'</string>
+ <string name="error_keySizeMinimum512bit">key size must be at least 512bit</string>
+ <string name="error_masterKeyMustNotBeElGamal">the master key cannot be an ElGamal key</string>
+ <string name="error_unknownAlgorithmChoice">unknown algorithm choice</string>
+ <string name="error_userIdNeedsAName">you need to specify a name</string>
+ <string name="error_userIdNeedsAnEmailAddress">you need to specify an email address</string>
+ <string name="error_keyNeedsAUserId">need at least one user id</string>
+ <string name="error_mainUserIdMustNotBeEmpty">main user id must not be empty</string>
+ <string name="error_keyNeedsMasterKey">need at least a master key</string>
+ <string name="error_expiryMustComeAfterCreation">expiry date must come after creation date</string>
+ <string name="error_noEncryptionKeysOrPassPhrase">no encryption key(s) or pass phrase given</string>
+ <string name="error_signatureFailed">signature failed</string>
+ <string name="error_noSignaturePassPhrase">no pass phrase given</string>
+ <string name="error_noSignatureKey">no signature key given</string>
+ <string name="error_invalidData">not valid encryption data</string>
+ <string name="error_corruptData">corrupt data</string>
+ <string name="error_noSymmetricEncryptionPacket">couldn't find a packet with symmetric encryption</string>
+ <string name="error_wrongPassPhrase">wrong pass phrase</string>
+ <string name="error_savingKeys">error saving some key(s)</string>
+
+ <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
+ <string name="progress_done">done.</string>
+ <string name="progress_initializing">initializing...</string>
+ <string name="progress_saving">saving...</string>
+ <string name="progress_importing">importing...</string>
+ <string name="progress_exporting">exporting...</string>
+ <string name="progress_generating">generating key, this can take a while...</string>
+ <string name="progress_buildingKey">building key...</string>
+ <string name="progress_preparingMasterKey">preparing master key...</string>
+ <string name="progress_certifyingMasterKey">certifying master key...</string>
+ <string name="progress_buildingMasterKeyRing">building master key ring...</string>
+ <string name="progress_addingSubKeys">adding sub keys...</string>
+ <string name="progress_savingKeyRing">saving key ring...</string>
+ <string name="progress_importingSecretKeys">importing secret keys...</string>
+ <string name="progress_importingPublicKeys">importing public keys...</string>
+ <string name="progress_reloadingKeys">reloading keys...</string>
+ <string name="progress_exportingKey">exporting key...</string>
+ <string name="progress_exportingKeys">exporting keys...</string>
+ <string name="progress_extractingSignatureKey">extracting signature key...</string>
+ <string name="progress_extractingKey">extracting key...</string>
+ <string name="progress_preparingStreams">preparing streams...</string>
+ <string name="progress_encrypting">encrypting data...</string>
+ <string name="progress_decrypting">decrypting data...</string>
+ <string name="progress_preparingSignature">preparing signature...</string>
+ <string name="progress_generatingSignature">generating signature...</string>
+ <string name="progress_processingSignature">processing signature...</string>
+ <string name="progress_verifyingSignature">verifying signature...</string>
+ <string name="progress_signing">signing...</string>
+ <string name="progress_readingData">reading data...</string>
+ <string name="progress_findingKey">finding key...</string>
+ <string name="progress_decompressingData">decompressing data...</string>
+ <string name="progress_verifyingIntegrity">verifying integrity...</string>
+
+</resources>
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b1fa76915..856ed0c4e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -16,53 +16,226 @@
<resources>
<string name="app_name">APG</string>
+
+ <!-- title_lowerCase: capitalized words, no punctuation -->
<string name="title_mailInbox">Mail Inbox</string>
<string name="title_managePublicKeys">Manage Public Keys</string>
<string name="title_manageSecretKeys">Manage Secret Keys</string>
<string name="title_selectRecipients">Select Recipients</string>
<string name="title_selectSignature">Select Signature</string>
- <string name="title_encryptMessage">Encrypt Message</string>
- <string name="title_decryptMessage">Decrypt Message</string>
+ <string name="title_encrypt">Encrypt</string>
+ <string name="title_decrypt">Decrypt</string>
<string name="title_authentification">Authentification</string>
<string name="title_createKey">Create Key</string>
<string name="title_editKey">Edit Key</string>
+ <string name="title_preferences">Preferences</string>
+ <string name="title_changePassPhrase">Change Pass Phrase</string>
+ <string name="title_setPassPhrase">Set Pass Phrase</string>
+ <string name="title_sendEmail">"Send Mail..."</string>
+ <string name="title_encryptToFile">Encrypt To File</string>
+ <string name="title_decryptToFile">Decrypt To File</string>
+ <string name="title_addAccount">Add Account</string>
+ <string name="title_importKeys">Import Keys</string>
+ <string name="title_exportKey">Export Key</string>
+ <string name="title_exportKeys">Export Keys</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>
- <string name="btn_send">Send via Email</string>
+ <!-- btn_lowerCase: capitalized words, no punctuation -->
+ <string name="btn_encryptToClipboard">Encrypt To Clipboard</string>
+ <string name="btn_send">Encrypt And Email</string>
+ <string name="btn_encrypt">Encrypt</string>
<string name="btn_decrypt">Decrypt</string>
+ <string name="btn_verify">Verify</string>
<string name="btn_selectEncryptKeys">Select Recipients</string>
<string name="btn_reply">Reply</string>
<string name="btn_encryptMessage">Encrypt Message</string>
<string name="btn_decryptMessage">Decrypt Message</string>
+ <string name="btn_encryptFile">Encrypt File</string>
+ <string name="btn_decryptFile">Decrypt File</string>
<string name="btn_save">Save</string>
<string name="btn_doNotSave">Cancel</string>
+ <string name="btn_delete">Delete</string>
+ <string name="btn_noDate">None</string>
+ <!-- menu_lowerCase: capitalized words, no punctuation -->
<string name="menu_about">About</string>
<string name="menu_addAccount">Add GMail Account</string>
+ <string name="menu_deleteAccount">Delete Account</string>
<string name="menu_managePublicKeys">Manage Public Keys</string>
<string name="menu_manageSecretKeys">Manage Secret Keys</string>
+ <string name="menu_preferences">Settings</string>
+ <string name="menu_changePassPhrase">Change Pass Phrase</string>
+ <string name="menu_setPassPhrase">Set Pass Phrase</string>
+ <string name="menu_importKeys">Import Keys</string>
+ <string name="menu_exportKeys">Export Keys</string>
+ <string name="menu_exportKey">Export Key</string>
+ <string name="menu_deleteKey">Delete Key</string>
+ <string name="menu_createKey">Create Key</string>
+ <string name="menu_editKey">Edit Key</string>
+
+ <!-- label_lowerCase: capitalized words, no punctuation -->
+ <string name="label_sign">Sign</string>
+ <string name="label_message">Message</string>
+ <string name="label_file">File</string>
+ <string name="label_passPhrase">Pass Phrase</string>
+ <string name="label_passPhraseAgain">Again</string>
+ <string name="label_algorithm">Algorithm</string>
+ <string name="label_asciiArmour">ASCII Armour</string>
+ <string name="label_selectPublicKeys">Public Key(s)</string>
+ <string name="label_deleteAfterEncryption">Delete After Encryption</string>
+ <string name="label_deleteAfterDecryption">Delete After Decryption</string>
+ <string name="label_encryptionAlgorithm">Encryption Algorithm</string>
+ <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="sign">Sign</string>
- <string name="sign_as">Sign as</string>
- <string name="no_keys_selected">Select Recipients</string>
- <string name="one_key_selected">1 Recipient</string>
- <string name="n_keys_selected">Recipients</string>
- <string name="unknown_user_id">&lt;unknown&gt;</string>
+ <string name="noKeysSelected">Select</string>
+ <string name="oneKeySelected">1 Selected</string>
+ <string name="nKeysSelected">Selected</string>
+ <string name="unknownUserId">&lt;unknown&gt;</string>
<string name="none">&lt;none&gt;</string>
+ <string name="noKey">&lt;no key&gt;</string>
+ <string name="noDate">-</string>
+ <string name="noExpiry">&lt;no expiry&gt;</string>
+ <string name="unknownStatus"></string>
+ <string name="canEncrypt">can encrypt</string>
+ <string name="canSign">can sign</string>
+ <string name="expired">expired</string>
+ <string name="notValid">not valid</string>
- <string name="sign_only">Sign only</string>
- <string name="encrypt_only">Encrypt only</string>
- <string name="sign_and_encrypt">Sign and Encrypt</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>
<string name="rsa">RSA</string>
- <string name="wrong_pass_phrase">Wrong pass phrase.</string>
- <string name="using_clipboard_content">Using clipboard content.</string>
- <string name="key_saved">Key saved.</string>
- <string name="set_a_pass_phrase">Set a pass phrase via the option menu first.</string>
+ <string name="filemanager_titleOpen">Open...</string>
+ <string name="filemanager_titleSave">Save As...</string>
+ <string name="filemanager_titleEncrypt">Select File To Encrypt...</string>
+ <string name="filemanager_titleDecrypt">Select File To Decrypt...</string>
+ <string name="filemanager_btnOpen">Open</string>
+ <string name="filemanager_btnSave">Save</string>
+
+ <string name="warning">Warning</string>
+ <string name="error">Error</string>
+ <string name="warningMessage">Warning: %s</string>
+ <string name="errorMessage">Error: %s</string>
+
+ <!-- sentences -->
+ <string name="wrongPassPhrase">Wrong pass phrase.</string>
+ <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="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>
+ <string name="passPhraseFor">Pass phrase for %s:</string>
+ <string name="fileDeleteConfirmation">Are you sure you want to delete\n%s?</string>
+ <string name="fileDeleteSuccessful">Successfully deleted.</string>
+ <string name="noFileSelected">Select a file first.</string>
+ <string name="decryptionSuccessful">Successfully decrypted.</string>
+ <string name="encryptionSuccessful">Successfully encrypted.</string>
+ <string name="encryptionToClipboardSuccessful">Successfully encrypted to clipboard.</string>
+ <string name="enterPassPhraseTwice">Enter the pass phrase twice.</string>
+ <string name="selectEncryptionKey">Select at least one encryption key.</string>
+ <string name="selectEncryptionOrSignatureKey">Select at least one encryption key or a signature key.</string>
+ <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="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="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>
+
+ <!-- error_lowerCase: phrases, no punctuation, all lowercase,
+ they will be put after "errorMessage", e.g. "Error: file not found" -->
+ <string name="error_fileDeleteFailed">deleting '%s' failed</string>
+ <string name="error_fileNotFound">file not found</string>
+ <string name="error_noSecretKeyFound">no suitable secret key found</string>
+ <string name="error_noKnownEncryptionFound">no known kind of encryption found</string>
+ <string name="error_externalStorageNotReady">external storage not ready</string>
+ <string name="error_accountNotFound">account '%s' not found</string>
+ <string name="error_addingAccountFailed">adding account '%s' failed</string>
+ <string name="error_invalidEmail">invalid email '%s'</string>
+ <string name="error_keySizeMinimum512bit">key size must be at least 512bit</string>
+ <string name="error_masterKeyMustNotBeElGamal">the master key cannot be an ElGamal key</string>
+ <string name="error_unknownAlgorithmChoice">unknown algorithm choice</string>
+ <string name="error_userIdNeedsAName">you need to specify a name</string>
+ <string name="error_userIdNeedsAnEmailAddress">you need to specify an email address</string>
+ <string name="error_keyNeedsAUserId">need at least one user id</string>
+ <string name="error_mainUserIdMustNotBeEmpty">main user id must not be empty</string>
+ <string name="error_keyNeedsMasterKey">need at least a master key</string>
+ <string name="error_expiryMustComeAfterCreation">expiry date must come after creation date</string>
+ <string name="error_noEncryptionKeysOrPassPhrase">no encryption key(s) or pass phrase given</string>
+ <string name="error_signatureFailed">signature failed</string>
+ <string name="error_noSignaturePassPhrase">no pass phrase given</string>
+ <string name="error_noSignatureKey">no signature key given</string>
+ <string name="error_invalidData">not valid encryption data</string>
+ <string name="error_corruptData">corrupt data</string>
+ <string name="error_noSymmetricEncryptionPacket">couldn't find a packet with symmetric encryption</string>
+ <string name="error_wrongPassPhrase">wrong pass phrase</string>
+ <string name="error_savingKeys">error saving some key(s)</string>
+
+ <!-- progress_lowerCase: lowercase, phrases, usually ending in '...' -->
+ <string name="progress_done">done.</string>
+ <string name="progress_initializing">initializing...</string>
+ <string name="progress_saving">saving...</string>
+ <string name="progress_importing">importing...</string>
+ <string name="progress_exporting">exporting...</string>
+ <string name="progress_generating">generating key, this can take a while...</string>
+ <string name="progress_buildingKey">building key...</string>
+ <string name="progress_preparingMasterKey">preparing master key...</string>
+ <string name="progress_certifyingMasterKey">certifying master key...</string>
+ <string name="progress_buildingMasterKeyRing">building master key ring...</string>
+ <string name="progress_addingSubKeys">adding sub keys...</string>
+ <string name="progress_savingKeyRing">saving key ring...</string>
+ <string name="progress_importingSecretKeys">importing secret keys...</string>
+ <string name="progress_importingPublicKeys">importing public keys...</string>
+ <string name="progress_reloadingKeys">reloading keys...</string>
+ <string name="progress_exportingKey">exporting key...</string>
+ <string name="progress_exportingKeys">exporting keys...</string>
+ <string name="progress_extractingSignatureKey">extracting signature key...</string>
+ <string name="progress_extractingKey">extracting key...</string>
+ <string name="progress_preparingStreams">preparing streams...</string>
+ <string name="progress_encrypting">encrypting data...</string>
+ <string name="progress_decrypting">decrypting data...</string>
+ <string name="progress_preparingSignature">preparing signature...</string>
+ <string name="progress_generatingSignature">generating signature...</string>
+ <string name="progress_processingSignature">processing signature...</string>
+ <string name="progress_verifyingSignature">verifying signature...</string>
+ <string name="progress_signing">signing...</string>
+ <string name="progress_readingData">reading data...</string>
+ <string name="progress_findingKey">finding key...</string>
+ <string name="progress_decompressingData">decompressing data...</string>
+ <string name="progress_verifyingIntegrity">verifying integrity...</string>
+
</resources>
diff --git a/src/org/openintents/intents/FileManager.java b/src/org/openintents/intents/FileManager.java
new file mode 100644
index 000000000..3a5cc0d86
--- /dev/null
+++ b/src/org/openintents/intents/FileManager.java
@@ -0,0 +1,78 @@
+/*
+ * 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 68c4f8877..e290a501e 100644
--- a/src/org/thialfihar/android/apg/Apg.java
+++ b/src/org/thialfihar/android/apg/Apg.java
@@ -16,7 +16,8 @@
package org.thialfihar.android.apg;
-import java.io.BufferedOutputStream;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -39,9 +40,12 @@ import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
import java.util.Vector;
import java.util.regex.Pattern;
+import org.bouncycastle2.bcpg.ArmoredInputStream;
import org.bouncycastle2.bcpg.ArmoredOutputStream;
import org.bouncycastle2.bcpg.BCPGOutputStream;
import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
@@ -63,6 +67,7 @@ import org.bouncycastle2.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle2.openpgp.PGPObjectFactory;
import org.bouncycastle2.openpgp.PGPOnePassSignature;
import org.bouncycastle2.openpgp.PGPOnePassSignatureList;
+import org.bouncycastle2.openpgp.PGPPBEEncryptedData;
import org.bouncycastle2.openpgp.PGPPrivateKey;
import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyEncryptedData;
@@ -95,9 +100,11 @@ public class Apg {
public static class Intent {
public static final String DECRYPT = "org.thialfihar.android.apg.intent.DECRYPT";
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.8.0";
+ public static String VERSION = "0.9.5";
public static String FULL_VERSION = "APG v" + VERSION;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS =
@@ -118,22 +125,18 @@ public class Apg {
CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP };
- protected static Vector<PGPPublicKeyRing> mPublicKeyRings;
- protected static Vector<PGPSecretKeyRing> mSecretKeyRings;
+ 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-----\n.*?-----END PGP MESSAGE-----).*",
+ Pattern.compile(".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*",
Pattern.DOTALL);
- protected static boolean mInitialized = false;
-
- protected static final int RETURN_NO_MASTER_KEY = -2;
- protected static final int RETURN_ERROR = -1;
- protected static final int RETURN_OK = 0;
- protected static final int RETURN_UPDATED = 1;
+ public static Pattern PGP_SIGNED_MESSAGE =
+ Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
+ Pattern.DOTALL);
- protected static final int TYPE_PUBLIC = 0;
- protected static final int TYPE_SECRET = 1;
+ protected static boolean mInitialized = false;
protected static HashMap<Long, Integer> mSecretKeyIdToIdMap;
protected static HashMap<Long, PGPSecretKeyRing> mSecretKeyIdToKeyRingMap;
@@ -153,7 +156,9 @@ public class Apg {
PublicKeys.KEY_DATA,
PublicKeys.WHO_ID, };
- private static String mPassPhrase = null;
+ private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
+ new HashMap<Long, CachedPassPhrase>();
+ private static String mEditPassPhrase = null;
public static class GeneralException extends Exception {
static final long serialVersionUID = 0xf812773342L;
@@ -163,6 +168,14 @@ public class Apg {
}
}
+ public static class NoAsymmetricEncryptionException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public NoAsymmetricEncryptionException() {
+ super();
+ }
+ }
+
static {
mPublicKeyRings = new Vector<PGPPublicKeyRing>();
mSecretKeyRings = new Vector<PGPSecretKeyRing>();
@@ -173,13 +186,20 @@ public class Apg {
}
public static void initialize(Activity context) {
- setPassPhrase(null);
if (mInitialized) {
return;
}
- loadKeyRings(context, TYPE_PUBLIC);
- loadKeyRings(context, TYPE_SECRET);
+ 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;
}
@@ -254,24 +274,63 @@ public class Apg {
}
}
- public static void setPassPhrase(String passPhrase) {
- mPassPhrase = passPhrase;
+ public static void setEditPassPhrase(String passPhrase) {
+ mEditPassPhrase = passPhrase;
+ }
+
+ public static String getEditPassPhrase() {
+ return mEditPassPhrase;
}
- public static String getPassPhrase() {
- return mPassPhrase;
+ public static void setCachedPassPhrase(long keyId, String passPhrase) {
+ mPassPhraseCache.put(keyId, new CachedPassPhrase(new Date().getTime(), passPhrase));
}
- public static PGPSecretKey createKey(KeyEditor.AlgorithmChoice algorithmChoice, int keySize,
- String passPhrase)
+ public static String getCachedPassPhrase(long keyId) {
+ long realId = keyId;
+ if (realId != Id.key.symmetric) {
+ PGPSecretKeyRing keyRing = findSecretKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
+ }
+ PGPSecretKey masterKey = getMasterKey(keyRing);
+ if (masterKey == null) {
+ return null;
+ }
+ realId = masterKey.getKeyID();
+ }
+ CachedPassPhrase cpp = mPassPhraseCache.get(realId);
+ if (cpp == null) {
+ return null;
+ }
+ // set it again to reset the cache life cycle
+ setCachedPassPhrase(realId, cpp.passPhrase);
+ return cpp.passPhrase;
+ }
+
+ public static void cleanUpCache(int ttl) {
+ 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) {
+ oldKeys.add(pair.getKey());
+ }
+ }
+
+ for (long keyId : oldKeys) {
+ mPassPhraseCache.remove(keyId);
+ }
+ }
+
+ public static PGPSecretKey createKey(Context context,
+ int algorithmChoice, int keySize, String passPhrase,
+ PGPSecretKey masterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
GeneralException, InvalidAlgorithmParameterException {
- if (algorithmChoice == null) {
- throw new GeneralException("unknown algorithm choice");
- }
if (keySize < 512) {
- throw new GeneralException("key size must be at least 512bit");
+ throw new GeneralException(context.getString(R.string.error_keySizeMinimum512bit));
}
Security.addProvider(new BouncyCastleProvider());
@@ -283,34 +342,30 @@ public class Apg {
int algorithm = 0;
KeyPairGenerator keyGen = null;
- switch (algorithmChoice.getId()) {
- case KeyEditor.AlgorithmChoice.DSA: {
+ switch (algorithmChoice) {
+ case Id.choice.algorithm.dsa: {
keyGen = KeyPairGenerator.getInstance("DSA", new BouncyCastleProvider());
keyGen.initialize(keySize, new SecureRandom());
algorithm = PGPPublicKey.DSA;
break;
}
- case KeyEditor.AlgorithmChoice.ELGAMAL: {
- if (keySize != 2048) {
- throw new GeneralException("ElGamal currently requires 2048bit");
+ case Id.choice.algorithm.elgamal: {
+ if (masterKey == null) {
+ throw new GeneralException(context.getString(R.string.error_masterKeyMustNotBeElGamal));
}
keyGen = KeyPairGenerator.getInstance("ELGAMAL", new BouncyCastleProvider());
- BigInteger p = new BigInteger(
- "36F0255DDE973DCB3B399D747F23E32ED6FDB1F77598338BFDF44159C4EC64DDAEB5F78671CBFB22" +
- "106AE64C32C5BCE4CFD4F5920DA0EBC8B01ECA9292AE3DBA1B7A4A899DA181390BB3BD1659C81294" +
- "F400A3490BF9481211C79404A576605A5160DBEE83B4E019B6D799AE131BA4C23DFF83475E9C40FA" +
- "6725B7C9E3AA2C6596E9C05702DB30A07C9AA2DC235C5269E39D0CA9DF7AAD44612AD6F88F696992" +
- "98F3CAB1B54367FB0E8B93F735E7DE83CD6FA1B9D1C931C41C6188D3E7F179FC64D87C5D13F85D70" +
- "4A3AA20F90B3AD3621D434096AA7E8E7C66AB683156A951AEA2DD9E76705FAEFEA8D71A575535597" +
- "0000000000000001", 16);
- ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, new BigInteger("2"));
+ BigInteger p = Primes.getBestPrime(keySize);
+ BigInteger g = new BigInteger("2");
+
+ ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
+
keyGen.initialize(elParams);
- algorithm = PGPPublicKey.ELGAMAL_GENERAL;
+ algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
break;
}
- case KeyEditor.AlgorithmChoice.RSA: {
+ case Id.choice.algorithm.rsa: {
keyGen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
keyGen.initialize(keySize, new SecureRandom());
@@ -319,17 +374,45 @@ public class Apg {
}
default: {
- throw new GeneralException("unknown algorithm choice");
+ throw new GeneralException(context.getString(R.string.error_unknownAlgorithmChoice));
}
}
PGPKeyPair keyPair = new PGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
- // enough for now, as we assemble the key again later anyway
- PGPSecretKey secretKey =
- new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, "",
- PGPEncryptedData.CAST5, passPhrase.toCharArray(), null, null,
- new SecureRandom(), new BouncyCastleProvider().getName());
+ PGPSecretKey secretKey = null;
+ if (masterKey == null) {
+ // enough for now, as we assemble the key again later anyway
+ secretKey = new PGPSecretKey(PGPSignature.DEFAULT_CERTIFICATION, keyPair, "",
+ PGPEncryptedData.CAST5, passPhrase.toCharArray(),
+ null, null,
+ new SecureRandom(), new BouncyCastleProvider().getName());
+
+ } else {
+ PGPPublicKey tmpKey = masterKey.getPublicKey();
+ PGPPublicKey masterPublicKey =
+ new PGPPublicKey(tmpKey.getAlgorithm(),
+ tmpKey.getKey(new BouncyCastleProvider()),
+ tmpKey.getCreationTime());
+ PGPPrivateKey masterPrivateKey =
+ masterKey.extractPrivateKey(passPhrase.toCharArray(),
+ new BouncyCastleProvider());
+
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
+ PGPKeyRingGenerator ringGen =
+ new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, "",
+ PGPEncryptedData.CAST5, passPhrase.toCharArray(),
+ null, null,
+ new SecureRandom(), new BouncyCastleProvider().getName());
+ ringGen.addSubKey(keyPair);
+ PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing();
+ Iterator it = secKeyRing.getSecretKeys();
+ // first one is the master key
+ it.next();
+ secretKey = (PGPSecretKey) it.next();
+ }
+
return secretKey;
}
@@ -353,7 +436,7 @@ public class Apg {
throws Apg.GeneralException, NoSuchProviderException, PGPException,
NoSuchAlgorithmException, SignatureException {
- progress.setProgress("building key...", 0, 100);
+ progress.setProgress(R.string.progress_buildingKey, 0, 100);
Security.addProvider(new BouncyCastleProvider());
@@ -378,9 +461,9 @@ public class Apg {
try {
userId = editor.getValue();
} catch (UserIdEditor.NoNameException e) {
- throw new Apg.GeneralException("you need to specify a name");
+ throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAName));
} catch (UserIdEditor.NoEmailException e) {
- throw new Apg.GeneralException("you need to specify an email");
+ throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAnEmailAddress));
} catch (UserIdEditor.InvalidEmailException e) {
throw new Apg.GeneralException(e.getMessage());
}
@@ -398,15 +481,15 @@ public class Apg {
}
if (userIds.size() == 0) {
- throw new Apg.GeneralException("need at least one user id");
+ throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsAUserId));
}
if (!gotMainUserId) {
- throw new Apg.GeneralException("main user id can't be empty");
+ throw new Apg.GeneralException(context.getString(R.string.error_mainUserIdMustNotBeEmpty));
}
if (keyEditors.getChildCount() == 0) {
- throw new Apg.GeneralException("need at least a main key");
+ throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsMasterKey));
}
for (int i = 0; i < keyEditors.getChildCount(); ++i) {
@@ -414,13 +497,13 @@ public class Apg {
keys.add(editor.getValue());
}
- progress.setProgress("preparing master key...", 10, 100);
+ progress.setProgress(R.string.progress_preparingMasterKey, 10, 100);
KeyEditor keyEditor = (KeyEditor) keyEditors.getChildAt(0);
- int usageId = keyEditor.getUsage().getId();
- boolean canSign = (usageId == KeyEditor.UsageChoice.SIGN_ONLY ||
- usageId == KeyEditor.UsageChoice.SIGN_AND_ENCRYPT);
- boolean canEncrypt = (usageId == KeyEditor.UsageChoice.ENCRYPT_ONLY ||
- usageId == KeyEditor.UsageChoice.SIGN_AND_ENCRYPT);
+ int usageId = keyEditor.getUsage();
+ boolean canSign = (usageId == Id.choice.usage.sign_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
+ boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
String mainUserId = userIds.get(0);
@@ -434,7 +517,7 @@ public class Apg {
masterKey.extractPrivateKey(oldPassPhrase.toCharArray(),
new BouncyCastleProvider());
- progress.setProgress("certifying master key...", 20, 100);
+ progress.setProgress(R.string.progress_certifyingMasterKey, 20, 100);
for (int i = 0; i < userIds.size(); ++i) {
String userId = userIds.get(i);
@@ -472,20 +555,20 @@ public class Apg {
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
long numDays = getNumDatesBetween(creationDate, expiryDate);
if (numDays <= 0) {
- throw new GeneralException("expiry date must be later than creation date");
+ throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
}
hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
}
- progress.setProgress("building master key ring...", 30, 100);
+ progress.setProgress(R.string.progress_buildingMasterKeyRing, 30, 100);
PGPKeyRingGenerator keyGen =
- new PGPKeyRingGenerator(PGPSignature.DEFAULT_CERTIFICATION,
+ new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
masterKeyPair, mainUserId,
PGPEncryptedData.CAST5, newPassPhrase.toCharArray(),
hashedPacketsGen.generate(), unhashedPacketsGen.generate(),
new SecureRandom(), new BouncyCastleProvider().getName());
- progress.setProgress("adding sub keys...", 40, 100);
+ progress.setProgress(R.string.progress_addingSubKeys, 40, 100);
for (int i = 1; i < keys.size(); ++i) {
progress.setProgress(40 + 50 * (i - 1)/ (keys.size() - 1), 100);
PGPSecretKey subKey = keys.get(i);
@@ -504,11 +587,11 @@ public class Apg {
unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
keyFlags = 0;
- usageId = keyEditor.getUsage().getId();
- canSign = (usageId == KeyEditor.UsageChoice.SIGN_ONLY ||
- usageId == KeyEditor.UsageChoice.SIGN_AND_ENCRYPT);
- canEncrypt = (usageId == KeyEditor.UsageChoice.ENCRYPT_ONLY ||
- usageId == KeyEditor.UsageChoice.SIGN_AND_ENCRYPT);
+ usageId = keyEditor.getUsage();
+ canSign = (usageId == Id.choice.usage.sign_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
+ canEncrypt = (usageId == Id.choice.usage.encrypt_only ||
+ usageId == Id.choice.usage.sign_and_encrypt);
if (canSign) {
keyFlags |= KeyFlags.SIGN_DATA;
}
@@ -523,7 +606,7 @@ public class Apg {
GregorianCalendar expiryDate = keyEditor.getExpiryDate();
long numDays = getNumDatesBetween(creationDate, expiryDate);
if (numDays <= 0) {
- throw new GeneralException("expiry date must be later than creation date");
+ throw new GeneralException(context.getString(R.string.error_expiryMustComeAfterCreation));
}
hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
}
@@ -535,13 +618,13 @@ public class Apg {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- progress.setProgress("saving key ring...", 90, 100);
+ progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
saveKeyRing(context, secretKeyRing);
saveKeyRing(context, publicKeyRing);
- loadKeyRings(context, TYPE_PUBLIC);
- loadKeyRings(context, TYPE_SECRET);
- progress.setProgress("done.", 100, 100);
+ 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) {
@@ -550,14 +633,14 @@ public class Apg {
PGPPublicKey masterKey = getMasterKey(keyRing);
if (masterKey == null) {
- return RETURN_NO_MASTER_KEY;
+ return Id.return_value.no_master_key;
}
try {
keyRing.encode(out);
out.close();
} catch (IOException e) {
- return RETURN_ERROR;
+ return Id.return_value.error;
}
values.put(PublicKeys.KEY_ID, masterKey.getKeyID());
@@ -567,10 +650,10 @@ public class Apg {
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 RETURN_UPDATED;
+ return Id.return_value.updated;
} else {
context.getContentResolver().insert(PublicKeys.CONTENT_URI, values);
- return RETURN_OK;
+ return Id.return_value.ok;
}
}
@@ -580,14 +663,14 @@ public class Apg {
PGPSecretKey masterKey = getMasterKey(keyRing);
if (masterKey == null) {
- return RETURN_NO_MASTER_KEY;
+ return Id.return_value.no_master_key;
}
try {
keyRing.encode(out);
out.close();
} catch (IOException e) {
- return RETURN_ERROR;
+ return Id.return_value.error;
}
values.put(SecretKeys.KEY_ID, masterKey.getKeyID());
@@ -597,10 +680,10 @@ public class Apg {
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 RETURN_UPDATED;
+ return Id.return_value.updated;
} else {
context.getContentResolver().insert(SecretKeys.CONTENT_URI, values);
- return RETURN_OK;
+ return Id.return_value.ok;
}
}
@@ -608,27 +691,27 @@ public class Apg {
ProgressDialogUpdater progress)
throws GeneralException, FileNotFoundException, PGPException, IOException {
Bundle returnData = new Bundle();
- PGPObjectFactory objectFactors = null;
+ PGPObjectFactory objectFactory = null;
- if (type == TYPE_SECRET) {
- progress.setProgress("importing secret keys...", 0, 100);
+ if (type == Id.type.secret_key) {
+ progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
} else {
- progress.setProgress("importing public keys...", 0, 100);
+ progress.setProgress(R.string.progress_importingPublicKeys, 0, 100);
}
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException("external storage not ready");
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
}
FileInputStream fileIn = new FileInputStream(filename);
InputStream in = PGPUtil.getDecoderStream(fileIn);
- objectFactors = new PGPObjectFactory(in);
+ objectFactory = new PGPObjectFactory(in);
Vector<Object> objects = new Vector<Object>();
- Object obj = objectFactors.nextObject();
+ Object obj = objectFactory.nextObject();
while (obj != null) {
objects.add(obj);
- obj = objectFactors.nextObject();
+ obj = objectFactory.nextObject();
}
int newKeys = 0;
@@ -640,7 +723,7 @@ public class Apg {
PGPSecretKeyRing secretKeyRing;
int retValue;
- if (type == TYPE_SECRET) {
+ if (type == Id.type.secret_key) {
if (!(obj instanceof PGPSecretKeyRing)) {
continue;
}
@@ -654,24 +737,24 @@ public class Apg {
retValue = saveKeyRing(context, publicKeyRing);
}
- if (retValue == RETURN_ERROR) {
- throw new GeneralException("error saving some key(s)");
+ if (retValue == Id.return_value.error) {
+ throw new GeneralException(context.getString(R.string.error_savingKeys));
}
- if (retValue == RETURN_UPDATED) {
+ if (retValue == Id.return_value.updated) {
++oldKeys;
- } else if (retValue == RETURN_OK) {
+ } else if (retValue == Id.return_value.ok) {
++newKeys;
}
}
- progress.setProgress("reloading keys...", 100, 100);
+ progress.setProgress(R.string.progress_reloadingKeys, 100, 100);
loadKeyRings(context, type);
returnData.putInt("added", newKeys);
returnData.putInt("updated", oldKeys);
- progress.setProgress("done.", 100, 100);
+ progress.setProgress(R.string.progress_done, 100, 100);
return returnData;
}
@@ -682,13 +765,13 @@ public class Apg {
Bundle returnData = new Bundle();
if (keys.size() == 1) {
- progress.setProgress("exporting key...", 0, 100);
+ progress.setProgress(R.string.progress_exportingKey, 0, 100);
} else {
- progress.setProgress("exporting keys...", 0, 100);
+ progress.setProgress(R.string.progress_exportingKeys, 0, 100);
}
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException("external storage not ready");
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
}
FileOutputStream fileOut = new FileOutputStream(new File(filename), false);
ArmoredOutputStream out = new ArmoredOutputStream(fileOut);
@@ -715,14 +798,14 @@ public class Apg {
fileOut.close();
returnData.putInt("exported", numKeys);
- progress.setProgress("done.", 100, 100);
+ progress.setProgress(R.string.progress_done, 100, 100);
return returnData;
}
private static void loadKeyRings(Activity context, int type) {
Cursor cursor;
- if (type == TYPE_SECRET) {
+ if (type == Id.type.secret_key) {
mSecretKeyRings.clear();
mSecretKeyIdToIdMap.clear();
mSecretKeyIdToKeyRingMap.clear();
@@ -750,7 +833,7 @@ public class Apg {
long keyId = cursor.getLong(keyIdIndex);
try {
- if (type == TYPE_SECRET) {
+ if (type == Id.type.secret_key) {
PGPSecretKeyRing key = new PGPSecretKeyRing(keyData);
mSecretKeyRings.add(key);
mSecretKeyIdToIdMap.put(keyId, id);
@@ -768,7 +851,7 @@ public class Apg {
}
}
- if (type == TYPE_SECRET) {
+ if (type == Id.type.secret_key) {
Collections.sort(mSecretKeyRings, new SecretKeySorter());
} else {
Collections.sort(mPublicKeyRings, new PublicKeySorter());
@@ -939,7 +1022,7 @@ public class Apg {
public static String getMainUserIdSafe(Context context, PGPPublicKey key) {
String userId = getMainUserId(key);
if (userId == null) {
- userId = context.getResources().getString(R.string.unknown_user_id);
+ userId = context.getResources().getString(R.string.unknownUserId);
}
return userId;
}
@@ -947,7 +1030,7 @@ public class Apg {
public static String getMainUserIdSafe(Context context, PGPSecretKey key) {
String userId = getMainUserId(key);
if (userId == null) {
- userId = context.getResources().getString(R.string.unknown_user_id);
+ userId = context.getResources().getString(R.string.unknownUserId);
}
return userId;
}
@@ -970,19 +1053,31 @@ public class Apg {
return key.isEncryptionKey();
}
- // special case, this algorithm, no need to look further
+ // special cases
if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
return true;
}
+ if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
+ return true;
+ }
+
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (!key.isMasterKey() || sig.getKeyID() == key.getKeyID()) {
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
- if ((hashed.getKeyFlags() &
- (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
- return true;
- }
+ if (hashed != null &&(hashed.getKeyFlags() &
+ (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null &&(unhashed.getKeyFlags() &
+ (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
+ return true;
}
}
return false;
@@ -997,13 +1092,25 @@ public class Apg {
return true;
}
+ // special case
+ if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
+ return true;
+ }
+
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (!key.isMasterKey() || sig.getKeyID() == key.getKeyID()) {
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
- if ((hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
- return true;
- }
+ if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
+ return true;
}
}
@@ -1052,14 +1159,14 @@ public class Apg {
PGPPublicKey masterKey = getMasterKey(keyRing);
Uri uri = Uri.withAppendedPath(PublicKeys.CONTENT_URI_BY_KEY_ID, "" + masterKey.getKeyID());
context.getContentResolver().delete(uri, null, null);
- loadKeyRings(context, TYPE_PUBLIC);
+ loadKeyRings(context, Id.type.public_key);
}
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, TYPE_SECRET);
+ loadKeyRings(context, Id.type.secret_key);
}
public static PGPPublicKey findPublicKey(long keyId) {
@@ -1134,59 +1241,67 @@ public class Apg {
return null;
}
- public static void encrypt(InputStream inStream, OutputStream outStream,
+ public static void encrypt(Context context,
+ InputStream inStream, OutputStream outStream,
+ long dataLength,
+ boolean armored,
long encryptionKeyIds[], long signatureKeyId,
String signaturePassPhrase,
- ProgressDialogUpdater progress)
+ ProgressDialogUpdater progress,
+ int symmetricAlgorithm, int hashAlgorithm, int compression,
+ String passPhrase)
throws IOException, GeneralException, PGPException, NoSuchProviderException,
NoSuchAlgorithmException, SignatureException {
Security.addProvider(new BouncyCastleProvider());
- ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
- armorOut.setHeader("Version", FULL_VERSION);
- OutputStream out = armorOut;
- OutputStream encryptOut = null;
+ if (encryptionKeyIds == null) {
+ encryptionKeyIds = new long[0];
+ }
+ ArmoredOutputStream armorOut = null;
+ OutputStream out = null;
+ OutputStream encryptOut = null;
+ if (armored) {
+ armorOut = new ArmoredOutputStream(outStream);
+ armorOut.setHeader("Version", FULL_VERSION);
+ out = armorOut;
+ } else {
+ out = outStream;
+ }
PGPSecretKey signingKey = null;
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
- if (encryptionKeyIds == null || encryptionKeyIds.length == 0) {
- throw new GeneralException("no encryption key(s) given");
+ if (encryptionKeyIds.length == 0 && passPhrase == null) {
+ throw new GeneralException(context.getString(R.string.error_noEncryptionKeysOrPassPhrase));
}
if (signatureKeyId != 0) {
signingKeyRing = findSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) {
- throw new GeneralException("signature failed");
+ throw new GeneralException(context.getString(R.string.error_signatureFailed));
}
if (signaturePassPhrase == null) {
- throw new GeneralException("no pass phrase given");
+ throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
}
+ progress.setProgress(R.string.progress_extractingSignatureKey, 0, 100);
signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
new BouncyCastleProvider());
}
PGPSignatureGenerator signatureGenerator = null;
- progress.setProgress("preparing data...", 0, 100);
-
- ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
- int n = 0;
- byte[] buffer = new byte[1 << 16];
- while ((n = inStream.read(buffer)) > 0) {
- byteOut.write(buffer, 0, n);
- }
- byteOut.close();
- byte messageData[] = byteOut.toByteArray();
-
- progress.setProgress("preparing streams...", 20, 100);
- // encryptFile and compress input file content
+ progress.setProgress(R.string.progress_preparingStreams, 5, 100);
+ // encrypt and compress input file content
PGPEncryptedDataGenerator cPk =
- new PGPEncryptedDataGenerator(PGPEncryptedData.AES_256, true, new SecureRandom(),
+ new PGPEncryptedDataGenerator(symmetricAlgorithm, true, new SecureRandom(),
new BouncyCastleProvider());
+ if (encryptionKeyIds.length == 0) {
+ // symmetric encryption
+ cPk.addMethod(passPhrase.toCharArray());
+ }
for (int i = 0; i < encryptionKeyIds.length; ++i) {
PGPPublicKey key = getEncryptPublicKey(encryptionKeyIds[i]);
if (key != null) {
@@ -1196,10 +1311,10 @@ public class Apg {
encryptOut = cPk.open(out, new byte[1 << 16]);
if (signatureKeyId != 0) {
- progress.setProgress("preparing signature...", 30, 100);
+ progress.setProgress(R.string.progress_preparingSignature, 10, 100);
signatureGenerator =
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
- HashAlgorithmTags.SHA1,
+ hashAlgorithm,
new BouncyCastleProvider());
signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
String userId = getMainUserId(getMasterKey(signingKeyRing));
@@ -1209,9 +1324,14 @@ public class Apg {
signatureGenerator.setHashedSubpackets(spGen.generate());
}
- PGPCompressedDataGenerator compressGen =
- new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZLIB);
- BCPGOutputStream bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
+ PGPCompressedDataGenerator compressGen = null;
+ BCPGOutputStream bcpgOut = null;
+ if (compression == Id.choice.compression.none) {
+ bcpgOut = new BCPGOutputStream(encryptOut);
+ } else {
+ compressGen = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZLIB);
+ bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
+ }
if (signatureKeyId != 0) {
signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
}
@@ -1219,68 +1339,80 @@ public class Apg {
PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
// file name not needed, so empty string
OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "",
- messageData.length, new Date());
-
- progress.setProgress("encrypting...", 40, 100);
- pOut.write(messageData);
+ new Date(), new byte[1 << 16]);
- if (signatureKeyId != 0) {
- progress.setProgress("finishing signature...", 70, 100);
- signatureGenerator.update(messageData);
+ progress.setProgress(R.string.progress_encrypting, 20, 100);
+ long done = 0;
+ int n = 0;
+ byte[] buffer = new byte[1 << 16];
+ while ((n = inStream.read(buffer)) > 0) {
+ pOut.write(buffer, 0, n);
+ if (signatureKeyId != 0) {
+ signatureGenerator.update(buffer, 0, n);
+ }
+ done += n;
+ if (dataLength != 0) {
+ progress.setProgress((int) (20 + (95 - 20) * done / dataLength), 100);
+ }
}
literalGen.close();
if (signatureKeyId != 0) {
+ progress.setProgress(R.string.progress_generatingSignature, 95, 100);
signatureGenerator.generate().encode(pOut);
}
- compressGen.close();
+ if (compressGen != null) {
+ compressGen.close();
+ }
encryptOut.close();
- out.close();
+ if (armored) {
+ armorOut.close();
+ }
- progress.setProgress("done.", 100, 100);
+ progress.setProgress(R.string.progress_done, 100, 100);
}
- public static void sign(InputStream inStream, OutputStream outStream,
- long signatureKeyId, String signaturePassPhrase,
- ProgressDialogUpdater progress)
+ public static void signText(Context context,
+ InputStream inStream, OutputStream outStream,
+ long signatureKeyId, String signaturePassPhrase,
+ int hashAlgorithm,
+ ProgressDialogUpdater progress)
throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
SignatureException {
Security.addProvider(new BouncyCastleProvider());
ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
armorOut.setHeader("Version", FULL_VERSION);
- OutputStream out = armorOut;
- OutputStream signOut = out;
PGPSecretKey signingKey = null;
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
if (signatureKeyId == 0) {
- throw new GeneralException("no signature key given");
+ throw new GeneralException(context.getString(R.string.error_noSignatureKey));
}
signingKeyRing = findSecretKeyRing(signatureKeyId);
signingKey = getSigningKey(signatureKeyId);
if (signingKey == null) {
- throw new GeneralException("signature failed");
+ throw new GeneralException(context.getString(R.string.error_signatureFailed));
}
if (signaturePassPhrase == null) {
- throw new GeneralException("no pass phrase given");
+ throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
}
signaturePrivateKey =
signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
new BouncyCastleProvider());
PGPSignatureGenerator signatureGenerator = null;
- progress.setProgress("preparing data...", 0, 100);
+ progress.setProgress(R.string.progress_preparingStreams, 0, 100);
- progress.setProgress("preparing signature...", 30, 100);
+ progress.setProgress(R.string.progress_preparingSignature, 30, 100);
signatureGenerator =
new PGPSignatureGenerator(signingKey.getPublicKey().getAlgorithm(),
- HashAlgorithmTags.SHA1,
+ hashAlgorithm,
new BouncyCastleProvider());
signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
String userId = getMainUserId(getMasterKey(signingKeyRing));
@@ -1289,22 +1421,38 @@ public class Apg {
spGen.setSignerUserID(false, userId);
signatureGenerator.setHashedSubpackets(spGen.generate());
- progress.setProgress("signing...", 40, 100);
- int n = 0;
- byte[] buffer = new byte[1 << 16];
- while ((n = inStream.read(buffer)) > 0) {
- signatureGenerator.update(buffer, 0, n);
+ progress.setProgress(R.string.progress_signing, 40, 100);
+
+ armorOut.beginClearText(hashAlgorithm);
+
+ ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
+ int lookAhead = readInputLine(lineOut, inStream);
+
+ processLine(armorOut, signatureGenerator, lineOut.toByteArray());
+
+ if (lookAhead != -1) {
+ do {
+ lookAhead = readInputLine(lineOut, lookAhead, inStream);
+
+ signatureGenerator.update((byte)'\r');
+ signatureGenerator.update((byte)'\n');
+
+ processLine(armorOut, signatureGenerator, lineOut.toByteArray());
+ }
+ while (lookAhead != -1);
}
- signatureGenerator.generate().encode(signOut);
- signOut.close();
- out.close();
+ armorOut.endClearText();
- progress.setProgress("done.", 100, 100);
+ BCPGOutputStream bOut = new BCPGOutputStream(armorOut);
+ signatureGenerator.generate().encode(bOut);
+ armorOut.close();
+
+ progress.setProgress(R.string.progress_done, 100, 100);
}
- public static long getDecryptionKeyId(InputStream inStream)
- throws GeneralException, IOException {
+ public static long getDecryptionKeyId(Context context, InputStream inStream)
+ throws GeneralException, NoAsymmetricEncryptionException, IOException {
InputStream in = PGPUtil.getDecoderStream(inStream);
PGPObjectFactory pgpF = new PGPObjectFactory(in);
PGPEncryptedDataList enc;
@@ -1318,27 +1466,71 @@ public class Apg {
}
if (enc == null) {
- throw new GeneralException("data not valid encryption data");
+ throw new GeneralException(context.getString(R.string.error_invalidData));
}
+ // TODO: currently we always only look at the first known key
// find the secret key
PGPSecretKey secretKey = null;
- for (PGPPublicKeyEncryptedData pbe :
- new IterableIterator<PGPPublicKeyEncryptedData>(enc.getEncryptedDataObjects())) {
- secretKey = findSecretKey(pbe.getKeyID());
- if (secretKey != null) {
- break;
+ Iterator it = enc.getEncryptedDataObjects();
+ boolean gotAsymmetricEncryption = false;
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPublicKeyEncryptedData) {
+ gotAsymmetricEncryption = true;
+ PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
+ secretKey = findSecretKey(pbe.getKeyID());
+ if (secretKey != null) {
+ break;
+ }
}
}
+
+ if (!gotAsymmetricEncryption) {
+ throw new NoAsymmetricEncryptionException();
+ }
+
if (secretKey == null) {
- throw new GeneralException("couldn't find a secret key to decrypt");
+ return Id.key.none;
}
return secretKey.getKeyID();
}
- public static Bundle decrypt(InputStream inStream, OutputStream outStream,
- String passPhrase, ProgressDialogUpdater progress)
+ public static boolean hasSymmetricEncryption(Context context, InputStream inStream)
+ throws GeneralException, IOException {
+ InputStream in = PGPUtil.getDecoderStream(inStream);
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ PGPEncryptedDataList enc;
+ Object o = pgpF.nextObject();
+
+ // the first object might be a PGP marker packet.
+ if (o instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) o;
+ } else {
+ enc = (PGPEncryptedDataList) pgpF.nextObject();
+ }
+
+ if (enc == null) {
+ throw new GeneralException(context.getString(R.string.error_invalidData));
+ }
+
+ Iterator it = enc.getEncryptedDataObjects();
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPBEEncryptedData) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public static Bundle decrypt(Context context,
+ PositionAwareInputStream inStream, OutputStream outStream,
+ long dataLength,
+ String passPhrase, ProgressDialogUpdater progress,
+ boolean assumeSymmetric)
throws IOException, GeneralException, PGPException, SignatureException {
Bundle returnData = new Bundle();
InputStream in = PGPUtil.getDecoderStream(inStream);
@@ -1347,7 +1539,8 @@ public class Apg {
Object o = pgpF.nextObject();
long signatureKeyId = 0;
- progress.setProgress("reading data...", 0, 100);
+ int currentProgress = 0;
+ progress.setProgress(R.string.progress_readingData, currentProgress, 100);
if (o instanceof PGPEncryptedDataList) {
enc = (PGPEncryptedDataList) o;
@@ -1356,35 +1549,74 @@ public class Apg {
}
if (enc == null) {
- throw new GeneralException("data not valid encryption data");
+ throw new GeneralException(context.getString(R.string.error_invalidData));
}
- progress.setProgress("finding key...", 10, 100);
- // find the secret key
- PGPPublicKeyEncryptedData pbe = null;
- PGPSecretKey secretKey = null;
- for (PGPPublicKeyEncryptedData encData :
- new IterableIterator<PGPPublicKeyEncryptedData>(enc.getEncryptedDataObjects())) {
- secretKey = findSecretKey(encData.getKeyID());
- if (secretKey != null) {
- pbe = encData;
- break;
+ InputStream clear = null;
+ PGPEncryptedData encryptedData = null;
+
+ currentProgress += 5;
+
+ // TODO: currently we always only look at the first known key or symmetric encryption,
+ // there might be more...
+ if (assumeSymmetric) {
+ PGPPBEEncryptedData pbe = null;
+ Iterator it = enc.getEncryptedDataObjects();
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPBEEncryptedData) {
+ pbe = (PGPPBEEncryptedData) obj;
+ break;
+ }
}
- }
- if (secretKey == null) {
- throw new GeneralException("couldn't find a secret key to decrypt");
- }
- progress.setProgress("extracting key...", 20, 100);
- PGPPrivateKey privateKey = null;
- try {
- privateKey = secretKey.extractPrivateKey(passPhrase.toCharArray(),
- new BouncyCastleProvider());
- } catch (PGPException e) {
- throw new PGPException("wrong pass phrase");
+ if (pbe == null) {
+ throw new GeneralException(context.getString(R.string.error_noSymmetricEncryptionPacket));
+ }
+
+ progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
+ clear = pbe.getDataStream(passPhrase.toCharArray(), new BouncyCastleProvider());
+ encryptedData = pbe;
+ currentProgress += 5;
+ } else {
+ progress.setProgress(R.string.progress_findingKey, currentProgress, 100);
+ PGPPublicKeyEncryptedData pbe = null;
+ PGPSecretKey secretKey = null;
+ Iterator it = enc.getEncryptedDataObjects();
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPublicKeyEncryptedData) {
+ PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
+ secretKey = findSecretKey(encData.getKeyID());
+ if (secretKey != null) {
+ pbe = encData;
+ break;
+ }
+ }
+ }
+
+ if (secretKey == null) {
+ throw new GeneralException(context.getString(R.string.error_noSecretKeyFound));
+ }
+
+ currentProgress += 5;
+ progress.setProgress(R.string.progress_extractingKey, currentProgress, 100);
+ PGPPrivateKey privateKey = null;
+ try {
+ privateKey = secretKey.extractPrivateKey(passPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ } catch (PGPException e) {
+ throw new PGPException(context.getString(R.string.error_wrongPassPhrase));
+ }
+ currentProgress += 5;
+ progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
+ clear = pbe.getDataStream(privateKey, new BouncyCastleProvider());
+ encryptedData = pbe;
+ currentProgress += 5;
}
- progress.setProgress("decrypting data...", 30, 100);
- InputStream clear = pbe.getDataStream(privateKey, new BouncyCastleProvider());
+
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
PGPOnePassSignature signature = null;
@@ -1392,15 +1624,16 @@ public class Apg {
int signatureIndex = -1;
if (dataChunk instanceof PGPCompressedData) {
- progress.setProgress("decompressing data...", 50, 100);
+ progress.setProgress(R.string.progress_decompressingData, currentProgress, 100);
PGPObjectFactory fact =
new PGPObjectFactory(((PGPCompressedData) dataChunk).getDataStream());
dataChunk = fact.nextObject();
plainFact = fact;
+ currentProgress += 10;
}
if (dataChunk instanceof PGPOnePassSignatureList) {
- progress.setProgress("processing signature...", 60, 100);
+ progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
returnData.putBoolean("signature", true);
PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
for (int i = 0; i < sigList.size(); ++i) {
@@ -1433,33 +1666,51 @@ public class Apg {
}
dataChunk = plainFact.nextObject();
+ currentProgress += 10;
}
if (dataChunk instanceof PGPLiteralData) {
- progress.setProgress("unpacking data...", 70, 100);
+ progress.setProgress(R.string.progress_decrypting, currentProgress, 100);
PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- BufferedOutputStream out = new BufferedOutputStream(outStream);
+ OutputStream out = outStream;
byte[] buffer = new byte[1 << 16];
InputStream dataIn = literalData.getInputStream();
- int bytesRead = 0;
- while ((bytesRead = dataIn.read(buffer)) > 0) {
- out.write(buffer, 0, bytesRead);
+ int startProgress = currentProgress;
+ int endProgress = 100;
+ if (signature != null) {
+ endProgress = 90;
+ } else if (encryptedData.isIntegrityProtected()) {
+ endProgress = 95;
+ }
+ int n = 0;
+ int done = 0;
+ long startPos = inStream.position();
+ while ((n = dataIn.read(buffer)) > 0) {
+ out.write(buffer, 0, n);
+ done += n;
if (signature != null) {
try {
- signature.update(buffer, 0, bytesRead);
+ signature.update(buffer, 0, n);
} catch (SignatureException e) {
returnData.putBoolean("signatureSuccess", false);
signature = null;
}
}
+ // unknown size, but try to at least have a moving, slowing down progress bar
+ currentProgress = startProgress + (endProgress - startProgress) * done / (done + 100000);
+ if (dataLength - startPos == 0) {
+ currentProgress = endProgress;
+ } else {
+ currentProgress = (int)(startProgress + (endProgress - startProgress) *
+ (inStream.position() - startPos) / (dataLength - startPos));
+ }
+ progress.setProgress(currentProgress, 100);
}
- out.close();
-
if (signature != null) {
- progress.setProgress("verifying signature...", 80, 100);
+ progress.setProgress(R.string.progress_verifyingSignature, 90, 100);
PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
PGPSignature messageSignature = (PGPSignature) signatureList.get(signatureIndex);
if (signature.verify(messageSignature)) {
@@ -1471,18 +1722,116 @@ public class Apg {
}
// TODO: add integrity somewhere
- if (pbe.isIntegrityProtected()) {
- progress.setProgress("verifying integrity...", 90, 100);
- if (!pbe.verify()) {
- System.err.println("message failed integrity check");
+ if (encryptedData.isIntegrityProtected()) {
+ progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100);
+ if (encryptedData.verify()) {
+ // passed
} else {
- System.err.println("message integrity check passed");
+ // failed
}
} else {
- System.err.println("no message integrity check");
+ // no integrity check
}
- progress.setProgress("done.", 100, 100);
+ progress.setProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ public static Bundle verifyText(Context context,
+ InputStream inStream, OutputStream outStream,
+ ProgressDialogUpdater progress)
+ throws IOException, GeneralException, PGPException, SignatureException {
+ Bundle returnData = new Bundle();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ArmoredInputStream aIn = new ArmoredInputStream(inStream);
+
+ progress.setProgress(R.string.progress_done, 0, 100);
+
+ // mostly taken from ClearSignedFileProcessor
+ ByteArrayOutputStream lineOut = new ByteArrayOutputStream();
+ int lookAhead = readInputLine(lineOut, aIn);
+ byte[] lineSep = getLineSeparator();
+
+ byte[] line = lineOut.toByteArray();
+ out.write(line, 0, getLengthWithoutSeparator(line));
+ out.write(lineSep);
+
+ while (lookAhead != -1 && aIn.isClearText()) {
+ lookAhead = readInputLine(lineOut, lookAhead, aIn);
+ line = lineOut.toByteArray();
+ out.write(line, 0, getLengthWithoutSeparator(line));
+ out.write(lineSep);
+ }
+
+ out.close();
+
+ byte[] clearText = out.toByteArray();
+ outStream.write(clearText);
+
+ returnData.putBoolean("signature", true);
+
+ progress.setProgress(R.string.progress_processingSignature, 60, 100);
+ PGPObjectFactory pgpFact = new PGPObjectFactory(aIn);
+
+ PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject();
+ if (sigList == null) {
+ throw new GeneralException(context.getString(R.string.error_corruptData));
+ }
+ PGPSignature signature = null;
+ long signatureKeyId = 0;
+ PGPPublicKey signatureKey = null;
+ for (int i = 0; i < sigList.size(); ++i) {
+ signature = sigList.get(i);
+ signatureKey = findPublicKey(signature.getKeyID());
+ if (signatureKeyId == 0) {
+ signatureKeyId = signature.getKeyID();
+ }
+ if (signatureKey == null) {
+ signature = null;
+ } else {
+ signatureKeyId = signature.getKeyID();
+ String userId = null;
+ PGPPublicKeyRing sigKeyRing = findPublicKeyRing(signatureKeyId);
+ if (sigKeyRing != null) {
+ userId = getMainUserId(getMasterKey(sigKeyRing));
+ }
+ returnData.putString("signatureUserId", userId);
+ break;
+ }
+ }
+
+ returnData.putLong("signatureKeyId", signatureKeyId);
+
+ if (signature == null) {
+ returnData.putBoolean("signatureUnknown", true);
+ progress.setProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ signature.initVerify(signatureKey, new BouncyCastleProvider());
+
+ InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText));
+
+ lookAhead = readInputLine(lineOut, sigIn);
+
+ processLine(signature, lineOut.toByteArray());
+
+ if (lookAhead != -1) {
+ do {
+ lookAhead = readInputLine(lineOut, lookAhead, sigIn);
+
+ signature.update((byte)'\r');
+ signature.update((byte)'\n');
+
+ processLine(signature, lineOut.toByteArray());
+ }
+ while (lookAhead != -1);
+ }
+
+ returnData.putBoolean("signatureSuccess", signature.verify());
+
+ progress.setProgress(R.string.progress_done, 100, 100);
return returnData;
}
@@ -1493,4 +1842,115 @@ public class Apg {
public static Vector<PGPSecretKeyRing> getSecretKeyRings() {
return mSecretKeyRings;
}
+
+
+ // taken from ClearSignedFileProcessor in BC
+ private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn)
+ throws IOException {
+ bOut.reset();
+
+ int lookAhead = -1;
+ int ch;
+
+ while ((ch = fIn.read()) >= 0) {
+ bOut.write(ch);
+ if (ch == '\r' || ch == '\n') {
+ lookAhead = readPassedEOL(bOut, ch, fIn);
+ break;
+ }
+ }
+
+ return lookAhead;
+ }
+
+ private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn)
+ throws IOException {
+ bOut.reset();
+
+ int ch = lookAhead;
+
+ do {
+ bOut.write(ch);
+ if (ch == '\r' || ch == '\n') {
+ lookAhead = readPassedEOL(bOut, ch, fIn);
+ break;
+ }
+ }
+ while ((ch = fIn.read()) >= 0);
+
+ if (ch < 0) {
+ lookAhead = -1;
+ }
+
+ return lookAhead;
+ }
+
+ private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
+ throws IOException {
+ int lookAhead = fIn.read();
+
+ if (lastCh == '\r' && lookAhead == '\n') {
+ bOut.write(lookAhead);
+ lookAhead = fIn.read();
+ }
+
+ return lookAhead;
+ }
+
+ private static void processLine(PGPSignature sig, byte[] line)
+ throws SignatureException, IOException {
+ int length = getLengthWithoutWhiteSpace(line);
+ if (length > 0) {
+ sig.update(line, 0, length);
+ }
+ }
+
+ private static void processLine(OutputStream aOut, PGPSignatureGenerator sGen, byte[] line)
+ throws SignatureException, IOException {
+ int length = getLengthWithoutWhiteSpace(line);
+ if (length > 0) {
+ sGen.update(line, 0, length);
+ }
+
+ aOut.write(line, 0, line.length);
+ }
+
+ private static int getLengthWithoutSeparator(byte[] line) {
+ int end = line.length - 1;
+
+ while (end >= 0 && isLineEnding(line[end])) {
+ end--;
+ }
+
+ return end + 1;
+ }
+
+ private static boolean isLineEnding(byte b) {
+ return b == '\r' || b == '\n';
+ }
+
+ private static int getLengthWithoutWhiteSpace(byte[] line) {
+ int end = line.length - 1;
+
+ while (end >= 0 && isWhiteSpace(line[end])) {
+ end--;
+ }
+
+ return end + 1;
+ }
+
+ private static boolean isWhiteSpace(byte b) {
+ return b == '\r' || b == '\n' || b == '\t' || b == ' ';
+ }
+
+ private static byte[] getLineSeparator() {
+ String nl = System.getProperty("line.separator");
+ byte[] nlBytes = new byte[nl.length()];
+
+ for (int i = 0; i != nlBytes.length; i++) {
+ nlBytes[i] = (byte)nl.charAt(i);
+ }
+
+ return nlBytes;
+ }
}
diff --git a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
index ed1c16833..67aad7529 100644
--- a/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
+++ b/src/org/thialfihar/android/apg/AskForSecretKeyPassPhrase.java
@@ -23,93 +23,77 @@ import org.bouncycastle2.openpgp.PGPSecretKey;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.content.Context;
import android.content.DialogInterface;
-import android.text.InputType;
-import android.text.method.PasswordTransformationMethod;
-import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.View;
-import android.view.View.OnKeyListener;
-import android.view.ViewGroup.LayoutParams;
import android.widget.EditText;
-import android.widget.LinearLayout;
import android.widget.Toast;
public class AskForSecretKeyPassPhrase {
- public static final int DIALOG_PASS_PHRASE = 12345;
-
public static interface PassPhraseCallbackInterface {
- void passPhraseCallback(String passPhrase);
+ void passPhraseCallback(long keyId, String passPhrase);
}
public static Dialog createDialog(Activity context, long secretKeyId,
PassPhraseCallbackInterface callback) {
AlertDialog.Builder alert = new AlertDialog.Builder(context);
- final PGPSecretKey secretKey =
- Apg.getMasterKey(Apg.findSecretKeyRing(secretKeyId));
- if (secretKey == null) {
- return null;
- }
-
- String userId = Apg.getMainUserIdSafe(context, secretKey);
-
alert.setTitle(R.string.title_authentification);
- alert.setMessage("Pass phrase for " + userId);
- final EditText input = new EditText(context);
- input.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
- input.setTransformationMethod(new PasswordTransformationMethod());
- input.setOnKeyListener(new OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- // If the event is a key-down event on the "enter" button
- if (event.getAction() == KeyEvent.ACTION_DOWN &&
- keyCode == KeyEvent.KEYCODE_ENTER) {
- try {
- ((AlertDialog) v.getParent()).getButton(AlertDialog.BUTTON_POSITIVE)
- .performClick();
- } catch (ClassCastException e) {
- // don't do anything if we're not in that dialog
- }
- return true;
- }
- return false;
+ final PGPSecretKey secretKey;
+
+ 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));
+ if (secretKey == null) {
+ return null;
}
- });
- // 5dip padding
- int padding = (int) (10 * context.getResources().getDisplayMetrics().densityDpi / 160);
- LinearLayout layout = new LinearLayout(context);
- layout.setPadding(padding, 0, padding, 0);
- layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.WRAP_CONTENT));
- input.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.WRAP_CONTENT));
- layout.addView(input);
- alert.setView(layout);
+ String userId = Apg.getMainUserIdSafe(context, secretKey);
+ alert.setMessage(context.getString(R.string.passPhraseFor, userId));
+ }
+
+ LayoutInflater inflater =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.pass_phrase, null);
+ final EditText input = (EditText) view.findViewById(R.id.passPhrase);
+ final EditText inputNotUsed = (EditText) view.findViewById(R.id.passPhraseAgain);
+ inputNotUsed.setVisibility(View.GONE);
+
+ 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(DIALOG_PASS_PHRASE);
+ activity.removeDialog(Id.dialog.pass_phrase);
String passPhrase = "" + input.getText();
- try {
- secretKey.extractPrivateKey(passPhrase.toCharArray(),
- new BouncyCastleProvider());
- } catch (PGPException e) {
- Toast.makeText(activity,
- R.string.wrong_pass_phrase,
- Toast.LENGTH_SHORT).show();
- return;
+ 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(passPhrase);
+ cb.passPhraseCallback(keyId, passPhrase);
}
});
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- activity.removeDialog(DIALOG_PASS_PHRASE);
+ activity.removeDialog(Id.dialog.pass_phrase);
}
});
diff --git a/src/org/thialfihar/android/apg/BaseActivity.java b/src/org/thialfihar/android/apg/BaseActivity.java
new file mode 100644
index 000000000..64705ba1f
--- /dev/null
+++ b/src/org/thialfihar/android/apg/BaseActivity.java
@@ -0,0 +1,467 @@
+/*
+ * 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.File;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.bouncycastle2.bcpg.CompressionAlgorithmTags;
+import org.bouncycastle2.bcpg.HashAlgorithmTags;
+import org.bouncycastle2.openpgp.PGPEncryptedData;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BaseActivity extends Activity
+ implements Runnable, ProgressDialogUpdater,
+ AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
+
+ private ProgressDialog mProgressDialog = null;
+ private Thread mRunningThread = null;
+
+ private long mSecretKeyId = 0;
+ 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) {
+ handlerCallback(msg);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ 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());
+ }
+ }, 0, ttl * 1000 / 2);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(0, Id.menu.option.about, 1, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Id.menu.option.about: {
+ showDialog(Id.dialog.about);
+ return true;
+ }
+
+ case Id.menu.option.preferences: {
+ startActivity(new Intent(this, PreferencesActivity.class));
+ return true;
+ }
+
+ default: {
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ // in case it is a progress dialog
+ mProgressDialog = new ProgressDialog(this);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+ mProgressDialog.setCancelable(false);
+ switch (id) {
+ case Id.dialog.encrypting: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.decrypting: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.saving: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_saving));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.importing: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_importing));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.exporting: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_exporting));
+ return mProgressDialog;
+ }
+
+ default: {
+ break;
+ }
+ }
+ mProgressDialog = null;
+
+ switch (id) {
+ case Id.dialog.about: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setTitle("About " + Apg.FULL_VERSION);
+
+ LayoutInflater inflater =
+ (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View layout = inflater.inflate(R.layout.info, null);
+ TextView message = (TextView) layout.findViewById(R.id.message);
+ message.setText("This is an attempt to bring OpenPGP to Android. " +
+ "It is far from complete, but more features are planned (see website).\n\n" +
+ "Feel free to send bug reports, suggestions, feature requests, feedback, " +
+ "photographs.\n\n" +
+ "mail: thi@thialfihar.org\n" +
+ "site: http://apg.thialfihar.org\n\n" +
+ "This software is provided \"as is\", without warranty of any kind.");
+ alert.setView(layout);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ BaseActivity.this.removeDialog(Id.dialog.about);
+ }
+ });
+
+ return alert.create();
+ }
+
+ case Id.dialog.pass_phrase: {
+ return AskForSecretKeyPassPhrase.createDialog(this, getSecretKeyId(), this);
+ }
+
+ case Id.dialog.pass_phrases_do_not_match: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.error);
+ alert.setMessage(R.string.passPhrasesDoNotMatch);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.pass_phrases_do_not_match);
+ }
+ });
+ alert.setCancelable(false);
+
+ return alert.create();
+ }
+
+ case Id.dialog.no_pass_phrase: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.error);
+ alert.setMessage(R.string.passPhraseMustNotBeEmpty);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.no_pass_phrase);
+ }
+ });
+ alert.setCancelable(false);
+
+ return alert.create();
+ }
+
+ case Id.dialog.delete_file: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.warning);
+ alert.setMessage(this.getString(R.string.fileDeleteConfirmation, getDeleteFile()));
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.delete_file);
+ File file = new File(getDeleteFile());
+ String msg = "";
+ if (file.delete()) {
+ msg = BaseActivity.this.getString(
+ R.string.fileDeleteSuccessful);
+ } else {
+ msg = BaseActivity.this.getString(
+ R.string.errorMessage,
+ BaseActivity.this.getString(
+ R.string.error_fileDeleteFailed, file));
+ }
+ Toast.makeText(BaseActivity.this,
+ msg, Toast.LENGTH_SHORT).show();
+ }
+ });
+ alert.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.delete_file);
+ }
+ });
+ alert.setCancelable(true);
+
+ return alert.create();
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return super.onCreateDialog(id);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.secret_keys: {
+ if (resultCode == RESULT_OK) {
+ Bundle bundle = data.getExtras();
+ setSecretKeyId(bundle.getLong("selectedKeyId"));
+ } else {
+ setSecretKeyId(Id.key.none);
+ }
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ public void setProgress(int resourceId, int progress, int max) {
+ setProgress(getString(resourceId), progress, max);
+ }
+
+ 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);
+ msg.setData(data);
+ mHandler.sendMessage(msg);
+ }
+
+ 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);
+ msg.setData(data);
+ mHandler.sendMessage(msg);
+ }
+
+ public void handlerCallback(Message msg) {
+ Bundle data = msg.getData();
+ if (data == null) {
+ return;
+ }
+
+ int type = data.getInt("type");
+ switch (type) {
+ case Id.message.progress_update: {
+ String message = data.getString("message");
+ if (mProgressDialog != null) {
+ if (message != null) {
+ mProgressDialog.setMessage(message);
+ }
+ mProgressDialog.setMax(data.getInt("max"));
+ mProgressDialog.setProgress(data.getInt("progress"));
+ }
+ break;
+ }
+
+ case Id.message.import_done: // intentionall no break
+ case Id.message.export_done: // intentionall no break
+ case Id.message.done: {
+ mProgressDialog = null;
+ doneCallback(msg);
+ break;
+ }
+ }
+ }
+
+ public void doneCallback(Message msg) {
+
+ }
+
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ Apg.setCachedPassPhrase(keyId, passPhrase);
+ }
+
+ public void sendMessage(Message msg) {
+ mHandler.sendMessage(msg);
+ }
+
+ public void startThread() {
+ mRunningThread = new Thread(this);
+ mRunningThread.start();
+ }
+
+ public void run() {
+
+ }
+
+ public void setSecretKeyId(long id) {
+ mSecretKeyId = id;
+ }
+
+ public long getSecretKeyId() {
+ return mSecretKeyId;
+ }
+
+ public int getPassPhraseCacheTtl() {
+ return mPreferences.getInt(Constants.pref.pass_phrase_cache_ttl, 300);
+ }
+
+ public void setPassPhraseCacheTtl(int value) {
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putInt(Constants.pref.pass_phrase_cache_ttl, value);
+ editor.commit();
+
+ setPassPhraseCacheTimer();
+ }
+
+ public int getDefaultEncryptionAlgorithm() {
+ return mPreferences.getInt(Constants.pref.default_encryption_algorithm,
+ PGPEncryptedData.AES_256);
+ }
+
+ public void setDefaultEncryptionAlgorithm(int value) {
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putInt(Constants.pref.default_encryption_algorithm, value);
+ editor.commit();
+ }
+
+ public int getDefaultHashAlgorithm() {
+ return mPreferences.getInt(Constants.pref.default_hash_algorithm,
+ HashAlgorithmTags.SHA256);
+ }
+
+ public void setDefaultHashAlgorithm(int value) {
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putInt(Constants.pref.default_hash_algorithm, value);
+ editor.commit();
+ }
+
+ public int getDefaultMessageCompression() {
+ return mPreferences.getInt(Constants.pref.default_message_compression,
+ CompressionAlgorithmTags.ZLIB);
+ }
+
+ public void setDefaultMessageCompression(int value) {
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putInt(Constants.pref.default_message_compression, value);
+ editor.commit();
+ }
+
+ public int getDefaultFileCompression() {
+ return mPreferences.getInt(Constants.pref.default_file_compression,
+ CompressionAlgorithmTags.ZLIB);
+ }
+
+ public void setDefaultFileCompression(int value) {
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putInt(Constants.pref.default_file_compression, value);
+ editor.commit();
+ }
+
+ public boolean getDefaultAsciiArmour() {
+ return mPreferences.getBoolean(Constants.pref.default_ascii_armour, false);
+ }
+
+ public void setDefaultAsciiArmour(boolean value) {
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putBoolean(Constants.pref.default_ascii_armour, value);
+ editor.commit();
+ }
+
+ public boolean hasSeenChangeLog() {
+ return mPreferences.getBoolean(Constants.pref.has_seen_change_log, false);
+ }
+
+ public void setHasSeenChangeLog(boolean value) {
+ SharedPreferences.Editor editor = mPreferences.edit();
+ editor.putBoolean(Constants.pref.has_seen_change_log, value);
+ editor.commit();
+ }
+
+ protected void setDeleteFile(String deleteFile) {
+ mDeleteFile = deleteFile;
+ }
+
+ protected String getDeleteFile() {
+ return mDeleteFile;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/CachedPassPhrase.java b/src/org/thialfihar/android/apg/CachedPassPhrase.java
new file mode 100644
index 000000000..e7566220e
--- /dev/null
+++ b/src/org/thialfihar/android/apg/CachedPassPhrase.java
@@ -0,0 +1,39 @@
+package org.thialfihar.android.apg;
+
+public class CachedPassPhrase {
+ public final long timestamp;
+ public final String passPhrase;
+
+ public CachedPassPhrase(long timestamp, String passPhrase) {
+ super();
+ this.timestamp = timestamp;
+ this.passPhrase = passPhrase;
+ }
+
+ public boolean equals(Object other) {
+ if (!(other instanceof CachedPassPhrase)) {
+ return false;
+ }
+
+ CachedPassPhrase o = (CachedPassPhrase) other;
+ if (timestamp != o.timestamp) {
+ return false;
+ }
+
+ if (passPhrase != o.passPhrase) {
+ if (passPhrase == null || o.passPhrase == null) {
+ return false;
+ }
+
+ if (!passPhrase.equals(o.passPhrase)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public String toString() {
+ return "(" + timestamp + ", *******)";
+ }
+}
diff --git a/src/org/thialfihar/android/apg/Constants.java b/src/org/thialfihar/android/apg/Constants.java
new file mode 100644
index 000000000..f4a6a5066
--- /dev/null
+++ b/src/org/thialfihar/android/apg/Constants.java
@@ -0,0 +1,35 @@
+/*
+ * 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 android.os.Environment;
+
+public final class Constants {
+ public static final class path {
+ public static final String app_dir = Environment.getExternalStorageDirectory() + "/APG";
+ }
+
+ public static final class pref {
+ public static final String has_seen_change_log = "seenChangeLogDialog" + Apg.VERSION;
+ public static final String default_encryption_algorithm = "defaultEncryptionAlgorithm";
+ public static final String default_hash_algorithm = "defaultHashAlgorithm";
+ public static final String default_ascii_armour = "defaultAsciiArmour";
+ public static final String default_message_compression = "defaultMessageCompression";
+ public static final String default_file_compression = "defaultFileCompression";
+ public static final String pass_phrase_cache_ttl = "passPhraseCacheTtl";
+ }
+}
diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java
new file mode 100644
index 000000000..80ad13d5e
--- /dev/null
+++ b/src/org/thialfihar/android/apg/DecryptActivity.java
@@ -0,0 +1,647 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Security;
+import java.security.SignatureException;
+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;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.text.ClipboardManager;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ViewFlipper;
+
+public class DecryptActivity extends BaseActivity {
+ private long mSignatureKeyId = 0;
+
+ private String mReplyTo = null;
+ private String mSubject = null;
+ private boolean mSignedOnly = false;
+ private boolean mAssumeSymmetricEncryption = false;
+
+ private EditText mMessage = null;
+ private LinearLayout mSignatureLayout = null;
+ private ImageView mSignatureStatusImage = null;
+ private TextView mUserId = null;
+ private TextView mUserIdRest = null;
+
+ private ViewFlipper mSource = null;
+ private TextView mSourceLabel = null;
+ private ImageView mSourcePrevious = null;
+ private ImageView mSourceNext = null;
+
+ private Button mDecryptButton = null;
+ private Button mReplyButton = null;
+
+ private int mDecryptTarget;
+
+ private EditText mFilename = null;
+ private CheckBox mDeleteAfter = null;
+ private ImageButton mBrowse = null;
+
+ private String mInputFilename = null;
+ private String mOutputFilename = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.decrypt);
+
+ mSource = (ViewFlipper) findViewById(R.id.source);
+ mSourceLabel = (TextView) findViewById(R.id.sourceLabel);
+ mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious);
+ mSourceNext = (ImageView) findViewById(R.id.sourceNext);
+
+ mSourcePrevious.setClickable(true);
+ mSourcePrevious.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
+ R.anim.push_right_in));
+ mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
+ R.anim.push_right_out));
+ mSource.showPrevious();
+ updateSource();
+ }
+ });
+
+ mSourceNext.setClickable(true);
+ OnClickListener nextSourceClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
+ R.anim.push_left_in));
+ mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
+ R.anim.push_left_out));
+ mSource.showNext();
+ updateSource();
+ }
+ };
+ mSourceNext.setOnClickListener(nextSourceClickListener);
+
+ mSourceLabel.setClickable(true);
+ mSourceLabel.setOnClickListener(nextSourceClickListener);
+
+ mMessage = (EditText) findViewById(R.id.message);
+ mDecryptButton = (Button) findViewById(R.id.btn_decrypt);
+ mReplyButton = (Button) findViewById(R.id.btn_reply);
+ mSignatureLayout = (LinearLayout) findViewById(R.id.signature);
+ mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status);
+ mUserId = (TextView) findViewById(R.id.mainUserId);
+ mUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
+
+ // measure the height of the source_file view and set the message view's min height to that,
+ // so it fills mSource fully... bit of a hack.
+ View tmp = findViewById(R.id.sourceFile);
+ tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ int height = tmp.getMeasuredHeight();
+ mMessage.setMinimumHeight(height);
+
+ mFilename = (EditText) findViewById(R.id.filename);
+ mBrowse = (ImageButton) findViewById(R.id.btn_browse);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ openFile();
+ }
+ });
+
+ mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterDecryption);
+
+ // default: message source
+ mSource.setInAnimation(null);
+ mSource.setOutAnimation(null);
+ while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
+ mSource.showNext();
+ }
+
+ Intent intent = getIntent();
+ if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) {
+ Uri uri = intent.getData();
+ try {
+ InputStream attachment = getContentResolver().openInputStream(uri);
+ ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+ byte bytes[] = new byte[1 << 16];
+ int length;
+ while ((length = attachment.read(bytes)) > 0) {
+ byteOut.write(bytes, 0, length);
+ }
+ byteOut.close();
+ String data = Strings.fromUTF8ByteArray(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();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ String data = extras.getString(Intent.EXTRA_TEXT);
+ if (data != null) {
+ mMessage.setText(data);
+ }
+ mSubject = extras.getString(Intent.EXTRA_SUBJECT);
+ if (mSubject.startsWith("Fwd: ")) {
+ mSubject = mSubject.substring(5);
+ }
+ } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT)) {
+ Bundle extras = intent.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ String data = extras.getString("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);
+ }
+ }
+ }
+ mReplyTo = extras.getString("replyTo");
+ mSubject = extras.getString("subject");
+ } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT_FILE)) {
+ mSource.setInAnimation(null);
+ mSource.setOutAnimation(null);
+ while (mSource.getCurrentView().getId() != R.id.sourceFile) {
+ mSource.showNext();
+ }
+ }
+
+ if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
+ mMessage.getText().length() == 0) {
+ ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ String data = "";
+ Matcher matcher = Apg.PGP_MESSAGE.matcher(clip.getText());
+ if (!matcher.matches()) {
+ matcher = Apg.PGP_SIGNED_MESSAGE.matcher(clip.getText());
+ }
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ mMessage.setText(data);
+ Toast.makeText(this, R.string.usingClipboardContent, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ mSignatureLayout.setVisibility(View.GONE);
+
+ mDecryptButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptClicked();
+ }
+ });
+
+ mReplyButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ replyClicked();
+ }
+ });
+ mReplyButton.setVisibility(View.INVISIBLE);
+
+ if (mSource.getCurrentView().getId() == R.id.sourceMessage &&
+ mMessage.getText().length() > 0) {
+ mDecryptButton.performClick();
+ }
+
+ updateSource();
+ }
+
+ private void openFile() {
+ String filename = mFilename.getText().toString();
+
+ Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
+
+ 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);
+
+ 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();
+ }
+ }
+
+ private void guessOutputFilename() {
+ mInputFilename = mFilename.getText().toString();
+ File file = new File(mInputFilename);
+ String filename = file.getName();
+ if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
+ filename = filename.substring(0, filename.length() - 4);
+ }
+ mOutputFilename = Constants.path.app_dir + "/" + filename;
+ }
+
+ private void updateSource() {
+ switch (mSource.getCurrentView().getId()) {
+ case R.id.sourceFile: {
+ mSourceLabel.setText(R.string.label_file);
+ mDecryptButton.setText(R.string.btn_decrypt);
+ break;
+ }
+
+ case R.id.sourceMessage: {
+ mSourceLabel.setText(R.string.label_message);
+ mDecryptButton.setText(R.string.btn_decrypt);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+
+ private void decryptClicked() {
+ if (mSource.getCurrentView().getId() == R.id.sourceFile) {
+ mDecryptTarget = Id.target.file;
+ } else {
+ mDecryptTarget = Id.target.message;
+ }
+ initiateDecryption();
+ }
+
+ private void initiateDecryption() {
+ if (mDecryptTarget == Id.target.file) {
+ String currentFilename = mFilename.getText().toString();
+ if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
+ guessOutputFilename();
+ }
+
+ if (mInputFilename.equals("")) {
+ Toast.makeText(this, R.string.noFileSelected, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ File file = new File(mInputFilename);
+ if (!file.exists() || !file.isFile()) {
+ Toast.makeText(this, getString(R.string.errorMessage,
+ getString(R.string.error_fileNotFound)),
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ if (mDecryptTarget == Id.target.message) {
+ String messageData = mMessage.getText().toString();
+ Matcher matcher = Apg.PGP_SIGNED_MESSAGE.matcher(messageData);
+ if (matcher.matches()) {
+ mSignedOnly = true;
+ decryptStart();
+ return;
+ }
+ }
+
+ // else treat it as an decrypted message/file
+ mSignedOnly = false;
+ String error = null;
+ try {
+ InputStream in;
+ if (mDecryptTarget == Id.target.file) {
+ in = new FileInputStream(mInputFilename);
+ } else {
+ in = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
+ }
+ try {
+ setSecretKeyId(Apg.getDecryptionKeyId(this, in));
+ if (getSecretKeyId() == Id.key.none) {
+ throw new Apg.GeneralException(getString(R.string.error_noSecretKeyFound));
+ }
+ mAssumeSymmetricEncryption = false;
+ } catch (Apg.NoAsymmetricEncryptionException e) {
+ setSecretKeyId(Id.key.symmetric);
+ // look at the file/message again to check whether there's
+ // symmetric encryption data in there
+ if (mDecryptTarget == Id.target.file) {
+ ((FileInputStream) in).reset();
+ } else {
+ ((ByteArrayInputStream) in).reset();
+ }
+ if (!Apg.hasSymmetricEncryption(this, in)) {
+ throw new Apg.GeneralException(getString(R.string.error_noKnownEncryptionFound));
+ }
+ mAssumeSymmetricEncryption = true;
+ }
+
+ if (getSecretKeyId() == Id.key.symmetric ||
+ Apg.getCachedPassPhrase(getSecretKeyId()) == null) {
+ showDialog(Id.dialog.pass_phrase);
+ } else {
+ if (mDecryptTarget == Id.target.file) {
+ askForOutputFilename();
+ } else {
+ decryptStart();
+ }
+ }
+ } catch (FileNotFoundException e) {
+ error = getString(R.string.error_fileNotFound);
+ } catch (IOException e) {
+ error = e.getLocalizedMessage();
+ } catch (Apg.GeneralException e) {
+ error = e.getLocalizedMessage();
+ }
+ if (error != null) {
+ Toast.makeText(this, getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void replyClicked() {
+ Intent intent = new Intent(this, EncryptActivity.class);
+ intent.setAction(Apg.Intent.ENCRYPT);
+ 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 });
+ startActivity(intent);
+ }
+
+ private void askForOutputFilename() {
+ showDialog(Id.dialog.output_filename);
+ }
+
+ @Override
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ super.passPhraseCallback(keyId, passPhrase);
+ if (mDecryptTarget == Id.target.file) {
+ askForOutputFilename();
+ } else {
+ decryptStart();
+ }
+ }
+
+ private void decryptStart() {
+ showDialog(Id.dialog.decrypting);
+ startThread();
+ }
+
+ @Override
+ public void run() {
+ String error = null;
+ Security.addProvider(new BouncyCastleProvider());
+
+ Bundle data = new Bundle();
+ Message msg = new Message();
+
+ try {
+ PositionAwareInputStream in = null;
+ OutputStream out = null;
+ long size = 0;
+
+ if (mDecryptTarget == Id.target.message) {
+ String messageData = mMessage.getText().toString();
+ in = new PositionAwareInputStream(new ByteArrayInputStream(messageData.getBytes()));
+ out = new ByteArrayOutputStream();
+ size = messageData.getBytes().length;
+ } else {
+ in = new PositionAwareInputStream(new FileInputStream(mInputFilename));
+ out = new FileOutputStream(mOutputFilename);
+ File file = new File(mInputFilename);
+ size = file.length();
+ }
+
+ if (mSignedOnly) {
+ data = Apg.verifyText(this, in, out, this);
+ } else {
+ data = Apg.decrypt(this, in, out, size, Apg.getCachedPassPhrase(getSecretKeyId()),
+ this, mAssumeSymmetricEncryption);
+ }
+
+ out.close();
+ if (mDecryptTarget == Id.target.message) {
+ data.putString("decryptedMessage",
+ Strings.fromUTF8ByteArray(((ByteArrayOutputStream)
+ out).toByteArray()));
+ }
+ } catch (PGPException e) {
+ error = e.getMessage();
+ } catch (IOException e) {
+ error = e.getMessage();
+ } catch (SignatureException e) {
+ error = e.getMessage();
+ e.printStackTrace();
+ } catch (Apg.GeneralException e) {
+ error = e.getMessage();
+ }
+
+ data.putInt("type", Id.message.done);
+
+ if (error != null) {
+ data.putString("error", error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ Bundle data = msg.getData();
+ removeDialog(Id.dialog.decrypting);
+ mSignatureKeyId = 0;
+ mSignatureLayout.setVisibility(View.GONE);
+ mReplyButton.setVisibility(View.INVISIBLE);
+
+ String error = data.getString("error");
+ if (error != null) {
+ Toast.makeText(DecryptActivity.this,
+ getString(R.string.errorMessage,
+ data.getString("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");
+ mMessage.setText(decryptedMessage);
+ mReplyButton.setVisibility(View.VISIBLE);
+ break;
+ }
+
+ case Id.target.file: {
+ if (mDeleteAfter.isChecked()) {
+ setDeleteFile(mInputFilename);
+ showDialog(Id.dialog.delete_file);
+ }
+ break;
+ }
+
+ default: {
+ // shouldn't happen
+ break;
+ }
+ }
+
+ if (data.getBoolean("signature")) {
+ String userId = data.getString("signatureUserId");
+ mSignatureKeyId = data.getLong("signatureKeyId");
+ mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL));
+ if (userId == null) {
+ userId = getResources().getString(R.string.unknownUserId);
+ }
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mUserIdRest.setText("<" + chunks[1]);
+ }
+ mUserId.setText(userId);
+
+ if (data.getBoolean("signatureSuccess")) {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
+ } else if (data.getBoolean("signatureUnknown")) {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ } else {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ }
+ mSignatureLayout.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @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);
+
+ mFilename.setText(filename);
+ }
+ }
+ return;
+ }
+
+ case Id.request.output_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);
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case Id.dialog.output_filename: {
+ return FileDialog.build(this, getString(R.string.title_decryptToFile),
+ getString(R.string.specifyFileToDecryptTo),
+ mOutputFilename,
+ new FileDialog.OnClickListener() {
+
+ @Override
+ public void onOkClick(String filename) {
+ removeDialog(Id.dialog.output_filename);
+ mOutputFilename = filename;
+ decryptStart();
+ }
+
+ @Override
+ public void onCancelClick() {
+ removeDialog(Id.dialog.output_filename);
+ }
+ },
+ getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave),
+ Id.request.output_filename);
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return super.onCreateDialog(id);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/DecryptMessageActivity.java b/src/org/thialfihar/android/apg/DecryptMessageActivity.java
deleted file mode 100644
index 179d5be55..000000000
--- a/src/org/thialfihar/android/apg/DecryptMessageActivity.java
+++ /dev/null
@@ -1,343 +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;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.Security;
-import java.security.SignatureException;
-import java.util.regex.Matcher;
-
-import org.bouncycastle2.jce.provider.BouncyCastleProvider;
-import org.bouncycastle2.openpgp.PGPException;
-import org.bouncycastle2.util.Strings;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.text.ClipboardManager;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class DecryptMessageActivity extends Activity
- implements Runnable, ProgressDialogUpdater,
- AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
- static final int GET_PUCLIC_KEYS = 1;
- static final int GET_SECRET_KEY = 2;
-
- static final int DIALOG_DECRYPTING = 1;
-
- static final int MESSAGE_PROGRESS_UPDATE = 1;
- static final int MESSAGE_DONE = 2;
-
- private long mDecryptionKeyId = 0;
- private long mSignatureKeyId = 0;
-
- private String mReplyTo = null;
- private String mSubject = null;
-
- private ProgressDialog mProgressDialog = null;
- private Thread mRunningThread = null;
-
- private EditText mMessage = null;
- private LinearLayout mSignatureLayout = null;
- private ImageView mSignatureStatusImage = null;
- private TextView mUserId = null;
- private TextView mUserIdRest = null;
- private Button mDecryptButton = null;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Bundle data = msg.getData();
- if (data != null) {
- int type = data.getInt("type");
- switch (type) {
- case MESSAGE_PROGRESS_UPDATE: {
- String message = data.getString("message");
- if (mProgressDialog != null) {
- if (message != null) {
- mProgressDialog.setMessage(message);
- }
- mProgressDialog.setMax(data.getInt("max"));
- mProgressDialog.setProgress(data.getInt("progress"));
- }
- break;
- }
-
- case MESSAGE_DONE: {
- removeDialog(DIALOG_DECRYPTING);
- mProgressDialog = null;
- mSignatureKeyId = 0;
- String error = data.getString("error");
- String decryptedMessage = data.getString("decryptedMessage");
- if (error != null) {
- Toast.makeText(DecryptMessageActivity.this,
- "Error: " + data.getString("error"),
- Toast.LENGTH_SHORT).show();
- }
- mSignatureLayout.setVisibility(View.INVISIBLE);
- if (decryptedMessage != null) {
- mMessage.setText(decryptedMessage);
- mDecryptButton.setText(R.string.btn_reply);
- mDecryptButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- replyClicked();
- }
- });
-
- if (data.getBoolean("signature")) {
- String userId = data.getString("signatureUserId");
- mSignatureKeyId = data.getLong("signatureKeyId");
- mUserIdRest.setText("id: " +
- Long.toHexString(mSignatureKeyId & 0xffffffffL));
- if (userId == null) {
- userId =
- getResources()
- .getString(
- R.string.unknown_user_id);
- }
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mUserIdRest.setText("<" + chunks[1]);
- }
- mUserId.setText(userId);
-
- if (data.getBoolean("signatureSuccess")) {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
- } else if (data.getBoolean("signatureUnknown")) {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- } else {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- }
- mSignatureLayout.setVisibility(View.VISIBLE);
- }
- }
-
- break;
- }
- }
- }
- }
- };
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.decrypt_message);
-
- Apg.initialize(this);
-
- mMessage = (EditText) findViewById(R.id.message);
- mDecryptButton = (Button) findViewById(R.id.btn_decrypt);
- mSignatureLayout = (LinearLayout) findViewById(R.id.layout_signature);
- mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status);
- mUserId = (TextView) findViewById(R.id.main_user_id);
- mUserIdRest = (TextView) findViewById(R.id.main_user_id_rest);
-
- Intent intent = getIntent();
- if (intent.getAction() != null && intent.getAction().equals(Intent.ACTION_VIEW)) {
- Uri uri = intent.getData();
- try {
- InputStream attachment = getContentResolver().openInputStream(uri);
- ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
- byte bytes[] = new byte[1 << 16];
- int length;
- while ((length = attachment.read(bytes)) > 0) {
- byteOut.write(bytes, 0, length);
- }
- byteOut.close();
- String data = Strings.fromUTF8ByteArray(byteOut.toByteArray());
- mMessage.setText(data);
- } catch (FileNotFoundException e) {
- // ignore, then
- } catch (IOException e) {
- // ignore, then
- }
- } else if (intent.getAction() != null && intent.getAction().equals(Apg.Intent.DECRYPT)) {
- String data = intent.getExtras().getString("data");
- if (data != null) {
- Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
- if (matcher.matches()) {
- data = matcher.group(1);
- mMessage.setText(data);
- }
- }
- mReplyTo = intent.getExtras().getString("replyTo");
- mSubject = intent.getExtras().getString("subject");
- } else {
- ClipboardManager clip = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
- String data = "";
- Matcher matcher = Apg.PGP_MESSAGE.matcher(clip.getText());
- if (matcher.matches()) {
- data = matcher.group(1);
- mMessage.setText(data);
- Toast.makeText(this, R.string.using_clipboard_content, Toast.LENGTH_SHORT).show();
- }
- }
-
- mSignatureLayout.setVisibility(View.INVISIBLE);
-
- mDecryptButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- decryptClicked();
- }
- });
-
- if (mMessage.getText().length() > 0) {
- mDecryptButton.performClick();
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case DIALOG_DECRYPTING: {
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setMessage("initializing...");
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- case AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE: {
- return AskForSecretKeyPassPhrase.createDialog(this, mDecryptionKeyId, this);
- }
- }
-
- return super.onCreateDialog(id);
- }
-
- @Override
- public void setProgress(int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
-
- @Override
- public void setProgress(String message, int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putString("message", message);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
-
- private void decryptClicked() {
- String error = null;
- ByteArrayInputStream in =
- new ByteArrayInputStream(mMessage.getText().toString().getBytes());
- try {
- mDecryptionKeyId = Apg.getDecryptionKeyId(in);
- showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
- } catch (IOException e) {
- error = e.getLocalizedMessage();
- } catch (Apg.GeneralException e) {
- error = e.getLocalizedMessage();
- }
- if (error != null) {
- Toast.makeText(this, "Error: " + error, Toast.LENGTH_SHORT).show();
- }
- }
-
- private void replyClicked() {
- Intent intent = new Intent(this, EncryptMessageActivity.class);
- intent.setAction(Apg.Intent.ENCRYPT);
- 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", mDecryptionKeyId);
- intent.putExtra("encryptionKeyIds", new long[] { mSignatureKeyId });
- startActivity(intent);
- }
-
- public void passPhraseCallback(String passPhrase) {
- Apg.setPassPhrase(passPhrase);
- decryptStart();
- }
-
- private void decryptStart() {
- showDialog(DIALOG_DECRYPTING);
- mRunningThread = new Thread(this);
- mRunningThread.start();
- }
-
- public void run() {
- String error = null;
- Security.addProvider(new BouncyCastleProvider());
-
- Bundle data = new Bundle();
- Message msg = new Message();
-
- ByteArrayInputStream in =
- new ByteArrayInputStream(mMessage.getText().toString().getBytes());
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- try {
- data = Apg.decrypt(in, out, Apg.getPassPhrase(), this);
- } catch (PGPException e) {
- error = e.getMessage();
- } catch (IOException e) {
- error = e.getMessage();
- } catch (SignatureException e) {
- error = e.getMessage();
- e.printStackTrace();
- } catch (Apg.GeneralException e) {
- error = e.getMessage();
- }
-
- data.putInt("type", MESSAGE_DONE);
-
- if (error != null) {
- data.putString("error", error);
- } else {
- data.putString("decryptedMessage", Strings.fromUTF8ByteArray(out.toByteArray()));
- }
-
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
-}
diff --git a/src/org/thialfihar/android/apg/EditKeyActivity.java b/src/org/thialfihar/android/apg/EditKeyActivity.java
index b5b7045b7..b1e37e8a1 100644
--- a/src/org/thialfihar/android/apg/EditKeyActivity.java
+++ b/src/org/thialfihar/android/apg/EditKeyActivity.java
@@ -24,43 +24,28 @@ import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
+import org.thialfihar.android.apg.ui.widget.KeyEditor;
import org.thialfihar.android.apg.ui.widget.SectionView;
import org.thialfihar.android.apg.utils.IterableIterator;
-import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Handler;
import android.os.Message;
-import android.text.InputType;
-import android.text.method.PasswordTransformationMethod;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
-public class EditKeyActivity extends Activity
- implements OnClickListener, ProgressDialogUpdater, Runnable {
- static final int OPTION_MENU_NEW_PASS_PHRASE = 1;
-
- static final int DIALOG_NEW_PASS_PHRASE = 1;
- static final int DIALOG_PASS_PHRASES_DO_NOT_MATCH = 2;
- static final int DIALOG_NO_PASS_PHRASE = 3;
- static final int DIALOG_SAVING = 4;
-
- static final int MESSAGE_PROGRESS_UPDATE = 1;
- static final int MESSAGE_DONE = 2;
+public class EditKeyActivity extends BaseActivity implements OnClickListener {
private PGPSecretKeyRing mKeyRing = null;
@@ -70,56 +55,9 @@ public class EditKeyActivity extends Activity
private Button mSaveButton;
private Button mDiscardButton;
- private ProgressDialog mProgressDialog = null;
- private Thread mRunningThread = null;
-
+ private String mCurrentPassPhrase = null;
private String mNewPassPhrase = null;
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Bundle data = msg.getData();
- if (data != null) {
- int type = data.getInt("type");
- switch (type) {
- case MESSAGE_PROGRESS_UPDATE: {
- String message = data.getString("message");
- if (mProgressDialog != null) {
- if (message != null) {
- mProgressDialog.setMessage(message);
- }
- mProgressDialog.setMax(data.getInt("max"));
- mProgressDialog.setProgress(data.getInt("progress"));
- }
- break;
- }
-
- case MESSAGE_DONE: {
- removeDialog(DIALOG_SAVING);
- mProgressDialog = null;
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(EditKeyActivity.this,
- "Error: " + data.getString("error"),
- Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(EditKeyActivity.this, R.string.key_saved,
- Toast.LENGTH_SHORT).show();
- setResult(RESULT_OK);
- finish();
- }
- break;
- }
-
- default: {
- break;
- }
- }
- }
- }
- };
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -133,6 +71,7 @@ public class EditKeyActivity extends Activity
if (intent.getExtras() != null) {
keyId = intent.getExtras().getLong("keyId");
}
+
if (keyId != 0) {
PGPSecretKey masterKey = null;
mKeyRing = Apg.getSecretKeyRing(keyId);
@@ -149,10 +88,6 @@ public class EditKeyActivity extends Activity
}
}
- if (Apg.getPassPhrase() == null) {
- Apg.setPassPhrase("");
- }
-
mSaveButton = (Button) findViewById(R.id.btn_save);
mDiscardButton = (Button) findViewById(R.id.btn_discard);
@@ -164,105 +99,95 @@ public class EditKeyActivity extends Activity
LinearLayout container = (LinearLayout) findViewById(R.id.container);
mUserIds = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
- mUserIds.setType(SectionView.TYPE_USER_ID);
+ mUserIds.setType(Id.type.user_id);
mUserIds.setUserIds(userIds);
container.addView(mUserIds);
mKeys = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
- mKeys.setType(SectionView.TYPE_KEY);
+ mKeys.setType(Id.type.key);
mKeys.setKeys(keys);
container.addView(mKeys);
+ mCurrentPassPhrase = Apg.getEditPassPhrase();
+ if (mCurrentPassPhrase == null) {
+ mCurrentPassPhrase = "";
+ }
+
Toast.makeText(this, "Warning: Key editing is still kind of beta.", Toast.LENGTH_LONG).show();
}
+ public long getMasterKeyId() {
+ if (mKeys.getEditors().getChildCount() == 0) {
+ return 0;
+ }
+ return ((KeyEditor) mKeys.getEditors().getChildAt(0)).getValue().getKeyID();
+ }
+
public boolean havePassPhrase() {
- return (Apg.getPassPhrase() != null && !Apg.getPassPhrase().equals("")) ||
- (mNewPassPhrase != null && mNewPassPhrase.equals(""));
+ return (!mCurrentPassPhrase.equals("")) ||
+ (mNewPassPhrase != null && !mNewPassPhrase.equals(""));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, OPTION_MENU_NEW_PASS_PHRASE, 0,
- (havePassPhrase() ? "Change Pass Phrase" : "Set Pass Phrase"))
+ menu.add(0, Id.menu.option.new_pass_phrase, 0,
+ (havePassPhrase() ? R.string.menu_changePassPhrase : R.string.menu_setPassPhrase))
.setIcon(android.R.drawable.ic_menu_add);
+ menu.add(0, Id.menu.option.preferences, 1, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(0, Id.menu.option.about, 2, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case OPTION_MENU_NEW_PASS_PHRASE: {
- showDialog(DIALOG_NEW_PASS_PHRASE);
+ case Id.menu.option.new_pass_phrase: {
+ showDialog(Id.dialog.new_pass_phrase);
return true;
}
default: {
- break;
+ return super.onOptionsItemSelected(item);
}
}
- return false;
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
- case DIALOG_SAVING: {
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setMessage("saving...");
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- case DIALOG_NEW_PASS_PHRASE: {
+ case Id.dialog.new_pass_phrase: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
if (havePassPhrase()) {
- alert.setTitle("Change Pass Phrase");
+ alert.setTitle(R.string.title_changePassPhrase);
} else {
- alert.setTitle("Set Pass Phrase");
+ alert.setTitle(R.string.title_setPassPhrase);
}
- alert.setMessage("Enter the pass phrase twice.");
-
- final EditText input1 = new EditText(this);
- final EditText input2 = new EditText(this);
- input1.setText("");
- input2.setText("");
- input1.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
- input2.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
- input1.setTransformationMethod(new PasswordTransformationMethod());
- input2.setTransformationMethod(new PasswordTransformationMethod());
-
- // 5dip padding
- int padding = (int) (10 * getResources().getDisplayMetrics().densityDpi / 160);
- LinearLayout layout = new LinearLayout(this);
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.setPadding(padding, 0, padding, 0);
- layout.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.WRAP_CONTENT));
- input1.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.WRAP_CONTENT));
- input2.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,
- LayoutParams.WRAP_CONTENT));
- layout.addView(input1);
- layout.addView(input2);
-
- alert.setView(layout);
+ alert.setMessage(R.string.enterPassPhraseTwice);
+
+ LayoutInflater inflater =
+ (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.pass_phrase, null);
+ final EditText input1 = (EditText) view.findViewById(R.id.passPhrase);
+ final EditText input2 = (EditText) view.findViewById(R.id.passPhraseAgain);
+
+ alert.setView(view);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_NEW_PASS_PHRASE);
+ removeDialog(Id.dialog.new_pass_phrase);
String passPhrase1 = "" + input1.getText();
String passPhrase2 = "" + input2.getText();
if (!passPhrase1.equals(passPhrase2)) {
- showDialog(DIALOG_PASS_PHRASES_DO_NOT_MATCH);
+ showDialog(Id.dialog.pass_phrases_do_not_match);
return;
}
if (passPhrase1.equals("")) {
- showDialog(DIALOG_NO_PASS_PHRASE);
+ showDialog(Id.dialog.no_pass_phrase);
return;
}
@@ -273,54 +198,17 @@ public class EditKeyActivity extends Activity
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_NEW_PASS_PHRASE);
- }
- });
-
- return alert.create();
- }
-
- case DIALOG_PASS_PHRASES_DO_NOT_MATCH: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle("Error");
- alert.setMessage("The pass phrases didn't match.");
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_PASS_PHRASES_DO_NOT_MATCH);
- }
- });
- alert.setCancelable(false);
-
- return alert.create();
- }
-
- case DIALOG_NO_PASS_PHRASE: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle("Error");
- alert.setMessage("Empty pass phrases are not supported.");
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_NO_PASS_PHRASE);
+ removeDialog(Id.dialog.new_pass_phrase);
}
});
- alert.setCancelable(false);
return alert.create();
}
default: {
- break;
+ return super.onCreateDialog(id);
}
}
- return super.onCreateDialog(id);
}
@Override
@@ -334,23 +222,22 @@ public class EditKeyActivity extends Activity
}
private void saveClicked() {
- if ((Apg.getPassPhrase() == null || Apg.getPassPhrase().equals("")) &&
- (mNewPassPhrase == null || mNewPassPhrase.equals(""))) {
- Toast.makeText(this, R.string.set_a_pass_phrase, Toast.LENGTH_SHORT).show();
+ if (!havePassPhrase()) {
+ Toast.makeText(this, R.string.setAPassPhrase, Toast.LENGTH_SHORT).show();
return;
}
- showDialog(DIALOG_SAVING);
- mRunningThread = new Thread(this);
- mRunningThread.start();
+ showDialog(Id.dialog.saving);
+ startThread();
}
+ @Override
public void run() {
String error = null;
Bundle data = new Bundle();
Message msg = new Message();
try {
- String oldPassPhrase = Apg.getPassPhrase();
+ String oldPassPhrase = mCurrentPassPhrase;
String newPassPhrase = mNewPassPhrase;
if (newPassPhrase == null) {
newPassPhrase = oldPassPhrase;
@@ -368,34 +255,32 @@ public class EditKeyActivity extends Activity
error = e.getMessage();
}
- data.putInt("type", MESSAGE_DONE);
+ data.putInt("type", Id.message.done);
if (error != null) {
data.putString("error", error);
}
msg.setData(data);
- mHandler.sendMessage(msg);
+ sendMessage(msg);
}
- public void setProgress(int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
- public void setProgress(String message, int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putString("message", message);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
+ Bundle data = msg.getData();
+ removeDialog(Id.dialog.saving);
+
+ String error = data.getString("error");
+ if (error != null) {
+ Toast.makeText(EditKeyActivity.this,
+ getString(R.string.errorMessage, data.getString("error")),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
+ setResult(RESULT_OK);
+ finish();
+ }
}
} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/EncryptActivity.java b/src/org/thialfihar/android/apg/EncryptActivity.java
new file mode 100644
index 000000000..073e9dc9c
--- /dev/null
+++ b/src/org/thialfihar/android/apg/EncryptActivity.java
@@ -0,0 +1,810 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
+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.bouncycastle2.util.Strings;
+import org.openintents.intents.FileManager;
+import org.thialfihar.android.apg.Apg.GeneralException;
+import org.thialfihar.android.apg.utils.Choice;
+
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Message;
+import android.text.ClipboardManager;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.animation.AnimationUtils;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.ViewFlipper;
+
+public class EncryptActivity extends BaseActivity {
+ private String mSubject = null;
+ private String mSendTo = null;
+
+ private long mEncryptionKeyIds[] = null;
+
+ private EditText mMessage = null;
+ private Button mSelectKeysButton = null;
+ private Button mEncryptButton = null;
+ private Button mEncryptToClipboardButton = null;
+ private CheckBox mSign = null;
+ private TextView mMainUserId = null;
+ private TextView mMainUserIdRest = null;
+
+ private ViewFlipper mSource = null;
+ private TextView mSourceLabel = null;
+ private ImageView mSourcePrevious = null;
+ private ImageView mSourceNext = null;
+
+ private ViewFlipper mMode = null;
+ private TextView mModeLabel = null;
+ private ImageView mModePrevious = null;
+ private ImageView mModeNext = null;
+
+ private int mEncryptTarget;
+
+ private EditText mPassPhrase = null;
+ private EditText mPassPhraseAgain = null;
+ private CheckBox mAsciiArmour = null;
+ private Spinner mFileCompression = null;
+
+ private EditText mFilename = null;
+ private CheckBox mDeleteAfter = null;
+ private ImageButton mBrowse = null;
+
+ private String mInputFilename = null;
+ private String mOutputFilename = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.encrypt);
+
+ mSource = (ViewFlipper) findViewById(R.id.source);
+ mSourceLabel = (TextView) findViewById(R.id.sourceLabel);
+ mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious);
+ mSourceNext = (ImageView) findViewById(R.id.sourceNext);
+
+ mSourcePrevious.setClickable(true);
+ mSourcePrevious.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_right_in));
+ mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_right_out));
+ mSource.showPrevious();
+ updateSource();
+ }
+ });
+
+ mSourceNext.setClickable(true);
+ OnClickListener nextSourceClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_left_in));
+ mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_left_out));
+ mSource.showNext();
+ updateSource();
+ }
+ };
+ mSourceNext.setOnClickListener(nextSourceClickListener);
+
+ mSourceLabel.setClickable(true);
+ mSourceLabel.setOnClickListener(nextSourceClickListener);
+
+ mMode = (ViewFlipper) findViewById(R.id.mode);
+ mModeLabel = (TextView) findViewById(R.id.modeLabel);
+ mModePrevious = (ImageView) findViewById(R.id.modePrevious);
+ mModeNext = (ImageView) findViewById(R.id.modeNext);
+
+ mModePrevious.setClickable(true);
+ mModePrevious.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_right_in));
+ mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_right_out));
+ mMode.showPrevious();
+ updateMode();
+ }
+ });
+
+ OnClickListener nextModeClickListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_left_in));
+ mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
+ R.anim.push_left_out));
+ mMode.showNext();
+ updateMode();
+ }
+ };
+ mModeNext.setOnClickListener(nextModeClickListener);
+
+ mModeLabel.setClickable(true);
+ mModeLabel.setOnClickListener(nextModeClickListener);
+
+ mMessage = (EditText) findViewById(R.id.message);
+ mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
+ mEncryptButton = (Button) findViewById(R.id.btn_encrypt);
+ mEncryptToClipboardButton = (Button) findViewById(R.id.btn_encryptToClipboard);
+ mSign = (CheckBox) findViewById(R.id.sign);
+ mMainUserId = (TextView) findViewById(R.id.mainUserId);
+ mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
+
+ mPassPhrase = (EditText) findViewById(R.id.passPhrase);
+ mPassPhraseAgain = (EditText) findViewById(R.id.passPhraseAgain);
+
+ // measure the height of the source_file view and set the message view's min height to that,
+ // so it fills mSource fully... bit of a hack.
+ View tmp = findViewById(R.id.sourceFile);
+ tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ int height = tmp.getMeasuredHeight();
+ mMessage.setMinimumHeight(height);
+
+ mFilename = (EditText) findViewById(R.id.filename);
+ mBrowse = (ImageButton) findViewById(R.id.btn_browse);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ openFile();
+ }
+ });
+
+ mFileCompression = (Spinner) findViewById(R.id.fileCompression);
+ Choice[] choices = new Choice[] {
+ new Choice(Id.choice.compression.none, getString(R.string.choice_none)),
+ new Choice(Id.choice.compression.zip, "ZIP"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2"),
+ new Choice(Id.choice.compression.zlib, "ZLIB"),
+ };
+ ArrayAdapter<Choice> adapter =
+ new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mFileCompression.setAdapter(adapter);
+
+ int defaultFileCompression = getDefaultFileCompression();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == defaultFileCompression) {
+ mFileCompression.setSelection(i);
+ break;
+ }
+ }
+
+ mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
+
+ mAsciiArmour = (CheckBox) findViewById(R.id.asciiArmour);
+ mAsciiArmour.setChecked(getDefaultAsciiArmour());
+ mAsciiArmour.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ guessOutputFilename();
+ }
+ });
+
+ mEncryptToClipboardButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptToClipboardClicked();
+ }
+ });
+
+ mEncryptButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptClicked();
+ }
+ });
+
+ mSelectKeysButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ selectPublicKeys();
+ }
+ });
+
+ mSign.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ CheckBox checkBox = (CheckBox) v;
+ if (checkBox.isChecked()) {
+ selectSecretKey();
+ } else {
+ setSecretKeyId(Id.key.none);
+ updateView();
+ }
+ }
+ });
+
+ Intent intent = getIntent();
+ if (intent.getAction() != null &&
+ (intent.getAction().equals(Apg.Intent.ENCRYPT) ||
+ intent.getAction().equals(Apg.Intent.ENCRYPT_FILE))) {
+ Bundle extras = intent.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 (signatureKeyId != 0) {
+ PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId);
+ PGPSecretKey masterKey = null;
+ if (keyRing != null) {
+ masterKey = Apg.getMasterKey(keyRing);
+ if (masterKey != null) {
+ Vector<PGPSecretKey> signKeys = Apg.getUsableSigningKeys(keyRing);
+ if (signKeys.size() > 0) {
+ setSecretKeyId(masterKey.getKeyID());
+ }
+ }
+ }
+ }
+
+ if (encryptionKeyIds != null) {
+ Vector<Long> goodIds = new Vector<Long>();
+ for (int i = 0; i < encryptionKeyIds.length; ++i) {
+ PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]);
+ PGPPublicKey masterKey = null;
+ if (keyRing == null) {
+ continue;
+ }
+ masterKey = Apg.getMasterKey(keyRing);
+ if (masterKey == null) {
+ continue;
+ }
+ Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
+ if (encryptKeys.size() == 0) {
+ continue;
+ }
+ goodIds.add(masterKey.getKeyID());
+ }
+ if (goodIds.size() > 0) {
+ mEncryptionKeyIds = new long[goodIds.size()];
+ for (int i = 0; i < goodIds.size(); ++i) {
+ mEncryptionKeyIds[i] = goodIds.get(i);
+ }
+ }
+ }
+
+ if (intent.getAction().equals(Apg.Intent.ENCRYPT)) {
+ if (data != null) {
+ mMessage.setText(data);
+ }
+ mSource.setInAnimation(null);
+ mSource.setOutAnimation(null);
+ while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
+ mSource.showNext();
+ }
+ } else if (intent.getAction().equals(Apg.Intent.ENCRYPT_FILE)) {
+ mSource.setInAnimation(null);
+ mSource.setOutAnimation(null);
+ while (mSource.getCurrentView().getId() != R.id.sourceFile) {
+ mSource.showNext();
+ }
+ }
+ }
+
+ updateView();
+ updateSource();
+ updateMode();
+ }
+
+ private void openFile() {
+ String filename = mFilename.getText().toString();
+
+ Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
+
+ 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);
+
+ 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();
+ }
+ }
+
+ private void guessOutputFilename() {
+ mInputFilename = mFilename.getText().toString();
+ File file = new File(mInputFilename);
+ String ending = (mAsciiArmour.isChecked() ? ".asc" : ".gpg");
+ mOutputFilename = Constants.path.app_dir + "/" + file.getName() + ending;
+ }
+
+ private void updateSource() {
+ switch (mSource.getCurrentView().getId()) {
+ case R.id.sourceFile: {
+ mSourceLabel.setText(R.string.label_file);
+ mEncryptButton.setText(R.string.btn_encrypt);
+ mEncryptToClipboardButton.setEnabled(false);
+ break;
+ }
+
+ case R.id.sourceMessage: {
+ mSourceLabel.setText(R.string.label_message);
+ mEncryptButton.setText(R.string.btn_send);
+ mEncryptToClipboardButton.setEnabled(true);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+
+ private void updateMode() {
+ switch (mMode.getCurrentView().getId()) {
+ case R.id.modeAsymmetric: {
+ mModeLabel.setText(R.string.label_asymmetric);
+ break;
+ }
+
+ case R.id.modeSymmetric: {
+ mModeLabel.setText(R.string.label_symmetric);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+
+ private void encryptToClipboardClicked() {
+ mEncryptTarget = Id.target.clipboard;
+ initiateEncryption();
+ }
+
+ private void encryptClicked() {
+ if (mSource.getCurrentView().getId() == R.id.sourceFile) {
+ mEncryptTarget = Id.target.file;
+ } else {
+ mEncryptTarget = Id.target.email;
+ }
+ initiateEncryption();
+ }
+
+ private void initiateEncryption() {
+ if (mEncryptTarget == Id.target.file) {
+ String currentFilename = mFilename.getText().toString();
+ if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
+ guessOutputFilename();
+ }
+
+ if (mInputFilename.equals("")) {
+ Toast.makeText(this, R.string.noFileSelected, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ File file = new File(mInputFilename);
+ if (!file.exists() || !file.isFile()) {
+ Toast.makeText(this, getString(R.string.errorMessage,
+ getString(R.string.error_fileNotFound)),
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ // symmetric encryption
+ if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
+ boolean gotPassPhrase = false;
+ String passPhrase = mPassPhrase.getText().toString();
+ String passPhraseAgain = mPassPhraseAgain.getText().toString();
+ if (!passPhrase.equals(passPhraseAgain)) {
+ Toast.makeText(this, R.string.passPhrasesDoNotMatch, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ gotPassPhrase = (passPhrase.length() != 0);
+ if (!gotPassPhrase) {
+ Toast.makeText(this, R.string.passPhraseMustNotBeEmpty, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ } else {
+ 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();
+ return;
+ }
+
+ if (!encryptIt && getSecretKeyId() == 0) {
+ Toast.makeText(this, R.string.selectEncryptionOrSignatureKey,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if (getSecretKeyId() != 0 && Apg.getCachedPassPhrase(getSecretKeyId()) == null) {
+ showDialog(Id.dialog.pass_phrase);
+ return;
+ }
+ }
+
+ if (mEncryptTarget == Id.target.file) {
+ askForOutputFilename();
+ } else {
+ encryptStart();
+ }
+ }
+
+ private void askForOutputFilename() {
+ showDialog(Id.dialog.output_filename);
+ }
+
+ @Override
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ super.passPhraseCallback(keyId, passPhrase);
+ if (mEncryptTarget == Id.target.file) {
+ askForOutputFilename();
+ } else {
+ encryptStart();
+ }
+ }
+
+ private void encryptStart() {
+ showDialog(Id.dialog.encrypting);
+ startThread();
+ }
+
+ @Override
+ public void run() {
+ String error = null;
+ Bundle data = new Bundle();
+ Message msg = new Message();
+
+ try {
+ InputStream in;
+ OutputStream out;
+ long size;
+ boolean useAsciiArmour = true;
+ long encryptionKeyIds[] = null;
+ long signatureKeyId = 0;
+ boolean signOnly = false;
+ int compressionId = 0;
+
+ String passPhrase = null;
+ if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
+ passPhrase = mPassPhrase.getText().toString();
+ if (passPhrase.length() == 0) {
+ passPhrase = null;
+ }
+ } else {
+ encryptionKeyIds = mEncryptionKeyIds;
+ signatureKeyId = getSecretKeyId();
+ signOnly = mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0;
+ }
+
+ if (mEncryptTarget == Id.target.file) {
+ if (mInputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
+ mOutputFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(getString(R.string.error_externalStorageNotReady));
+ }
+ }
+
+ in = new FileInputStream(mInputFilename);
+ out = new FileOutputStream(mOutputFilename);
+
+ File file = new File(mInputFilename);
+ size = file.length();
+ useAsciiArmour = mAsciiArmour.isChecked();
+ compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
+ } else {
+ String message = mMessage.getText().toString();
+
+ if (signOnly) {
+ // 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
+ message = message.replaceAll(" +\n", "\n");
+ message = message.replaceAll("\n\n+", "\n\n");
+ message = message.replaceFirst("^\n+", "");
+ // make sure there'll be exactly one newline at the end
+ message = message.replaceFirst("\n*$", "\n");
+ }
+
+ byte[] byteData = Strings.toUTF8ByteArray(message);
+ in = new ByteArrayInputStream(byteData);
+ out = new ByteArrayOutputStream();
+
+ size = byteData.length;
+ useAsciiArmour = true;
+ compressionId = getDefaultMessageCompression();
+ }
+
+ if (signOnly) {
+ Apg.signText(this, in, out, getSecretKeyId(),
+ Apg.getCachedPassPhrase(getSecretKeyId()),
+ getDefaultHashAlgorithm(), this);
+ } else {
+ Apg.encrypt(this, in, out, size, useAsciiArmour,
+ encryptionKeyIds, signatureKeyId,
+ Apg.getCachedPassPhrase(signatureKeyId), this,
+ getDefaultEncryptionAlgorithm(), getDefaultHashAlgorithm(),
+ compressionId, passPhrase);
+ }
+
+ out.close();
+ if (mEncryptTarget != Id.target.file) {
+ data.putString("message", new String(((ByteArrayOutputStream)out).toByteArray()));
+ }
+ } catch (IOException e) {
+ error = e.getMessage();
+ } catch (PGPException e) {
+ error = e.getMessage();
+ } catch (NoSuchProviderException e) {
+ error = e.getMessage();
+ } catch (NoSuchAlgorithmException e) {
+ error = e.getMessage();
+ } catch (SignatureException e) {
+ error = e.getMessage();
+ } catch (Apg.GeneralException e) {
+ error = e.getMessage();
+ }
+
+ data.putInt("type", Id.message.done);
+
+ if (error != null) {
+ data.putString("error", error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ private void updateView() {
+ if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
+ mSelectKeysButton.setText(R.string.noKeysSelected);
+ } else if (mEncryptionKeyIds.length == 1) {
+ mSelectKeysButton.setText(R.string.oneKeySelected);
+ } else {
+ mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " +
+ getResources().getString(R.string.nKeysSelected));
+ }
+
+ if (getSecretKeyId() == 0) {
+ mSign.setChecked(false);
+ mMainUserId.setText("");
+ mMainUserIdRest.setText("");
+ } else {
+ String uid = getResources().getString(R.string.unknownUserId);
+ String uidExtra = "";
+ PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(getSecretKeyId());
+ if (keyRing != null) {
+ PGPSecretKey key = Apg.getMasterKey(keyRing);
+ if (key != null) {
+ String userId = Apg.getMainUserIdSafe(this, key);
+ String chunks[] = userId.split(" <", 2);
+ uid = chunks[0];
+ if (chunks.length > 1) {
+ uidExtra = "<" + chunks[1];
+ }
+ }
+ }
+ mMainUserId.setText(uid);
+ mMainUserIdRest.setText(uidExtra);
+ mSign.setChecked(true);
+ }
+ }
+
+ private void selectPublicKeys() {
+ Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
+ intent.putExtra("selection", mEncryptionKeyIds);
+ startActivityForResult(intent, Id.request.public_keys);
+ }
+
+ private void selectSecretKey() {
+ Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
+ startActivityForResult(intent, Id.request.secret_keys);
+ }
+
+ @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);
+
+ mFilename.setText(filename);
+ }
+ }
+ return;
+ }
+
+ case Id.request.output_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;
+ }
+
+ case Id.request.secret_keys: {
+ if (resultCode == RESULT_OK) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ updateView();
+ break;
+ }
+
+ case Id.request.public_keys: {
+ if (resultCode == RESULT_OK) {
+ Bundle bundle = data.getExtras();
+ mEncryptionKeyIds = bundle.getLongArray("selection");
+ }
+ updateView();
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ removeDialog(Id.dialog.encrypting);
+
+ Bundle data = msg.getData();
+ String error = data.getString("error");
+ if (error != null) {
+ Toast.makeText(EncryptActivity.this,
+ getString(R.string.errorMessage, data.getString("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;
+ }
+
+ 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.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;
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case Id.dialog.output_filename: {
+ return FileDialog.build(this, getString(R.string.title_encryptToFile),
+ getString(R.string.specifyFileToEncryptTo),
+ mOutputFilename,
+ new FileDialog.OnClickListener() {
+
+ @Override
+ public void onOkClick(String filename) {
+ removeDialog(Id.dialog.output_filename);
+ mOutputFilename = filename;
+ encryptStart();
+ }
+
+ @Override
+ public void onCancelClick() {
+ removeDialog(Id.dialog.output_filename);
+ }
+ },
+ getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave),
+ Id.request.output_filename);
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return super.onCreateDialog(id);
+ }
+} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/EncryptMessageActivity.java b/src/org/thialfihar/android/apg/EncryptMessageActivity.java
deleted file mode 100644
index 27e4c29be..000000000
--- a/src/org/thialfihar/android/apg/EncryptMessageActivity.java
+++ /dev/null
@@ -1,428 +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;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SignatureException;
-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.bouncycastle2.util.Strings;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class EncryptMessageActivity extends Activity
- implements Runnable, ProgressDialogUpdater,
- AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
- static final int GET_PUCLIC_KEYS = 1;
- static final int GET_SECRET_KEY = 2;
-
- static final int DIALOG_ENCRYPTING = 1;
-
- static final int MESSAGE_PROGRESS_UPDATE = 1;
- static final int MESSAGE_DONE = 2;
-
- private String mSubject = null;
- private String mSendTo = null;
-
- private long mEncryptionKeyIds[] = null;
- private long mSignatureKeyId = 0;
-
- private ProgressDialog mProgressDialog = null;
- private Thread mRunningThread = null;
-
- private EditText mMessage = null;
- private Button mSelectKeysButton = null;
- private Button mSendButton = null;
- private CheckBox mSign = null;
- private TextView mMainUserId = null;
- private TextView mMainUserIdRest = null;
-
- private Handler mhandler = new Handler() {
- @Override
- public void handleMessage(Message mSg) {
- Bundle data = mSg.getData();
- if (data != null) {
- int type = data.getInt("type");
- switch (type) {
- case MESSAGE_PROGRESS_UPDATE: {
- String message = data.getString("message");
- if (mProgressDialog != null) {
- if (message != null) {
- mProgressDialog.setMessage(message);
- }
- mProgressDialog.setMax(data.getInt("max"));
- mProgressDialog.setProgress(data.getInt("progress"));
- }
- break;
- }
-
- case MESSAGE_DONE: {
- removeDialog(DIALOG_ENCRYPTING);
- mProgressDialog = null;
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(EncryptMessageActivity.this,
- "Error: " + data.getString("error"),
- Toast.LENGTH_SHORT).show();
- return;
- } else {
- String message = data.getString("message");
- String signature = data.getString("signature");
- 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 (signature != null) {
- String fullText = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
- "Hash: SHA256\n" + "\n" +
- message + "\n" + signature;
- emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, fullText);
- }
- if (mSubject != null) {
- emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
- mSubject);
- }
- if (mSendTo != null) {
- emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
- new String[] { mSendTo });
- }
- EncryptMessageActivity.this.
- startActivity(Intent.createChooser(emailIntent, "Send mail..."));
- }
- break;
- }
-
- default: {
- break;
- }
- }
- }
- }
- };
-
- @Override
- public void setProgress(int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mhandler.sendMessage(msg);
- }
-
- @Override
- public void setProgress(String message, int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putString("message", message);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mhandler.sendMessage(msg);
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.encrypt_message);
-
- Apg.initialize(this);
-
- mMessage = (EditText) findViewById(R.id.message);
- mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
- mSendButton = (Button) findViewById(R.id.btn_send);
- mSign = (CheckBox) findViewById(R.id.sign);
- mMainUserId = (TextView) findViewById(R.id.main_user_id);
- mMainUserIdRest = (TextView) findViewById(R.id.main_user_id_rest);
-
- Intent intent = getIntent();
- if (intent.getAction() != null &&
- intent.getAction().equals(Apg.Intent.ENCRYPT)) {
- String data = intent.getExtras().getString("data");
- mSendTo = intent.getExtras().getString("sendTo");
- mSubject = intent.getExtras().getString("subject");
- long signatureKeyId = intent.getExtras().getLong("signatureKeyId");
- long encryptionKeyIds[] = intent.getExtras().getLongArray("encryptionKeyIds");
- if (signatureKeyId != 0) {
- PGPSecretKeyRing keyRing = Apg.findSecretKeyRing(signatureKeyId);
- PGPSecretKey masterKey = null;
- if (keyRing != null) {
- masterKey = Apg.getMasterKey(keyRing);
- if (masterKey != null) {
- Vector<PGPSecretKey> signKeys = Apg.getUsableSigningKeys(keyRing);
- if (signKeys.size() > 0) {
- mSignatureKeyId = masterKey.getKeyID();
- }
- }
- }
- }
-
- if (encryptionKeyIds != null) {
- Vector<Long> goodIds = new Vector<Long>();
- for (int i = 0; i < encryptionKeyIds.length; ++i) {
- PGPPublicKeyRing keyRing = Apg.findPublicKeyRing(encryptionKeyIds[i]);
- PGPPublicKey masterKey = null;
- if (keyRing == null) {
- continue;
- }
- masterKey = Apg.getMasterKey(keyRing);
- if (masterKey == null) {
- continue;
- }
- Vector<PGPPublicKey> encryptKeys = Apg.getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- continue;
- }
- goodIds.add(masterKey.getKeyID());
- }
- if (goodIds.size() > 0) {
- mEncryptionKeyIds = new long[goodIds.size()];
- for (int i = 0; i < goodIds.size(); ++i) {
- mEncryptionKeyIds[i] = goodIds.get(i);
- }
- }
- }
- if (data != null) {
- mMessage.setText(data);
- }
- }
-
- mSendButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- sendClicked();
- }
- });
-
- mSelectKeysButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- selectPublicKeys();
- }
- });
-
- mSign.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- CheckBox checkBox = (CheckBox) v;
- if (checkBox.isChecked()) {
- selectSecretKey();
- } else {
- mSignatureKeyId = 0;
- Apg.setPassPhrase(null);
- updateView();
- }
- }
- });
-
- updateView();
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case DIALOG_ENCRYPTING: {
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setMessage("initializing...");
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- case AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE: {
- return AskForSecretKeyPassPhrase.createDialog(this, mSignatureKeyId, this);
- }
- }
-
- return super.onCreateDialog(id);
- }
-
- private void sendClicked() {
- if (mSignatureKeyId != 0 && Apg.getPassPhrase() == null) {
- showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
- } else {
- encryptStart();
- }
- }
-
- public void passPhraseCallback(String passPhrase) {
- Apg.setPassPhrase(passPhrase);
- encryptStart();
- }
-
- private void encryptStart() {
- showDialog(DIALOG_ENCRYPTING);
- mRunningThread = new Thread(this);
- mRunningThread.start();
- }
-
- public void run() {
- String error = null;
- Bundle data = new Bundle();
- Message msg = new Message();
- String message = mMessage.getText().toString();
- // 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
- message = message.replaceAll(" +\n", "\n");
- message = message.replaceAll("\n\n+", "\n\n");
- message = message.replaceFirst("^\n+", "");
- message = message.replaceFirst("\n+$", "");
- ByteArrayInputStream in =
- new ByteArrayInputStream(Strings.toUTF8ByteArray(message));
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- try {
- if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
- Apg.encrypt(in, out, mEncryptionKeyIds, mSignatureKeyId, Apg.getPassPhrase(), this);
- data.putString("message", new String(out.toByteArray()));
- } else {
- Apg.sign(in, out, mSignatureKeyId, Apg.getPassPhrase(), this);
- data.putString("message", message);
- data.putString("signature", new String(out.toByteArray()));
- }
- } catch (IOException e) {
- error = e.getMessage();
- } catch (PGPException e) {
- error = e.getMessage();
- } catch (NoSuchProviderException e) {
- error = e.getMessage();
- } catch (NoSuchAlgorithmException e) {
- error = e.getMessage();
- } catch (SignatureException e) {
- error = e.getMessage();
- } catch (Apg.GeneralException e) {
- error = e.getMessage();
- }
-
- data.putInt("type", MESSAGE_DONE);
-
- if (error != null) {
- data.putString("error", error);
- }
-
- msg.setData(data);
- mhandler.sendMessage(msg);
- }
-
- private void updateView() {
- if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- mSelectKeysButton.setText(R.string.no_keys_selected);
- } else if (mEncryptionKeyIds.length == 1) {
- mSelectKeysButton.setText(R.string.one_key_selected);
- } else {
- mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " " +
- getResources().getString(R.string.n_keys_selected));
- }
-
- if (mSignatureKeyId == 0) {
- mSign.setText(R.string.sign);
- mSign.setChecked(false);
- mMainUserId.setText("");
- mMainUserIdRest.setText("");
- } else {
- String uid = getResources().getString(R.string.unknown_user_id);
- String uidExtra = "";
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(mSignatureKeyId);
- if (keyRing != null) {
- PGPSecretKey key = Apg.getMasterKey(keyRing);
- if (key != null) {
- String userId = Apg.getMainUserIdSafe(this, key);
- String chunks[] = userId.split(" <", 2);
- uid = chunks[0];
- if (chunks.length > 1) {
- uidExtra = "<" + chunks[1];
- }
- }
- }
- mMainUserId.setText(uid);
- mMainUserIdRest.setText(uidExtra);
- mSign.setText(R.string.sign_as);
- mSign.setChecked(true);
- }
- }
-
- private void selectPublicKeys() {
- Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
- intent.putExtra("selection", mEncryptionKeyIds);
- startActivityForResult(intent, GET_PUCLIC_KEYS);
- }
-
- private void selectSecretKey() {
- Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
- startActivityForResult(intent, GET_SECRET_KEY);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case GET_PUCLIC_KEYS: {
- if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- mEncryptionKeyIds = bundle.getLongArray("selection");
- updateView();
- }
- break;
- }
-
- case GET_SECRET_KEY: {
- if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- long newId = bundle.getLong("selectedKeyId");
- if (mSignatureKeyId != newId) {
- Apg.setPassPhrase(null);
- }
- mSignatureKeyId = newId;
- } else {
- mSignatureKeyId = 0;
- Apg.setPassPhrase(null);
- }
- updateView();
- break;
- }
-
- default:
- break;
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/FileDialog.java b/src/org/thialfihar/android/apg/FileDialog.java
new file mode 100644
index 000000000..22d64fc84
--- /dev/null
+++ b/src/org/thialfihar/android/apg/FileDialog.java
@@ -0,0 +1,119 @@
+/*
+ * 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 org.openintents.intents.FileManager;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.Toast;
+
+public class FileDialog {
+ private static EditText mFilename;
+ private static ImageButton mBrowse;
+ private static Activity mActivity;
+ private static String mFileManagerTitle;
+ private static String mFileManagerButton;
+ private static int mRequestCode;
+
+ public static interface OnClickListener {
+ public void onCancelClick();
+ public void onOkClick(String filename);
+ }
+
+ public static AlertDialog build(Activity activity, String title, String message,
+ String defaultFile, OnClickListener onClickListener,
+ String fileManagerTitle, String fileManagerButton,
+ int requestCode) {
+ LayoutInflater inflater =
+ (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+
+ alert.setTitle(title);
+ alert.setMessage(message);
+
+ View view = (View) inflater.inflate(R.layout.file_dialog, null);
+
+ mActivity = activity;
+ mFilename = (EditText) view.findViewById(R.id.input);
+ mFilename.setText(defaultFile);
+ mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ openFile();
+ }
+ });
+ mFileManagerTitle = fileManagerTitle;
+ mFileManagerButton = fileManagerButton;
+ mRequestCode = requestCode;
+
+ alert.setView(view);
+
+ final OnClickListener clickListener = onClickListener;
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ clickListener.onOkClick(mFilename.getText().toString());
+ }
+ });
+
+ alert.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ clickListener.onCancelClick();
+ }
+ });
+ return alert.create();
+ }
+
+ public static void setFilename(String filename) {
+ if (mFilename != null) {
+ mFilename.setText(filename);
+ }
+ }
+
+ /**
+ * Opens the file manager to select a file to open.
+ */
+ private static void openFile() {
+ String filename = mFilename.getText().toString();
+
+ Intent intent = new Intent(FileManager.ACTION_PICK_FILE);
+
+ intent.setData(Uri.parse("file://" + filename));
+
+ intent.putExtra(FileManager.EXTRA_TITLE, mFileManagerTitle);
+ intent.putExtra(FileManager.EXTRA_BUTTON_TEXT, mFileManagerButton);
+
+ try {
+ mActivity.startActivityForResult(intent, mRequestCode);
+ } catch (ActivityNotFoundException e) {
+ // No compatible file manager was found.
+ Toast.makeText(mActivity, R.string.oiFilemanagerNotInstalled, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java
new file mode 100644
index 000000000..47cd0a890
--- /dev/null
+++ b/src/org/thialfihar/android/apg/Id.java
@@ -0,0 +1,129 @@
+/*
+ * 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 org.bouncycastle2.bcpg.CompressionAlgorithmTags;
+
+public final class Id {
+ public static final class menu {
+ public static final int export = 0x21070001;
+ public static final int delete = 0x21070002;
+ public static final int edit = 0x21070003;
+
+ public static final class option {
+ public static final int new_pass_phrase = 0x21070001;
+ public static final int create = 0x21070002;
+ public static final int about = 0x21070003;
+ public static final int manage_public_keys = 0x21070004;
+ public static final int manage_secret_keys = 0x21070005;
+ public static final int import_keys = 0x21070006;
+ public static final int export_keys = 0x21070007;
+ public static final int preferences = 0x21070008;
+ }
+ }
+
+ public static final class message {
+ public static final int progress_update = 0x21070001;
+ public static final int done = 0x21070002;
+ public static final int import_keys = 0x21070003;
+ public static final int export_keys = 0x21070004;
+ public static final int import_done = 0x21070005;
+ public static final int export_done = 0x21070006;
+ public static final int create_key = 0x21070007;
+ public static final int edit_key = 0x21070008;
+ }
+
+ public static final class request {
+ public static final int public_keys = 0x21070001;
+ public static final int secret_keys = 0x21070002;
+ public static final int filename = 0x21070003;
+ public static final int output_filename = 0x21070004;
+ }
+
+ public static final class dialog {
+ public static final int pass_phrase = 0x21070001;
+ public static final int encrypting = 0x21070002;
+ public static final int decrypting = 0x21070003;
+ public static final int new_pass_phrase = 0x21070004;
+ public static final int pass_phrases_do_not_match = 0x21070005;
+ public static final int no_pass_phrase = 0x21070006;
+ public static final int saving = 0x21070007;
+ public static final int delete_key = 0x21070008;
+ public static final int import_keys = 0x21070009;
+ public static final int importing = 0x2107000a;
+ public static final int export_key = 0x2107000b;
+ public static final int export_keys = 0x2107000c;
+ public static final int exporting = 0x2107000d;
+ public static final int new_account = 0x2107000e;
+ public static final int about = 0x2107000f;
+ public static final int change_log = 0x21070010;
+ public static final int output_filename = 0x21070011;
+ public static final int delete_file = 0x21070012;
+ }
+
+ public static final class task {
+ public static final int import_keys = 0x21070001;
+ public static final int export_keys = 0x21070002;
+ }
+
+ public static final class type {
+ public static final int public_key = 0x21070001;
+ public static final int secret_key = 0x21070002;
+ public static final int user_id = 0x21070003;
+ public static final int key = 0x21070004;
+ }
+
+ public static final class choice {
+ public static final class algorithm {
+ public static final int dsa = 0x21070001;
+ public static final int elgamal = 0x21070002;
+ public static final int rsa = 0x21070003;
+ }
+
+ public static final class compression {
+ public static final int none = 0x21070001;
+ public static final int zlib = CompressionAlgorithmTags.ZLIB;
+ public static final int bzip2 = CompressionAlgorithmTags.BZIP2;
+ public static final int zip = CompressionAlgorithmTags.ZIP;
+ }
+
+ public static final class usage {
+ public static final int sign_only = 0x21070001;
+ public static final int encrypt_only = 0x21070002;
+ public static final int sign_and_encrypt = 0x21070003;
+ }
+ }
+
+ public static final class return_value {
+ public static final int ok = 0;
+ public static final int error = -1;
+ public static final int no_master_key = -2;
+ public static final int updated = 1;
+ }
+
+ public static final class target {
+ public static final int clipboard = 0x21070001;
+ public static final int email = 0x21070002;
+ public static final int file = 0x21070003;
+ public static final int message = 0x21070004;
+ }
+
+ public static final class key {
+ public static final int none = 0;
+ public static final int symmetric = -1;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/MailListActivity.java b/src/org/thialfihar/android/apg/MailListActivity.java
index 8e63a7920..f0abe8f45 100644
--- a/src/org/thialfihar/android/apg/MailListActivity.java
+++ b/src/org/thialfihar/android/apg/MailListActivity.java
@@ -37,9 +37,9 @@ import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
public class MailListActivity extends ListActivity {
- LayoutInflater minflater = null;
+ LayoutInflater mInflater = null;
- private class Conversation {
+ private static class Conversation {
public long id;
public String subject;
public Vector<Message> messages;
@@ -50,16 +50,18 @@ public class MailListActivity extends ListActivity {
}
}
- private class Message {
+ private static class Message {
public Conversation parent;
public long id;
public String subject;
public String fromAddress;
public String data;
public String replyTo;
+ public boolean signedOnly;
public Message(Conversation parent, long id, String subject,
- String fromAddress, String replyTo, String data) {
+ String fromAddress, String replyTo,
+ String data, boolean signedOnly) {
this.parent = parent;
this.id = id;
this.subject = subject;
@@ -69,6 +71,7 @@ public class MailListActivity extends ListActivity {
if (this.replyTo == null || this.replyTo.equals("")) {
this.replyTo = this.fromAddress;
}
+ this.signedOnly = signedOnly;
}
}
@@ -79,7 +82,7 @@ public class MailListActivity extends ListActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- minflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mconversations = new Vector<Conversation>();
mmessages = new Vector<Message>();
@@ -115,18 +118,26 @@ public class MailListActivity extends ListActivity {
int bodyIndex = messageCursor.getColumnIndex("body");
String data = messageCursor.getString(bodyIndex);
data = Html.fromHtml(data).toString();
+ boolean signedOnly = false;
Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
if (matcher.matches()) {
data = matcher.group(1);
} else {
- data = null;
+ matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data);
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ signedOnly = true;
+ } else {
+ data = null;
+ }
}
Message message =
new Message(conversation,
messageCursor.getLong(idIndex),
messageCursor.getString(subjectIndex),
messageCursor.getString(fromAddressIndex),
- messageCursor.getString(replyToIndex), data);
+ messageCursor.getString(replyToIndex),
+ data, signedOnly);
messages.add(message);
mmessages.add(message);
@@ -139,7 +150,7 @@ public class MailListActivity extends ListActivity {
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long id) {
- Intent intent = new Intent(MailListActivity.this, DecryptMessageActivity.class);
+ 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);
@@ -180,20 +191,25 @@ public class MailListActivity extends ListActivity {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
- View view = minflater.inflate(R.layout.mailbox_message_item, null);
+ View view = mInflater.inflate(R.layout.mailbox_message_item, null);
Message message = (Message) getItem(position);
TextView subject = (TextView) view.findViewById(R.id.subject);
- TextView email = (TextView) view.findViewById(R.id.email_address);
- ImageView encrypted = (ImageView) view.findViewById(R.id.ic_encrypted);
+ TextView email = (TextView) view.findViewById(R.id.emailAddress);
+ ImageView status = (ImageView) view.findViewById(R.id.ic_status);
subject.setText(message.subject);
email.setText(message.fromAddress);
if (message.data != null) {
- encrypted.setVisibility(View.VISIBLE);
+ if (message.signedOnly) {
+ status.setImageResource(R.drawable.signed);
+ } else {
+ status.setImageResource(R.drawable.encrypted);
+ }
+ status.setVisibility(View.VISIBLE);
} else {
- encrypted.setVisibility(View.INVISIBLE);
+ status.setVisibility(View.INVISIBLE);
}
return view;
diff --git a/src/org/thialfihar/android/apg/MainActivity.java b/src/org/thialfihar/android/apg/MainActivity.java
index e7107f255..a4d584304 100644
--- a/src/org/thialfihar/android/apg/MainActivity.java
+++ b/src/org/thialfihar/android/apg/MainActivity.java
@@ -18,21 +18,16 @@ package org.thialfihar.android.apg;
import org.thialfihar.android.apg.provider.Accounts;
-import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.Bundle;
-import android.text.SpannableString;
-import android.text.method.LinkMovementMethod;
-import android.text.util.Linkify;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -46,25 +41,11 @@ import android.widget.Button;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
-import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
-public class MainActivity extends Activity {
- private static final int DIALOG_NEW_ACCOUNT = 1;
- private static final int DIALOG_ABOUT = 2;
- private static final int DIALOG_CHANGE_LOG = 3;
-
- private static final int OPTION_MENU_ADD_ACCOUNT = 1;
- private static final int OPTION_MENU_ABOUT = 2;
- private static final int OPTION_MENU_MANAGE_PUBLIC_KEYS = 3;
- private static final int OPTION_MENU_MANAGE_SECRET_KEYS = 4;
-
- private static final int MENU_DELETE_ACCOUNT = 1;
-
- private static String PREF_SEEN_CHANGE_LOG = "seenChangeLogDialog" + Apg.VERSION;
-
+public class MainActivity extends BaseActivity {
private ListView mAccounts = null;
@Override
@@ -74,19 +55,43 @@ public class MainActivity extends Activity {
Button encryptMessageButton = (Button) findViewById(R.id.btn_encryptMessage);
Button decryptMessageButton = (Button) findViewById(R.id.btn_decryptMessage);
- mAccounts = (ListView) findViewById(R.id.account_list);
+ Button encryptFileButton = (Button) findViewById(R.id.btn_encryptFile);
+ Button decryptFileButton = (Button) findViewById(R.id.btn_decryptFile);
+ mAccounts = (ListView) findViewById(R.id.accounts);
encryptMessageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- startEncryptMessageActivity();
+ Intent intent = new Intent(MainActivity.this, EncryptActivity.class);
+ intent.setAction(Apg.Intent.ENCRYPT);
+ startActivity(intent);
}
});
decryptMessageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- startDecryptMessageActivity();
+ Intent intent = new Intent(MainActivity.this, DecryptActivity.class);
+ intent.setAction(Apg.Intent.DECRYPT);
+ startActivity(intent);
+ }
+ });
+
+ encryptFileButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(MainActivity.this, EncryptActivity.class);
+ intent.setAction(Apg.Intent.ENCRYPT_FILE);
+ startActivity(intent);
+ }
+ });
+
+ decryptFileButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(MainActivity.this, DecryptActivity.class);
+ intent.setAction(Apg.Intent.DECRYPT_FILE);
+ startActivity(intent);
}
});
@@ -103,157 +108,106 @@ public class MainActivity extends Activity {
cursor.moveToFirst();
int nameIndex = cursor.getColumnIndex(Accounts.NAME);
String accountName = cursor.getString(nameIndex);
- startMailListActivity(accountName);
+ startActivity(new Intent(MainActivity.this, MailListActivity.class)
+ .putExtra("account", accountName));
}
}
});
registerForContextMenu(mAccounts);
- SharedPreferences prefs = getPreferences(MODE_PRIVATE);
- if (!prefs.getBoolean(PREF_SEEN_CHANGE_LOG, false)) {
- showDialog(DIALOG_CHANGE_LOG);
+ if (!hasSeenChangeLog()) {
+ showDialog(Id.dialog.change_log);
}
}
@Override
protected Dialog onCreateDialog(int id) {
switch (id) {
- case DIALOG_NEW_ACCOUNT: {
+ case Id.dialog.new_account: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
- alert.setTitle("Add Account");
- alert.setMessage("Specify the Google Mail account you want to add.");
+ alert.setTitle(R.string.title_addAccount);
+ alert.setMessage(R.string.specifyGoogleMailAccount);
final EditText input = new EditText(this);
alert.setView(input);
alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- MainActivity.this.removeDialog(DIALOG_NEW_ACCOUNT);
- String accountName = "" + input.getText();
-
- Cursor testCursor =
- managedQuery(Uri.parse("content://gmail-ls/conversations/" +
- accountName),
- null, null, null, null);
- if (testCursor == null) {
- Toast.makeText(MainActivity.this,
- "Error: account '" + accountName +
- "' not found",
- Toast.LENGTH_SHORT).show();
- return;
- }
-
- ContentValues values = new ContentValues();
- values.put(Accounts.NAME, accountName);
- try {
- MainActivity.this.getContentResolver()
- .insert(Accounts.CONTENT_URI,
- values);
- } catch (SQLException e) {
- Toast.makeText(MainActivity.this,
- "Error: failed to add account '" +
- accountName + "'",
- Toast.LENGTH_SHORT).show();
- }
- }
- });
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ MainActivity.this.removeDialog(Id.dialog.new_account);
+ String accountName = "" + input.getText();
+
+ Cursor testCursor =
+ managedQuery(Uri.parse("content://gmail-ls/conversations/" +
+ accountName),
+ null, null, null, null);
+ if (testCursor == null) {
+ Toast.makeText(MainActivity.this,
+ getString(R.string.errorMessage,
+ getString(R.string.error_accountNotFound,
+ accountName)),
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Accounts.NAME, accountName);
+ try {
+ MainActivity.this.getContentResolver()
+ .insert(Accounts.CONTENT_URI,
+ values);
+ } catch (SQLException e) {
+ Toast.makeText(MainActivity.this,
+ getString(R.string.errorMessage,
+ getString(R.string.error_addingAccountFailed,
+ accountName)),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
alert.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- MainActivity.this.removeDialog(DIALOG_NEW_ACCOUNT);
+ MainActivity.this.removeDialog(Id.dialog.new_account);
}
});
return alert.create();
}
- case DIALOG_ABOUT: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setTitle("About " + Apg.FULL_VERSION);
- ScrollView scrollView = new ScrollView(this);
- TextView message = new TextView(this);
-
- SpannableString info =
- new SpannableString("This is an attempt to bring OpenPGP to Android. " +
- "It is far from complete, but more features are " +
- "planned (see website).\n" +
- "\n" +
- "Feel free to send bug reports, suggestions, feature " +
- "requests, feedback, photographs.\n" +
- "\n" +
- "mail: thi@thialfihar.org\n" +
- "site: http://apg.thialfihar.org\n" +
- "\n" +
- "This software is provided \"as is\", without " +
- "warranty of any kind.");
- Linkify.addLinks(info, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES);
- message.setMovementMethod(LinkMovementMethod.getInstance());
- message.setText(info);
- // 5dip padding
- int padding = (int) (10 * getResources().getDisplayMetrics().densityDpi / 160);
- message.setPadding(padding, padding, padding, padding);
- message.setTextAppearance(this, android.R.style.TextAppearance_Medium);
- scrollView.addView(message);
- alert.setView(scrollView);
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- MainActivity.this.removeDialog(DIALOG_ABOUT);
- }
- });
-
- return alert.create();
- }
-
- case DIALOG_CHANGE_LOG: {
+ case Id.dialog.change_log: {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("Changes " + Apg.FULL_VERSION);
- ScrollView scrollView = new ScrollView(this);
- TextView message = new TextView(this);
-
- SpannableString info =
- new SpannableString("Read the warnings!\n\n" +
- "Changes:\n" +
- " * create/edit keys\n" +
- " * export keys\n" +
- " * GUI more Android-like\n" +
- " * better error handling\n" +
- " * bug fixes, optimizations\n" +
- " * starting with v0.8.0 APG will be open source, see website\n" +
- "\n" +
- "WARNING: be careful editing your existing keys, as they " +
- "WILL be stripped of certificates right now.\n" +
- "WARNING: key creation/editing doesn't support all " +
- "GPG features yet. In particular: " +
- "key cross-certification is NOT supported, so signing " +
- "with those keys will get a warning when the signature is " +
- "checked.\n" +
- "\n" +
- "I hope APG continues to be useful to you, please send " +
- "bug reports, feature wishes, feedback.");
- message.setText(info);
- // 5dip padding
- int padding = (int) (10 * getResources().getDisplayMetrics().densityDpi / 160);
- message.setPadding(padding, padding, padding, padding);
- message.setTextAppearance(this, android.R.style.TextAppearance_Medium);
- scrollView.addView(message);
- alert.setView(scrollView);
+ LayoutInflater inflater =
+ (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View layout = inflater.inflate(R.layout.info, null);
+ TextView message = (TextView) layout.findViewById(R.id.message);
+
+ message.setText("Read the warnings!\n\n" +
+ "Changes:\n" +
+ "\n" +
+ "WARNING: be careful editing your existing keys, as they " +
+ "WILL be stripped of certificates right now.\n" +
+ "\n" +
+ "WARNING: key creation/editing doesn't support all " +
+ "GPG features yet. In particular: " +
+ "key cross-certification is NOT supported, so signing " +
+ "with those keys will get a warning when the signature is " +
+ "checked.\n" +
+ "\n" +
+ "I hope APG continues to be useful to you, please send " +
+ "bug reports, feature wishes, feedback.");
+ alert.setView(layout);
alert.setCancelable(false);
alert.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
- MainActivity.this.removeDialog(DIALOG_CHANGE_LOG);
- SharedPreferences prefs = getPreferences(MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(PREF_SEEN_CHANGE_LOG, true);
- editor.commit();
+ MainActivity.this.removeDialog(Id.dialog.change_log);
+ setHasSeenChangeLog(true);
}
});
@@ -261,21 +215,22 @@ public class MainActivity extends Activity {
}
default: {
- break;
+ return super.onCreateDialog(id);
}
}
- return super.onCreateDialog(id);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, OPTION_MENU_MANAGE_PUBLIC_KEYS, 0, R.string.menu_managePublicKeys)
+ menu.add(0, Id.menu.option.create, 0, R.string.menu_addAccount)
+ .setIcon(android.R.drawable.ic_menu_add);
+ menu.add(1, Id.menu.option.manage_public_keys, 1, R.string.menu_managePublicKeys)
.setIcon(android.R.drawable.ic_menu_manage);
- menu.add(0, OPTION_MENU_MANAGE_SECRET_KEYS, 1, R.string.menu_manageSecretKeys)
+ menu.add(1, Id.menu.option.manage_secret_keys, 2, R.string.menu_manageSecretKeys)
.setIcon(android.R.drawable.ic_menu_manage);
- menu.add(1, OPTION_MENU_ADD_ACCOUNT, 2, R.string.menu_addAccount)
- .setIcon(android.R.drawable.ic_menu_add);
- menu.add(1, OPTION_MENU_ABOUT, 3, R.string.menu_about)
+ menu.add(2, Id.menu.option.preferences, 3, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(2, Id.menu.option.about, 4, R.string.menu_about)
.setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
@@ -283,41 +238,35 @@ public class MainActivity extends Activity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case OPTION_MENU_ADD_ACCOUNT: {
- showDialog(DIALOG_NEW_ACCOUNT);
- return true;
- }
-
- case OPTION_MENU_ABOUT: {
- showDialog(DIALOG_ABOUT);
+ case Id.menu.option.create: {
+ showDialog(Id.dialog.new_account);
return true;
}
- case OPTION_MENU_MANAGE_PUBLIC_KEYS: {
- startPublicKeyManager();
+ case Id.menu.option.manage_public_keys: {
+ startActivity(new Intent(this, PublicKeyListActivity.class));
return true;
}
- case OPTION_MENU_MANAGE_SECRET_KEYS: {
- startSecretKeyManager();
+ case Id.menu.option.manage_secret_keys: {
+ startActivity(new Intent(this, SecretKeyListActivity.class));
return true;
}
default: {
- break;
+ return super.onOptionsItemSelected(item);
}
}
- return false;
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
- TextView nameTextView = (TextView) v.findViewById(R.id.account_name);
+ TextView nameTextView = (TextView) v.findViewById(R.id.accountName);
if (nameTextView != null) {
menu.setHeaderTitle(nameTextView.getText());
- menu.add(0, MENU_DELETE_ACCOUNT, 0, "Delete Account");
+ menu.add(0, Id.menu.delete, 0, R.string.menu_deleteAccount);
}
}
@@ -327,7 +276,7 @@ public class MainActivity extends Activity {
(AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
switch (menuItem.getItemId()) {
- case MENU_DELETE_ACCOUNT: {
+ case Id.menu.delete: {
Uri uri = Uri.withAppendedPath(Accounts.CONTENT_URI, "" + info.id);
this.getContentResolver().delete(uri, null, null);
return true;
@@ -339,28 +288,8 @@ public class MainActivity extends Activity {
}
}
- public void startPublicKeyManager() {
- startActivity(new Intent(this, PublicKeyListActivity.class));
- }
-
- public void startSecretKeyManager() {
- startActivity(new Intent(this, SecretKeyListActivity.class));
- //startActivity(new Intent(this, EditKeyActivity.class));
- }
-
- public void startEncryptMessageActivity() {
- startActivity(new Intent(this, EncryptMessageActivity.class));
- }
-
- public void startDecryptMessageActivity() {
- startActivity(new Intent(this, DecryptMessageActivity.class));
- }
-
- public void startMailListActivity(String account) {
- startActivity(new Intent(this, MailListActivity.class).putExtra("account", account));
- }
- private class AccountListAdapter extends CursorAdapter {
+ private static class AccountListAdapter extends CursorAdapter {
private LayoutInflater minflater;
public AccountListAdapter(Context context, Cursor cursor) {
@@ -380,7 +309,7 @@ public class MainActivity extends Activity {
@Override
public void bindView(View view, Context context, Cursor cursor) {
- TextView nameTextView = (TextView) view.findViewById(R.id.account_name);
+ TextView nameTextView = (TextView) view.findViewById(R.id.accountName);
int nameIndex = cursor.getColumnIndex(Accounts.NAME);
final String account = cursor.getString(nameIndex);
nameTextView.setText(account);
diff --git a/src/org/thialfihar/android/apg/PositionAwareInputStream.java b/src/org/thialfihar/android/apg/PositionAwareInputStream.java
new file mode 100644
index 000000000..661e053f2
--- /dev/null
+++ b/src/org/thialfihar/android/apg/PositionAwareInputStream.java
@@ -0,0 +1,67 @@
+package org.thialfihar.android.apg;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class PositionAwareInputStream extends InputStream {
+ private InputStream mStream;
+ private long mPosition;
+
+ public PositionAwareInputStream(InputStream in) {
+ mStream = in;
+ mPosition = 0;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int ch = mStream.read();
+ ++mPosition;
+ return ch;
+ }
+
+ @Override
+ public int available() throws IOException {
+ return mStream.available();
+ }
+
+ @Override
+ public void close() throws IOException {
+ mStream.close();
+ }
+
+ @Override
+ public boolean markSupported() {
+ return false;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ int result = mStream.read(b);
+ mPosition += result;
+ return result;
+ }
+
+ @Override
+ public int read(byte[] b, int offset, int length) throws IOException {
+ int result = mStream.read(b, offset, length);
+ mPosition += result;
+ return result;
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ mStream.reset();
+ mPosition = 0;
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ long result = mStream.skip(n);
+ mPosition += result;
+ return result;
+ }
+
+ public long position() {
+ return mPosition;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/PreferencesActivity.java b/src/org/thialfihar/android/apg/PreferencesActivity.java
new file mode 100644
index 000000000..fae63d63b
--- /dev/null
+++ b/src/org/thialfihar/android/apg/PreferencesActivity.java
@@ -0,0 +1,222 @@
+/*
+ * 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 org.bouncycastle2.bcpg.HashAlgorithmTags;
+import org.bouncycastle2.openpgp.PGPEncryptedData;
+import org.thialfihar.android.apg.utils.Choice;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+public class PreferencesActivity extends BaseActivity {
+ private Spinner mPassPhraseCacheTtl = null;
+ private Spinner mEncryptionAlgorithm = null;
+ private Spinner mHashAlgorithm = null;
+ private Spinner mMessageCompression = null;
+ private Spinner mFileCompression = null;
+ private CheckBox mAsciiArmour = null;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.preferences);
+
+ mPassPhraseCacheTtl = (Spinner) findViewById(R.id.passPhraseCacheTtl);
+
+ Choice choices[] = {
+ new Choice(15, getString(R.string.choice_15secs)),
+ new Choice(60, getString(R.string.choice_1min)),
+ 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);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mPassPhraseCacheTtl.setAdapter(adapter);
+
+ int passPhraseCache = getPassPhraseCacheTtl();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == passPhraseCache) {
+ mPassPhraseCacheTtl.setSelection(i);
+ break;
+ }
+ }
+
+ mPassPhraseCacheTtl.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapter, View view, int index, long id) {
+ setPassPhraseCacheTtl(((Choice) mPassPhraseCacheTtl.getSelectedItem()).getId());
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapter) {
+ // nothing to do
+ }
+ });
+
+ mEncryptionAlgorithm = (Spinner) findViewById(R.id.encryptionAlgorithm);
+ choices = new Choice[] {
+ new Choice(PGPEncryptedData.AES_128, "AES 128"),
+ new Choice(PGPEncryptedData.AES_192, "AES 192"),
+ new Choice(PGPEncryptedData.AES_256, "AES 256"),
+ new Choice(PGPEncryptedData.BLOWFISH, "Blowfish"),
+ new Choice(PGPEncryptedData.TWOFISH, "Twofish"),
+ new Choice(PGPEncryptedData.CAST5, "CAST5"),
+ new Choice(PGPEncryptedData.DES, "DES"),
+ new Choice(PGPEncryptedData.TRIPLE_DES, "Triple DES"),
+ new Choice(PGPEncryptedData.IDEA, "IDEA"),
+ };
+ adapter = new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mEncryptionAlgorithm.setAdapter(adapter);
+
+ int defaultEncryptionAlgorithm = getDefaultEncryptionAlgorithm();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == defaultEncryptionAlgorithm) {
+ mEncryptionAlgorithm.setSelection(i);
+ break;
+ }
+ }
+
+ mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapter, View view, int index, long id) {
+ setDefaultEncryptionAlgorithm(((Choice) mEncryptionAlgorithm.getSelectedItem()).getId());
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapter) {
+ // nothing to do
+ }
+ });
+
+ mHashAlgorithm = (Spinner) findViewById(R.id.hashAlgorithm);
+ choices = new Choice[] {
+ new Choice(HashAlgorithmTags.MD5, "MD5"),
+ new Choice(HashAlgorithmTags.RIPEMD160, "RIPEMD160"),
+ new Choice(HashAlgorithmTags.SHA1, "SHA1"),
+ new Choice(HashAlgorithmTags.SHA224, "SHA224"),
+ new Choice(HashAlgorithmTags.SHA256, "SHA256"),
+ new Choice(HashAlgorithmTags.SHA384, "SHA384"),
+ new Choice(HashAlgorithmTags.SHA512, "SHA512"),
+ };
+ adapter = new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mHashAlgorithm.setAdapter(adapter);
+
+ int defaultHashAlgorithm = getDefaultHashAlgorithm();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == defaultHashAlgorithm) {
+ mHashAlgorithm.setSelection(i);
+ break;
+ }
+ }
+
+ mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapter, View view, int index, long id) {
+ setDefaultHashAlgorithm(((Choice) mHashAlgorithm.getSelectedItem()).getId());
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapter) {
+ // nothing to do
+ }
+ });
+
+ mMessageCompression = (Spinner) findViewById(R.id.messageCompression);
+ choices = new Choice[] {
+ new Choice(Id.choice.compression.none, getString(R.string.choice_none)),
+ new Choice(Id.choice.compression.zip, "ZIP"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2"),
+ new Choice(Id.choice.compression.zlib, "ZLIB"),
+ };
+ adapter = new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mMessageCompression.setAdapter(adapter);
+
+ int defaultMessageCompression = getDefaultMessageCompression();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == defaultMessageCompression) {
+ mMessageCompression.setSelection(i);
+ break;
+ }
+ }
+
+ mMessageCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapter, View view, int index, long id) {
+ setDefaultMessageCompression(((Choice) mMessageCompression.getSelectedItem()).getId());
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapter) {
+ // nothing to do
+ }
+ });
+
+ mFileCompression = (Spinner) findViewById(R.id.fileCompression);
+ choices = new Choice[] {
+ new Choice(Id.choice.compression.none, getString(R.string.choice_none)),
+ new Choice(Id.choice.compression.zip, "ZIP"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2"),
+ new Choice(Id.choice.compression.zlib, "ZLIB"),
+ };
+ adapter = new ArrayAdapter<Choice>(this, android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mFileCompression.setAdapter(adapter);
+
+ int defaultFileCompression = getDefaultFileCompression();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == defaultFileCompression) {
+ mFileCompression.setSelection(i);
+ break;
+ }
+ }
+
+ mFileCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> adapter, View view, int index, long id) {
+ setDefaultFileCompression(((Choice) mFileCompression.getSelectedItem()).getId());
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> adapter) {
+ // nothing to do
+ }
+ });
+
+ mAsciiArmour = (CheckBox) findViewById(R.id.asciiArmour);
+ mAsciiArmour.setChecked(getDefaultAsciiArmour());
+ mAsciiArmour.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setDefaultAsciiArmour(mAsciiArmour.isChecked());
+ }
+ });
+ }
+}
+
diff --git a/src/org/thialfihar/android/apg/Primes.java b/src/org/thialfihar/android/apg/Primes.java
new file mode 100644
index 000000000..e97a6c6c5
--- /dev/null
+++ b/src/org/thialfihar/android/apg/Primes.java
@@ -0,0 +1,185 @@
+/*
+ * 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.math.BigInteger;
+
+public final class Primes {
+ // taken from http://www.ietf.org/rfc/rfc3526.txt
+ public static final String P1536 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF";
+
+ public static final String P2048 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AACAA68 FFFFFFFF FFFFFFFF";
+
+ public static final String P3072 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF";
+
+ public static final String P4096 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199" +
+ "FFFFFFFF FFFFFFFF";
+
+ public static final String P6144 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
+ "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
+ "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
+ "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
+ "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
+ "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
+ "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
+ "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
+ "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
+ "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
+ "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
+ "12BF2D5B 0B7474D6 E694F91E 6DCC4024 FFFFFFFF FFFFFFFF";
+
+ public static final String P8192 =
+ "FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1" +
+ "29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD" +
+ "EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245" +
+ "E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED" +
+ "EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D" +
+ "C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F" +
+ "83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D" +
+ "670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B" +
+ "E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9" +
+ "DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510" +
+ "15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64" +
+ "ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7" +
+ "ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B" +
+ "F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C" +
+ "BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31" +
+ "43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7" +
+ "88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA" +
+ "2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6" +
+ "287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED" +
+ "1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9" +
+ "93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492" +
+ "36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD" +
+ "F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831" +
+ "179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B" +
+ "DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF" +
+ "5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6" +
+ "D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3" +
+ "23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA" +
+ "CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328" +
+ "06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C" +
+ "DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE" +
+ "12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4" +
+ "38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300" +
+ "741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568" +
+ "3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9" +
+ "22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B" +
+ "4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A" +
+ "062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36" +
+ "4597E899 A0255DC1 64F31CC5 0846851D F9AB4819 5DED7EA1" +
+ "B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92" +
+ "4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47" +
+ "9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71" +
+ "60C980DD 98EDD3DF FFFFFFFF FFFFFFFF";
+
+ public static BigInteger getBestPrime(int keySize) {
+ String primeString;
+ if (keySize >= (8192 + 6144) / 2) {
+ primeString = P8192;
+ } else if (keySize >= (6144 + 4096) / 2) {
+ primeString = P6144;
+ } else if (keySize >= (4096 + 3072) / 2) {
+ primeString = P4096;
+ } else if (keySize >= (3072 + 2048) / 2) {
+ primeString = P3072;
+ } else if (keySize >= (2048 + 1536) / 2) {
+ primeString = P2048;
+ } else {
+ primeString = P1536;
+ }
+
+ return new BigInteger(primeString.replaceAll(" ", ""), 16);
+ }
+}
diff --git a/src/org/thialfihar/android/apg/ProgressDialogUpdater.java b/src/org/thialfihar/android/apg/ProgressDialogUpdater.java
index bdc8055ed..fe38c2a34 100644
--- a/src/org/thialfihar/android/apg/ProgressDialogUpdater.java
+++ b/src/org/thialfihar/android/apg/ProgressDialogUpdater.java
@@ -18,5 +18,6 @@ package org.thialfihar.android.apg;
public interface ProgressDialogUpdater {
void setProgress(String message, int current, int total);
+ void setProgress(int resourceId, int current, int total);
void setProgress(int current, int total);
}
diff --git a/src/org/thialfihar/android/apg/PublicKeyListActivity.java b/src/org/thialfihar/android/apg/PublicKeyListActivity.java
index cebd9c7ae..67bc608ad 100644
--- a/src/org/thialfihar/android/apg/PublicKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/PublicKeyListActivity.java
@@ -27,201 +27,75 @@ import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.ExpandableListActivity;
-import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
import android.os.Message;
import android.view.ContextMenu;
-import android.view.KeyEvent;
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.view.View.OnKeyListener;
import android.widget.BaseExpandableListAdapter;
-import android.widget.EditText;
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 ExpandableListActivity
- implements Runnable, ProgressDialogUpdater {
- static final int MENU_DELETE = 1;
- static final int MENU_EXPORT = 2;
-
- static final int OPTION_MENU_IMPORT_KEYS = 1;
- static final int OPTION_MENU_EXPORT_KEYS = 2;
-
- static final int MESSAGE_PROGRESS_UPDATE = 1;
- static final int MESSAGE_IMPORT_DONE = 2;
- static final int MESSAGE_EXPORT_DONE = 3;
-
- static final int DIALOG_DELETE_KEY = 1;
- static final int DIALOG_IMPORT_KEYS = 2;
- static final int DIALOG_IMPORTING = 3;
- static final int DIALOG_EXPORT_KEYS = 4;
- static final int DIALOG_EXPORTING = 5;
- static final int DIALOG_EXPORT_KEY = 6;
-
- static final int TASK_IMPORT = 1;
- static final int TASK_EXPORT = 2;
+public class PublicKeyListActivity extends BaseActivity {
+ ExpandableListView mList;
protected int mSelectedItem = -1;
- protected String mImportFilename = null;
- protected String mExportFilename = null;
protected int mTask = 0;
- private ProgressDialog mProgressDialog = null;
- private Thread mRunningThread = null;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Bundle data = msg.getData();
- if (data != null) {
- int type = data.getInt("type");
- switch (type) {
- case MESSAGE_PROGRESS_UPDATE: {
- String message = data.getString("message");
- if (mProgressDialog != null) {
- if (message != null) {
- mProgressDialog.setMessage(message);
- }
- mProgressDialog.setMax(data.getInt("max"));
- mProgressDialog.setProgress(data.getInt("progress"));
- }
- break;
- }
-
- case MESSAGE_IMPORT_DONE: {
- removeDialog(DIALOG_IMPORTING);
- mProgressDialog = null;
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(PublicKeyListActivity.this,
- "Error: " + 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 = "Succssfully added " + added + " keys and updated " +
- updated + " keys.";
- } else if (added > 0) {
- message = "Succssfully added " + added + " keys.";
- } else if (updated > 0) {
- message = "Succssfully updated " + updated + " keys.";
- } else {
- message = "No keys added or updated.";
- }
- Toast.makeText(PublicKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- refreshList();
- break;
- }
-
- case MESSAGE_EXPORT_DONE: {
- removeDialog(DIALOG_EXPORTING);
- mProgressDialog = null;
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(PublicKeyListActivity.this,
- "Error: " + data.getString("error"),
- Toast.LENGTH_SHORT).show();
- } else {
- int exported = data.getInt("exported");
- String message;
- if (exported == 1) {
- message = "Succssfully exported 1 key.";
- } else if (exported > 0) {
- message = "Succssfully exported " + exported + " keys.";
- } else{
- message = "No keys exported.";
- }
- Toast.makeText(PublicKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- break;
- }
-
- default: {
- break;
- }
- }
- }
- }
- };
-
- public void setProgress(int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
-
- public void setProgress(String message, int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putString("message", message);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
+ private String mImportFilename = Constants.path.app_dir + "/pubring.gpg";
+ private String mExportFilename = Constants.path.app_dir + "/pubexport.asc";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.key_list);
- Apg.initialize(this);
-
- setListAdapter(new PublicKeyListAdapter(this));
- registerForContextMenu(getExpandableListView());
+ mList = (ExpandableListView) findViewById(R.id.list);
+ mList.setAdapter(new PublicKeyListAdapter(this));
+ registerForContextMenu(mList);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, OPTION_MENU_IMPORT_KEYS, 0, "Import Keys")
+ menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
.setIcon(android.R.drawable.ic_menu_add);
- menu.add(0, OPTION_MENU_EXPORT_KEYS, 1, "Export Keys")
+ menu.add(0, Id.menu.option.export_keys, 1, R.string.menu_exportKeys)
.setIcon(android.R.drawable.ic_menu_save);
+ menu.add(1, Id.menu.option.preferences, 2, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(1, Id.menu.option.about, 3, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case OPTION_MENU_IMPORT_KEYS: {
- showDialog(DIALOG_IMPORT_KEYS);
+ case Id.menu.option.import_keys: {
+ showDialog(Id.dialog.import_keys);
return true;
}
- case OPTION_MENU_EXPORT_KEYS: {
- showDialog(DIALOG_EXPORT_KEYS);
+ case Id.menu.option.export_keys: {
+ showDialog(Id.dialog.export_keys);
return true;
}
default: {
- break;
+ return super.onOptionsItemSelected(item);
}
}
- return false;
}
@Override
@@ -236,8 +110,8 @@ public class PublicKeyListActivity extends ExpandableListActivity
PGPPublicKeyRing keyRing = Apg.getPublicKeyRings().get(groupPosition);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
- menu.add(0, MENU_EXPORT, 0, "Export Key");
- menu.add(0, MENU_DELETE, 1, "Delete Key");
+ menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
+ menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
}
}
@@ -252,15 +126,15 @@ public class PublicKeyListActivity extends ExpandableListActivity
}
switch (menuItem.getItemId()) {
- case MENU_EXPORT: {
+ case Id.menu.export: {
mSelectedItem = groupPosition;
- showDialog(DIALOG_EXPORT_KEY);
+ showDialog(Id.dialog.export_key);
return true;
}
- case MENU_DELETE: {
+ case Id.menu.delete: {
mSelectedItem = groupPosition;
- showDialog(DIALOG_DELETE_KEY);
+ showDialog(Id.dialog.delete_key);
return true;
}
@@ -275,167 +149,123 @@ public class PublicKeyListActivity extends ExpandableListActivity
boolean singleKeyExport = false;
switch (id) {
- case DIALOG_DELETE_KEY: {
+ 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("Warning ");
- builder.setMessage("Do you really want to delete the key '" + userId + "'?\n" +
- "You can't undo this!");
+ builder.setTitle(R.string.warning);
+ builder.setMessage(getString(R.string.keyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
+ builder.setPositiveButton(R.string.btn_delete,
+ new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
- removeDialog(DIALOG_DELETE_KEY);
+ removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
- removeDialog(DIALOG_DELETE_KEY);
+ removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
- case DIALOG_IMPORT_KEYS: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setTitle("Import Keys");
- alert.setMessage("Please specify which file to import from.");
-
- final EditText input = new EditText(this);
- // TODO: default file?
- input.setText(Environment.getExternalStorageDirectory() + "/pubring.gpg");
- input.setOnKeyListener(new OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- // TODO: this doesn't actually work yet
- // If the event is a key-down event on the "enter"
- // button
- if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
- (keyCode == KeyEvent.KEYCODE_ENTER)) {
- try {
- ((AlertDialog) v.getParent())
- .getButton(AlertDialog.BUTTON_POSITIVE)
- .performClick();
- } catch (ClassCastException e) {
- // don't do anything if we're not in that dialog
- }
- return true;
- }
- return false;
- }
- });
- alert.setView(input);
+ case Id.dialog.import_keys: {
+ return FileDialog.build(this, getString(R.string.title_importKeys),
+ getString(R.string.specifyFileToImportFrom),
+ mImportFilename,
+ new FileDialog.OnClickListener() {
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_IMPORT_KEYS);
- mImportFilename = input.getText().toString();
+ @Override
+ public void onOkClick(String filename) {
+ removeDialog(Id.dialog.import_keys);
+ mImportFilename = filename;
importKeys();
}
- });
- alert.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_IMPORT_KEYS);
+ @Override
+ public void onCancelClick() {
+ removeDialog(Id.dialog.import_keys);
}
- });
- return alert.create();
+ },
+ getString(R.string.filemanager_titleOpen),
+ getString(R.string.filemanager_btnOpen),
+ Id.request.filename);
}
- case DIALOG_EXPORT_KEY: {
+ case Id.dialog.export_key: {
singleKeyExport = true;
- // break intentionally omitted, to use the DIALOG_EXPORT_KEYS dialog
+ // break intentionally omitted, to use the Id.dialog.export_keys dialog
}
- case DIALOG_EXPORT_KEYS: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ case Id.dialog.export_keys: {
+ String title = (singleKeyExport ?
+ getString(R.string.title_exportKey) :
+ getString(R.string.title_exportKeys));
- if (singleKeyExport) {
- alert.setTitle("Export Key");
- } else {
- alert.setTitle("Export Keys");
- mSelectedItem = -1;
- }
- final int thisDialogId = (singleKeyExport ? DIALOG_DELETE_KEY : DIALOG_EXPORT_KEYS);
- alert.setMessage("Please specify which file to export to.\n" +
- "WARNING! File will be overwritten if it exists.");
+ final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
- final EditText input = new EditText(this);
- // TODO: default file?
- input.setText(Environment.getExternalStorageDirectory() + "/pubexport.asc");
- alert.setView(input);
+ return FileDialog.build(this, title,
+ getString(R.string.specifyFileToExportTo),
+ mExportFilename,
+ new FileDialog.OnClickListener() {
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
+ @Override
+ public void onOkClick(String filename) {
removeDialog(thisDialogId);
- mExportFilename = input.getText().toString();
+ mExportFilename = filename;
exportKeys();
}
- });
- alert.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
+ @Override
+ public void onCancelClick() {
removeDialog(thisDialogId);
}
- });
- return alert.create();
+ },
+ getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave),
+ Id.request.filename);
}
- case DIALOG_IMPORTING: {
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setMessage("importing...");
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- case DIALOG_EXPORTING: {
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setMessage("exporting...");
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
+ default: {
+ return super.onCreateDialog(id);
}
}
- return super.onCreateDialog(id);
}
public void importKeys() {
- showDialog(DIALOG_IMPORTING);
- mTask = TASK_IMPORT;
- mRunningThread = new Thread(this);
- mRunningThread.start();
+ showDialog(Id.dialog.importing);
+ mTask = Id.task.import_keys;
+ startThread();
}
public void exportKeys() {
- showDialog(DIALOG_EXPORTING);
- mTask = TASK_EXPORT;
- mRunningThread = new Thread(this);
- mRunningThread.start();
+ 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 == TASK_IMPORT) {
+ if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
- if (mTask == TASK_IMPORT) {
- data = Apg.importKeyRings(this, Apg.TYPE_PUBLIC, filename, this);
+ 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) {
@@ -448,7 +278,7 @@ public class PublicKeyListActivity extends ExpandableListActivity
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
- error = "file '" + filename + "' not found";
+ error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
@@ -457,10 +287,10 @@ public class PublicKeyListActivity extends ExpandableListActivity
error = e.getMessage();
}
- if (mTask == TASK_IMPORT) {
- data.putInt("type", MESSAGE_IMPORT_DONE);
+ if (mTask == Id.task.import_keys) {
+ data.putInt("type", Id.message.import_done);
} else {
- data.putInt("type", MESSAGE_EXPORT_DONE);
+ data.putInt("type", Id.message.export_done);
}
if (error != null) {
@@ -468,7 +298,7 @@ public class PublicKeyListActivity extends ExpandableListActivity
}
msg.setData(data);
- mHandler.sendMessage(msg);
+ sendMessage(msg);
}
private void deleteKey(int index) {
@@ -478,10 +308,77 @@ public class PublicKeyListActivity extends ExpandableListActivity
}
private void refreshList() {
- ((PublicKeyListAdapter) getExpandableListAdapter()).notifyDataSetChanged();
+ ((PublicKeyListAdapter) mList.getExpandableListAdapter()).notifyDataSetChanged();
}
- private class PublicKeyListAdapter extends BaseExpandableListAdapter {
+ @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 {
@@ -581,9 +478,9 @@ public class PublicKeyListActivity extends ExpandableListActivity
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.main_user_id);
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.main_user_id_rest);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = Apg.getMainUserId(key);
@@ -597,7 +494,7 @@ public class PublicKeyListActivity extends ExpandableListActivity
}
if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknown_user_id);
+ mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
@@ -625,22 +522,22 @@ public class PublicKeyListActivity extends ExpandableListActivity
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
- TextView keyId = (TextView) view.findViewById(R.id.key_id);
+ 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.key_details);
+ TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(key);
keyDetails.setText("(" + algorithmStr + ")");
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encrypt_key);
+ 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_sign_key);
+ ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!Apg.isSigningKey(key)) {
signIcon.setVisibility(View.GONE);
}
@@ -649,7 +546,7 @@ public class PublicKeyListActivity extends ExpandableListActivity
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
- TextView userId = (TextView) view.findViewById(R.id.user_id);
+ TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
@@ -657,4 +554,32 @@ public class PublicKeyListActivity extends ExpandableListActivity
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 f42b4ccad..a69fc5b9c 100644
--- a/src/org/thialfihar/android/apg/SecretKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/SecretKeyListActivity.java
@@ -27,27 +27,20 @@ import org.thialfihar.android.apg.utils.IterableIterator;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.app.ExpandableListActivity;
-import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
import android.os.Message;
-import android.util.Log;
import android.view.ContextMenu;
-import android.view.KeyEvent;
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.view.View.OnKeyListener;
import android.widget.BaseExpandableListAdapter;
-import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
@@ -55,196 +48,67 @@ import android.widget.Toast;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.ExpandableListView.OnChildClickListener;
-public class SecretKeyListActivity extends ExpandableListActivity
- implements Runnable, ProgressDialogUpdater, OnChildClickListener,
- AskForSecretKeyPassPhrase.PassPhraseCallbackInterface {
- static final int CREATE_SECRET_KEY = 1;
- static final int EDIT_SECRET_KEY = 2;
-
- static final int MENU_EDIT = 1;
- static final int MENU_EXPORT = 2;
- static final int MENU_DELETE = 3;
-
- static final int OPTION_MENU_IMPORT_KEYS = 1;
- static final int OPTION_MENU_EXPORT_KEYS = 2;
- static final int OPTION_MENU_CREATE_KEY = 3;
-
- static final int MESSAGE_PROGRESS_UPDATE = 1;
- static final int MESSAGE_DONE = 2;
- static final int MESSAGE_IMPORT_DONE = 2;
- static final int MESSAGE_EXPORT_DONE = 3;
-
- static final int DIALOG_DELETE_KEY = 1;
- static final int DIALOG_IMPORT_KEYS = 2;
- static final int DIALOG_IMPORTING = 3;
- static final int DIALOG_EXPORT_KEYS = 4;
- static final int DIALOG_EXPORTING = 5;
- static final int DIALOG_EXPORT_KEY = 6;
-
- static final int TASK_IMPORT = 1;
- static final int TASK_EXPORT = 2;
+public class SecretKeyListActivity extends BaseActivity implements OnChildClickListener {
+ ExpandableListView mList;
protected int mSelectedItem = -1;
- protected String mImportFilename = null;
- protected String mExportFilename = null;
protected int mTask = 0;
- private ProgressDialog mProgressDialog = null;
- private Thread mRunningThread = null;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Bundle data = msg.getData();
- if (data != null) {
- int type = data.getInt("type");
- switch (type) {
- case MESSAGE_PROGRESS_UPDATE: {
- String message = data.getString("message");
- if (mProgressDialog != null) {
- if (message != null) {
- mProgressDialog.setMessage(message);
- }
- mProgressDialog.setMax(data.getInt("max"));
- mProgressDialog.setProgress(data.getInt("progress"));
- }
- break;
- }
-
- case MESSAGE_IMPORT_DONE: {
- removeDialog(DIALOG_IMPORTING);
- mProgressDialog = null;
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(SecretKeyListActivity.this,
- "Error: " + 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 = "Succssfully added " + added + " keys and updated " +
- updated + " keys.";
- } else if (added > 0) {
- message = "Succssfully added " + added + " keys.";
- } else if (updated > 0) {
- message = "Succssfully updated " + updated + " keys.";
- } else {
- message = "No keys added or updated.";
- }
- Toast.makeText(SecretKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- refreshList();
- break;
- }
-
- case MESSAGE_EXPORT_DONE: {
- removeDialog(DIALOG_EXPORTING);
- mProgressDialog = null;
-
- String error = data.getString("error");
- if (error != null) {
- Toast.makeText(SecretKeyListActivity.this,
- "Error: " + data.getString("error"),
- Toast.LENGTH_SHORT).show();
- } else {
- int exported = data.getInt("exported");
- String message;
- if (exported == 1) {
- message = "Succssfully exported 1 key.";
- } else if (exported > 0) {
- message = "Succssfully exported " + exported + " keys.";
- } else{
- message = "No keys exported.";
- }
- Toast.makeText(SecretKeyListActivity.this, message,
- Toast.LENGTH_SHORT).show();
- }
- break;
- }
-
- default: {
- break;
- }
- }
- }
- }
- };
-
- public void setProgress(int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
-
- public void setProgress(String message, int progress, int max) {
- Message msg = new Message();
- Bundle data = new Bundle();
- data.putInt("type", MESSAGE_PROGRESS_UPDATE);
- data.putString("message", message);
- data.putInt("progress", progress);
- data.putInt("max", max);
- msg.setData(data);
- mHandler.sendMessage(msg);
- }
+ private String mImportFilename = Constants.path.app_dir + "/secring.gpg";
+ private String mExportFilename = Constants.path.app_dir + "/secexport.asc";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setContentView(R.layout.key_list);
- Apg.initialize(this);
-
- setListAdapter(new SecretKeyListAdapter(this));
- registerForContextMenu(getExpandableListView());
- getExpandableListView().setOnChildClickListener(this);
+ mList = (ExpandableListView) findViewById(R.id.list);
+ mList.setAdapter(new SecretKeyListAdapter(this));
+ registerForContextMenu(mList);
+ mList.setOnChildClickListener(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, OPTION_MENU_IMPORT_KEYS, 0, "Import Keys")
+ menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
.setIcon(android.R.drawable.ic_menu_add);
- menu.add(0, OPTION_MENU_EXPORT_KEYS, 1, "Export Keys")
+ menu.add(0, Id.menu.option.export_keys, 1, R.string.menu_exportKeys)
.setIcon(android.R.drawable.ic_menu_save);
- menu.add(1, OPTION_MENU_CREATE_KEY, 2, "Create Key")
+ menu.add(1, Id.menu.option.create, 2, R.string.menu_createKey)
.setIcon(android.R.drawable.ic_menu_add);
+ menu.add(2, Id.menu.option.preferences, 3, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(2, Id.menu.option.about, 4, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case OPTION_MENU_IMPORT_KEYS: {
- showDialog(DIALOG_IMPORT_KEYS);
+ case Id.menu.option.import_keys: {
+ showDialog(Id.dialog.import_keys);
return true;
}
- case OPTION_MENU_EXPORT_KEYS: {
- showDialog(DIALOG_EXPORT_KEYS);
+ case Id.menu.option.export_keys: {
+ showDialog(Id.dialog.export_keys);
return true;
}
- case OPTION_MENU_CREATE_KEY: {
+ case Id.menu.option.create: {
createKey();
return true;
}
default: {
- break;
+ return super.onOptionsItemSelected(item);
}
}
- return false;
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
ExpandableListView.ExpandableListContextMenuInfo info =
(ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
@@ -255,9 +119,9 @@ public class SecretKeyListActivity extends ExpandableListActivity
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(groupPosition);
String userId = Apg.getMainUserIdSafe(this, Apg.getMasterKey(keyRing));
menu.setHeaderTitle(userId);
- menu.add(0, MENU_EDIT, 0, "Edit Key");
- menu.add(0, MENU_EXPORT, 1, "Export Key");
- menu.add(0, MENU_DELETE, 2, "Delete 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);
}
}
@@ -272,21 +136,21 @@ public class SecretKeyListActivity extends ExpandableListActivity
}
switch (menuItem.getItemId()) {
- case MENU_EDIT: {
+ case Id.menu.edit: {
mSelectedItem = groupPosition;
- showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
+ checkPassPhraseAndEdit();
return true;
}
- case MENU_EXPORT: {
+ case Id.menu.export: {
mSelectedItem = groupPosition;
- showDialog(DIALOG_EXPORT_KEY);
+ showDialog(Id.dialog.export_key);
return true;
}
- case MENU_DELETE: {
+ case Id.menu.delete: {
mSelectedItem = groupPosition;
- showDialog(DIALOG_DELETE_KEY);
+ showDialog(Id.dialog.delete_key);
return true;
}
@@ -300,7 +164,7 @@ public class SecretKeyListActivity extends ExpandableListActivity
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
int childPosition, long id) {
mSelectedItem = groupPosition;
- showDialog(AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE);
+ checkPassPhraseAndEdit();
return true;
}
@@ -309,174 +173,125 @@ public class SecretKeyListActivity extends ExpandableListActivity
boolean singleKeyExport = false;
switch (id) {
- case DIALOG_DELETE_KEY: {
+ 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("Warning ");
- builder.setMessage("Do you really want to delete the key '" + userId + "'?\n" +
- "You can't undo this!");
+ builder.setTitle(R.string.warning);
+ builder.setMessage(getString(R.string.secretKeyDeletionConfirmation, userId));
builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
+ builder.setPositiveButton(R.string.btn_delete,
+ new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
deleteKey(mSelectedItem);
mSelectedItem = -1;
- removeDialog(DIALOG_DELETE_KEY);
+ removeDialog(Id.dialog.delete_key);
}
});
builder.setNegativeButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
mSelectedItem = -1;
- removeDialog(DIALOG_DELETE_KEY);
+ removeDialog(Id.dialog.delete_key);
}
});
return builder.create();
}
- case DIALOG_IMPORT_KEYS: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setTitle("Import Keys");
- alert.setMessage("Please specify which file to import from.");
-
- final EditText input = new EditText(this);
- // TODO: default file?
- input.setText(Environment.getExternalStorageDirectory() + "/secring.gpg");
- input.setOnKeyListener(new OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- // TODO: this doesn't actually work yet
- // If the event is a key-down event on the "enter"
- // button
- if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
- (keyCode == KeyEvent.KEYCODE_ENTER)) {
- try {
- ((AlertDialog) v.getParent())
- .getButton(AlertDialog.BUTTON_POSITIVE)
- .performClick();
- } catch (ClassCastException e) {
- // don't do anything if we're not in that dialog
- }
- return true;
- }
- return false;
- }
- });
- alert.setView(input);
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_IMPORT_KEYS);
- mImportFilename = input.getText().toString();
+ 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();
}
- });
- alert.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(DIALOG_IMPORT_KEYS);
+ @Override
+ public void onCancelClick() {
+ removeDialog(Id.dialog.import_keys);
}
- });
- return alert.create();
+ },
+ getString(R.string.filemanager_titleOpen),
+ getString(R.string.filemanager_btnOpen),
+ Id.request.filename);
}
- case DIALOG_EXPORT_KEY: {
+ case Id.dialog.export_key: {
singleKeyExport = true;
- // break intentionally omitted, to use the DIALOG_EXPORT_KEYS dialog
+ // break intentionally omitted, to use the Id.dialog.export_keys dialog
}
- case DIALOG_EXPORT_KEYS: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ case Id.dialog.export_keys: {
+ String title = (singleKeyExport ?
+ getString(R.string.title_exportKey) :
+ getString(R.string.title_exportKeys));
- if (singleKeyExport) {
- alert.setTitle("Export Key");
- } else {
- alert.setTitle("Export Keys");
- mSelectedItem = -1;
- }
- final int thisDialogId = (singleKeyExport ? DIALOG_DELETE_KEY : DIALOG_EXPORT_KEYS);
- alert.setMessage("Please specify which file to export to.\n" +
- "WARNING! You are about to export a SECRET key.\n" +
- "WARNING! File will be overwritten if it exists.");
-
- final EditText input = new EditText(this);
- // TODO: default file?
- input.setText(Environment.getExternalStorageDirectory() + "/secexport.asc");
- input.setOnKeyListener(new OnKeyListener() {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- // TODO: this doesn't actually work yet
- // If the event is a key-down event on the "enter"
- // button
- if ((event.getAction() == KeyEvent.ACTION_DOWN) &&
- (keyCode == KeyEvent.KEYCODE_ENTER)) {
- try {
- ((AlertDialog) v.getParent())
- .getButton(AlertDialog.BUTTON_POSITIVE)
- .performClick();
- } catch (ClassCastException e) {
- // don't do anything if we're not in that dialog
- }
- return true;
- }
- return false;
- }
- });
- alert.setView(input);
+ final int thisDialogId = (singleKeyExport ? Id.dialog.export_key : Id.dialog.export_keys);
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
+ return FileDialog.build(this, title,
+ getString(R.string.specifyFileToExportSecretKeysTo),
+ mExportFilename,
+ new FileDialog.OnClickListener() {
+
+ @Override
+ public void onOkClick(String filename) {
removeDialog(thisDialogId);
- mExportFilename = input.getText().toString();
+ mExportFilename = filename;
exportKeys();
}
- });
- alert.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
+ @Override
+ public void onCancelClick() {
removeDialog(thisDialogId);
}
- });
- return alert.create();
+ },
+ getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave),
+ Id.request.filename);
}
- case DIALOG_IMPORTING: {
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setMessage("importing...");
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- case DIALOG_EXPORTING: {
- mProgressDialog = new ProgressDialog(this);
- mProgressDialog.setMessage("exporting...");
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- case AskForSecretKeyPassPhrase.DIALOG_PASS_PHRASE: {
+ case Id.dialog.pass_phrase: {
PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
long keyId = keyRing.getSecretKey().getKeyID();
return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
}
+
+ default: {
+ return super.onCreateDialog(id);
+ }
+ }
+ }
+
+ public void checkPassPhraseAndEdit() {
+ PGPSecretKeyRing keyRing = Apg.getSecretKeyRings().get(mSelectedItem);
+ long keyId = keyRing.getSecretKey().getKeyID();
+ String passPhrase = Apg.getCachedPassPhrase(keyId);
+ if (passPhrase == null) {
+ showDialog(Id.dialog.pass_phrase);
+ } else {
+ Apg.setEditPassPhrase(passPhrase);
+ editKey();
}
- return super.onCreateDialog(id);
}
- public void passPhraseCallback(String passPhrase) {
- Apg.setPassPhrase(passPhrase);
+ @Override
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ super.passPhraseCallback(keyId, passPhrase);
+ Apg.setEditPassPhrase(passPhrase);
editKey();
}
private void createKey() {
+ Apg.setEditPassPhrase("");
Intent intent = new Intent(this, EditKeyActivity.class);
- startActivityForResult(intent, CREATE_SECRET_KEY);
+ startActivityForResult(intent, Id.message.create_key);
}
private void editKey() {
@@ -484,56 +299,74 @@ public class SecretKeyListActivity extends ExpandableListActivity
long keyId = keyRing.getSecretKey().getKeyID();
Intent intent = new Intent(this, EditKeyActivity.class);
intent.putExtra("keyId", keyId);
- startActivityForResult(intent, EDIT_SECRET_KEY);
+ startActivityForResult(intent, Id.message.edit_key);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case CREATE_SECRET_KEY: // intentionally no break
- case EDIT_SECRET_KEY: {
+ case Id.message.create_key: // intentionally no break
+ case Id.message.edit_key: {
if (resultCode == RESULT_OK) {
refreshList();
}
break;
}
- default:
+ 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);
}
public void importKeys() {
- showDialog(DIALOG_IMPORTING);
- mTask = TASK_IMPORT;
- mRunningThread = new Thread(this);
- mRunningThread.start();
+ showDialog(Id.dialog.importing);
+ mTask = Id.task.import_keys;
+ startThread();
}
public void exportKeys() {
- showDialog(DIALOG_EXPORTING);
- mTask = TASK_EXPORT;
- mRunningThread = new Thread(this);
- mRunningThread.start();
+ 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 == TASK_IMPORT) {
+ if (mTask == Id.task.import_keys) {
filename = mImportFilename;
} else {
filename = mExportFilename;
}
try {
- if (mTask == TASK_IMPORT) {
- data = Apg.importKeyRings(this, Apg.TYPE_SECRET, filename, this);
+ 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) {
@@ -546,7 +379,7 @@ public class SecretKeyListActivity extends ExpandableListActivity
data = Apg.exportKeyRings(this, keys, filename, this);
}
} catch (FileNotFoundException e) {
- error = "file '" + filename + "' not found";
+ error = getString(R.string.error_fileNotFound);
} catch (IOException e) {
error = e.getMessage();
} catch (PGPException e) {
@@ -555,10 +388,10 @@ public class SecretKeyListActivity extends ExpandableListActivity
error = e.getMessage();
}
- if (mTask == TASK_IMPORT) {
- data.putInt("type", MESSAGE_IMPORT_DONE);
+ if (mTask == Id.task.import_keys) {
+ data.putInt("type", Id.message.import_done);
} else {
- data.putInt("type", MESSAGE_EXPORT_DONE);
+ data.putInt("type", Id.message.export_done);
}
if (error != null) {
@@ -566,7 +399,7 @@ public class SecretKeyListActivity extends ExpandableListActivity
}
msg.setData(data);
- mHandler.sendMessage(msg);
+ sendMessage(msg);
}
private void deleteKey(int index) {
@@ -576,11 +409,77 @@ public class SecretKeyListActivity extends ExpandableListActivity
}
private void refreshList() {
- ((SecretKeyListAdapter) getExpandableListAdapter())
- .notifyDataSetChanged();
+ ((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 class SecretKeyListAdapter extends BaseExpandableListAdapter {
+ private static class SecretKeyListAdapter extends BaseExpandableListAdapter {
private LayoutInflater mInflater;
private class KeyChild {
@@ -679,9 +578,9 @@ public class SecretKeyListActivity extends ExpandableListActivity
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.main_user_id);
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
mainUserId.setText("");
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.main_user_id_rest);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
mainUserIdRest.setText("");
String userId = Apg.getMainUserId(key);
@@ -695,7 +594,7 @@ public class SecretKeyListActivity extends ExpandableListActivity
}
if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknown_user_id);
+ mainUserId.setText(R.string.unknownUserId);
}
if (mainUserIdRest.getText().length() == 0) {
@@ -723,22 +622,22 @@ public class SecretKeyListActivity extends ExpandableListActivity
view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
}
- TextView keyId = (TextView) view.findViewById(R.id.key_id);
+ 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.key_details);
+ TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(key);
keyDetails.setText("(" + algorithmStr + ")");
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encrypt_key);
+ 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_sign_key);
+ ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
if (!Apg.isSigningKey(key)) {
signIcon.setVisibility(View.GONE);
}
@@ -747,7 +646,7 @@ public class SecretKeyListActivity extends ExpandableListActivity
case KeyChild.USER_ID: {
view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
- TextView userId = (TextView) view.findViewById(R.id.user_id);
+ TextView userId = (TextView) view.findViewById(R.id.userId);
userId.setText(child.userId);
break;
}
diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java
index 551d9508e..fbb0b6fe0 100644
--- a/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/SelectPublicKeyListActivity.java
@@ -16,40 +16,27 @@
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.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPPublicKeyRing;
-import org.thialfihar.android.apg.utils.IterableIterator;
-import android.app.Activity;
-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.view.View.OnClickListener;
-import android.widget.BaseAdapter;
import android.widget.Button;
-import android.widget.CheckBox;
import android.widget.ListView;
-import android.widget.TextView;
-public class SelectPublicKeyListActivity extends Activity {
- protected Vector<PGPPublicKeyRing> mKeyRings;
- protected LayoutInflater mInflater;
+public class SelectPublicKeyListActivity extends BaseActivity {
protected Intent mIntent;
protected ListView mList;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ setContentView(R.layout.select_public_key);
// fill things
mIntent = getIntent();
@@ -58,17 +45,18 @@ public class SelectPublicKeyListActivity extends Activity {
selectedKeyIds = mIntent.getExtras().getLongArray("selection");
}
- Apg.initialize(this);
- mKeyRings = (Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
- Collections.sort(mKeyRings, new Apg.PublicKeySorter());
+ mList = (ListView) findViewById(R.id.list);
+ // needed in Android 1.5, where the XML attribute gets ignored
+ mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- setContentView(R.layout.select_public_key);
+ Vector<PGPPublicKeyRing> keyRings =
+ (Vector<PGPPublicKeyRing>) Apg.getPublicKeyRings().clone();
+ Collections.sort(keyRings, new Apg.PublicKeySorter());
+ mList.setAdapter(new SelectPublicKeyListAdapter(mList, keyRings));
- mList = (ListView) findViewById(R.id.list);
- mList.setAdapter(new PublicKeyListAdapter(this));
if (selectedKeyIds != null) {
- for (int i = 0; i < mKeyRings.size(); ++i) {
- PGPPublicKeyRing keyRing = mKeyRings.get(i);
+ for (int i = 0; i < keyRings.size(); ++i) {
+ PGPPublicKeyRing keyRing = keyRings.get(i);
PGPPublicKey key = Apg.getMasterKey(keyRing);
if (key == null) {
continue;
@@ -122,138 +110,4 @@ public class SelectPublicKeyListActivity extends Activity {
setResult(RESULT_OK, data);
finish();
}
-
- private class PublicKeyListAdapter extends BaseAdapter {
- public PublicKeyListAdapter(Context context) {
- }
-
- @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;
- }
-
- @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) {
- PGPPublicKeyRing keyRing = mKeyRings.get(position);
- PGPPublicKey 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_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.main_user_id);
- mainUserId.setText(R.string.unknown_user_id);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.main_user_id_rest);
- mainUserIdRest.setText("");
- TextView keyId = (TextView) view.findViewById(R.id.key_id);
- keyId.setText("<no key>");
- TextView creation = (TextView) view.findViewById(R.id.creation);
- creation.setText("-");
- TextView expiry = (TextView) view.findViewById(R.id.expiry);
- expiry.setText("no expire");
- TextView status = (TextView) view.findViewById(R.id.status);
- status.setText("???");
-
- 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);
- }
-
- PGPPublicKey timespanKey = key;
- if (usableKeys.size() > 0) {
- timespanKey = usableKeys.get(0);
- status.setText("can encrypt");
- } else if (encryptKeys.size() > 0) {
- timespanKey = encryptKeys.get(0);
- Date now = new Date();
- if (now.compareTo(Apg.getCreationDate(timespanKey)) > 0) {
- status.setText("not valid");
- } else {
- status.setText("expired");
- }
- } else {
- status.setText("no key");
- }
-
- 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() + " ");
-
- CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
- selected.setChecked(mList.isItemChecked(position));
-
- view.setEnabled(enabled);
- mainUserId.setEnabled(enabled);
- mainUserIdRest.setEnabled(enabled);
- keyId.setEnabled(enabled);
- creation.setEnabled(enabled);
- expiry.setEnabled(enabled);
- selected.setEnabled(enabled);
- status.setEnabled(enabled);
-
- return view;
- }
- }
} \ No newline at end of file
diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
new file mode 100644
index 000000000..1b0b82fd8
--- /dev/null
+++ b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
@@ -0,0 +1,186 @@
+/*
+ * 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.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 android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.CheckBox;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SelectPublicKeyListAdapter extends BaseAdapter {
+ protected Vector<PGPPublicKeyRing> mKeyRings;
+ protected LayoutInflater mInflater;
+ protected ListView mParent;
+
+ public SelectPublicKeyListAdapter(ListView parent,
+ Vector<PGPPublicKeyRing> keyRings) {
+ setKeyRings(keyRings);
+ mParent = parent;
+ mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public void setKeyRings(Vector<PGPPublicKeyRing> keyRings) {
+ mKeyRings = keyRings;
+ notifyDataSetChanged();
+ }
+
+ public Vector<PGPPublicKeyRing> getKeyRings() {
+ return mKeyRings;
+ }
+
+ @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;
+ }
+
+ @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) {
+ PGPPublicKeyRing keyRing = mKeyRings.get(position);
+ PGPPublicKey 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_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);
+ }
+
+ keyId.setText("" + Long.toHexString(key.getKeyID() & 0xffffffffL));
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+
+ PGPPublicKey timespanKey = key;
+ if (usableKeys.size() > 0) {
+ timespanKey = usableKeys.get(0);
+ 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 {
+ 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() + " ");
+
+ CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
+
+ selected.setChecked(mParent.isItemChecked(position));
+
+ view.setEnabled(enabled);
+ mainUserId.setEnabled(enabled);
+ mainUserIdRest.setEnabled(enabled);
+ keyId.setEnabled(enabled);
+ creation.setEnabled(enabled);
+ expiry.setEnabled(enabled);
+ selected.setEnabled(enabled);
+ status.setEnabled(enabled);
+
+ return view;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java
index da7094c53..b6811d6e3 100644
--- a/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java
+++ b/src/org/thialfihar/android/apg/SelectSecretKeyListActivity.java
@@ -25,7 +25,6 @@ import org.bouncycastle2.openpgp.PGPSecretKey;
import org.bouncycastle2.openpgp.PGPSecretKeyRing;
import org.thialfihar.android.apg.utils.IterableIterator;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -38,7 +37,7 @@ import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
-public class SelectSecretKeyListActivity extends Activity {
+public class SelectSecretKeyListActivity extends BaseActivity {
protected Vector<PGPSecretKeyRing> mKeyRings;
protected LayoutInflater mInflater;
protected Intent mIntent;
@@ -55,8 +54,6 @@ public class SelectSecretKeyListActivity extends Activity {
// fill things
mIntent = getIntent();
- Apg.initialize(this);
-
mKeyRings = (Vector<PGPSecretKeyRing>) Apg.getSecretKeyRings().clone();
Collections.sort(mKeyRings, new Apg.SecretKeySorter());
@@ -137,18 +134,18 @@ public class SelectSecretKeyListActivity extends Activity {
}
}
- TextView mainUserId = (TextView) view.findViewById(R.id.main_user_id);
- mainUserId.setText(R.string.unknown_user_id);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.main_user_id_rest);
+ 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.key_id);
- keyId.setText("<no key>");
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ keyId.setText(R.string.noKey);
TextView creation = (TextView) view.findViewById(R.id.creation);
- creation.setText("");
+ creation.setText(R.string.noDate);
TextView expiry = (TextView) view.findViewById(R.id.expiry);
- expiry.setText("");
+ expiry.setText(R.string.noExpiry);
TextView status = (TextView) view.findViewById(R.id.status);
- status.setText("???");
+ status.setText(R.string.unknownStatus);
if (key != null) {
String userId = Apg.getMainUserId(key);
@@ -174,17 +171,17 @@ public class SelectSecretKeyListActivity extends Activity {
PGPSecretKey timespanKey = key;
if (usableKeys.size() > 0) {
timespanKey = usableKeys.get(0);
- status.setText("can sign");
+ 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("not valid");
+ status.setText(R.string.notValid);
} else {
- status.setText("expired");
+ status.setText(R.string.expired);
}
} else {
- status.setText("no key");
+ status.setText(R.string.noKey);
}
creation.setText(DateFormat.getDateInstance().format(Apg.getCreationDate(timespanKey)));
diff --git a/src/org/thialfihar/android/apg/provider/DataProvider.java b/src/org/thialfihar/android/apg/provider/DataProvider.java
index 0a6a814e4..fbc1be047 100644
--- a/src/org/thialfihar/android/apg/provider/DataProvider.java
+++ b/src/org/thialfihar/android/apg/provider/DataProvider.java
@@ -115,14 +115,7 @@ public class DataProvider extends ContentProvider {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- int currentVersion = oldVersion;
- while (currentVersion < newVersion) {
- switch (currentVersion) {
- default: {
- break;
- }
- }
- }
+ // TODO: upgrade db if necessary, and do that in a clever way
}
}
diff --git a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
index 263f34675..bc38fba4c 100644
--- a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
+++ b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
@@ -22,8 +22,10 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Vector;
+import org.bouncycastle2.openpgp.PGPPublicKey;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.thialfihar.android.apg.Apg;
+import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.utils.Choice;
@@ -65,26 +67,6 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
};
- public static class AlgorithmChoice extends Choice {
- public static final int DSA = 1;
- public static final int ELGAMAL = 2;
- public static final int RSA = 3;
-
- public AlgorithmChoice(int id, String name) {
- super(id, name);
- }
- }
-
- public static class UsageChoice extends Choice {
- public static final int SIGN_ONLY = 1;
- public static final int ENCRYPT_ONLY = 2;
- public static final int SIGN_AND_ENCRYPT = 3;
-
- public UsageChoice(int id, String name) {
- super(id, name);
- }
- }
-
public KeyEditor(Context context) {
super(context);
}
@@ -99,26 +81,25 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
setAlwaysDrawnWithCacheEnabled(true);
mAlgorithm = (TextView) findViewById(R.id.algorithm);
- mKeyId = (TextView) findViewById(R.id.key_id);
+ mKeyId = (TextView) findViewById(R.id.keyId);
mCreationDate = (TextView) findViewById(R.id.creation);
mExpiryDateButton = (Button) findViewById(R.id.expiry);
mUsage = (Spinner) findViewById(R.id.usage);
- KeyEditor.UsageChoice choices[] = {
- new KeyEditor.UsageChoice(KeyEditor.UsageChoice.SIGN_ONLY,
- getResources().getString(R.string.sign_only)),
- new KeyEditor.UsageChoice(KeyEditor.UsageChoice.ENCRYPT_ONLY,
- getResources().getString(R.string.encrypt_only)),
- new KeyEditor.UsageChoice(KeyEditor.UsageChoice.SIGN_AND_ENCRYPT,
- getResources().getString(R.string.sign_and_encrypt)),
+ Choice choices[] = {
+ new Choice(Id.choice.usage.sign_only,
+ getResources().getString(R.string.choice_signOnly)),
+ new Choice(Id.choice.usage.encrypt_only,
+ getResources().getString(R.string.choice_encryptOnly)),
+ new Choice(Id.choice.usage.sign_and_encrypt,
+ getResources().getString(R.string.choice_signAndEncrypt)),
};
- ArrayAdapter<KeyEditor.UsageChoice> adapter =
- new ArrayAdapter<KeyEditor.UsageChoice>(getContext(),
- android.R.layout.simple_spinner_item,
- choices);
+ ArrayAdapter<Choice> adapter =
+ new ArrayAdapter<Choice>(getContext(),
+ android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mUsage.setAdapter(adapter);
- mDeleteButton = (ImageButton) findViewById(R.id.edit_delete);
+ mDeleteButton = (ImageButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
setExpiryDate(null);
@@ -137,7 +118,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
date.get(Calendar.MONTH),
date.get(Calendar.DAY_OF_MONTH));
dialog.setCancelable(true);
- dialog.setButton(Dialog.BUTTON_NEGATIVE, "None",
+ dialog.setButton(Dialog.BUTTON_NEGATIVE,
+ getContext().getString(R.string.btn_noDate),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setExpiryDate(null);
@@ -169,31 +151,43 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
mKeyId.setText(keyId1Str + " " + keyId2Str);
- Vector<KeyEditor.UsageChoice> choices = new Vector<KeyEditor.UsageChoice>();
- choices.add(new KeyEditor.UsageChoice(KeyEditor.UsageChoice.SIGN_ONLY,
- getResources().getString(R.string.sign_only)));
+ Vector<Choice> choices = new Vector<Choice>();
+ boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
+ if (!isElGamalKey) {
+ choices.add(new Choice(Id.choice.usage.sign_only,
+ getResources().getString(R.string.choice_signOnly)));
+ }
if (!mIsMasterKey) {
- choices.add(new KeyEditor.UsageChoice(KeyEditor.UsageChoice.ENCRYPT_ONLY,
- getResources().getString(R.string.encrypt_only)));
+ choices.add(new Choice(Id.choice.usage.encrypt_only,
+ getResources().getString(R.string.choice_encryptOnly)));
+ }
+ if (!isElGamalKey) {
+ choices.add(new Choice(Id.choice.usage.sign_and_encrypt,
+ getResources().getString(R.string.choice_signAndEncrypt)));
}
- choices.add(new KeyEditor.UsageChoice(KeyEditor.UsageChoice.SIGN_AND_ENCRYPT,
- getResources().getString(R.string.sign_and_encrypt)));
- ArrayAdapter<KeyEditor.UsageChoice> adapter =
- new ArrayAdapter<KeyEditor.UsageChoice>(getContext(),
- android.R.layout.simple_spinner_item,
- choices);
+ ArrayAdapter<Choice> adapter =
+ new ArrayAdapter<Choice>(getContext(),
+ android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mUsage.setAdapter(adapter);
+ int selectId = 0;
if (Apg.isEncryptionKey(key)) {
if (Apg.isSigningKey(key)) {
- mUsage.setSelection(2);
+ selectId = Id.choice.usage.sign_and_encrypt;
} else {
- mUsage.setSelection(1);
+ selectId = Id.choice.usage.encrypt_only;
}
} else {
- mUsage.setSelection(0);
+ selectId = Id.choice.usage.sign_only;
+ }
+
+ for (int i = 0; i < choices.size(); ++i) {
+ if (choices.get(i).getId() == selectId) {
+ mUsage.setSelection(i);
+ break;
+ }
}
GregorianCalendar cal = new GregorianCalendar();
@@ -242,7 +236,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
return mExpiryDate;
}
- public UsageChoice getUsage() {
- return (UsageChoice) mUsage.getSelectedItem();
+ public int getUsage() {
+ return ((Choice) mUsage.getSelectedItem()).getId();
}
}
diff --git a/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/src/org/thialfihar/android/apg/ui/widget/SectionView.java
index b257da409..cc1410c26 100644
--- a/src/org/thialfihar/android/apg/ui/widget/SectionView.java
+++ b/src/org/thialfihar/android/apg/ui/widget/SectionView.java
@@ -25,8 +25,10 @@ import java.util.Vector;
import org.bouncycastle2.openpgp.PGPException;
import org.bouncycastle2.openpgp.PGPSecretKey;
import org.thialfihar.android.apg.Apg;
+import org.thialfihar.android.apg.Id;
import org.thialfihar.android.apg.R;
import org.thialfihar.android.apg.ui.widget.Editor.EditorListener;
+import org.thialfihar.android.apg.utils.Choice;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -48,16 +50,13 @@ import android.widget.TextView;
import android.widget.Toast;
public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Runnable {
- public static final int TYPE_USER_ID = 1;
- public static final int TYPE_KEY = 2;
-
private LayoutInflater mInflater;
private View mAdd;
private ViewGroup mEditors;
private TextView mTitle;
private int mType = 0;
- private KeyEditor.AlgorithmChoice mNewKeyAlgorithmChoice;
+ private Choice mNewKeyAlgorithmChoice;
private int mNewKeySize;
volatile private PGPSecretKey mNewKey;
@@ -80,7 +79,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
String error = data.getString("error");
if (error != null) {
Toast.makeText(getContext(),
- "Error: " + error,
+ getContext().getString(R.string.errorMessage, error),
Toast.LENGTH_SHORT).show();
}
@@ -114,12 +113,12 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
public void setType(int type) {
mType = type;
switch (type) {
- case TYPE_USER_ID: {
+ case Id.type.user_id: {
mTitle.setText(R.string.section_userIds);
break;
}
- case TYPE_KEY: {
+ case Id.type.key: {
mTitle.setText(R.string.section_keys);
break;
}
@@ -161,7 +160,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
/** {@inheritDoc} */
public void onClick(View v) {
switch (mType) {
- case TYPE_USER_ID: {
+ case Id.type.user_id: {
UserIdEditor view =
(UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
mEditors, false);
@@ -173,32 +172,37 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
break;
}
- case TYPE_KEY: {
+ case Id.type.key: {
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
View view = mInflater.inflate(R.layout.create_key, null);
dialog.setView(view);
- dialog.setTitle("Create Key");
+ dialog.setTitle(R.string.title_createKey);
+ dialog.setMessage(R.string.keyCreationElGamalInfo);
+
+ boolean wouldBeMasterKey = (mEditors.getChildCount() == 0);
final Spinner algorithm = (Spinner) view.findViewById(R.id.algorithm);
- KeyEditor.AlgorithmChoice choices[] = {
- new KeyEditor.AlgorithmChoice(KeyEditor.AlgorithmChoice.DSA,
- getResources().getString(R.string.dsa)),
- /*new KeyEditor.AlgorithmChoice(KeyEditor.AlgorithmChoice.ELGAMAL,
- getResources().getString(R.string.elgamal)),*/
- new KeyEditor.AlgorithmChoice(KeyEditor.AlgorithmChoice.RSA,
- getResources().getString(R.string.rsa)),
- };
- ArrayAdapter<KeyEditor.AlgorithmChoice> adapter =
- new ArrayAdapter<KeyEditor.AlgorithmChoice>(
- getContext(),
- android.R.layout.simple_spinner_item,
- choices);
+ Vector<Choice> choices = new Vector<Choice>();
+ choices.add(new Choice(Id.choice.algorithm.dsa,
+ getResources().getString(R.string.dsa)));
+ if (!wouldBeMasterKey) {
+ choices.add(new Choice(Id.choice.algorithm.elgamal,
+ getResources().getString(R.string.elgamal)));
+ }
+
+ choices.add(new Choice(Id.choice.algorithm.rsa,
+ getResources().getString(R.string.rsa)));
+
+ ArrayAdapter<Choice> adapter =
+ new ArrayAdapter<Choice>(getContext(),
+ android.R.layout.simple_spinner_item,
+ choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
algorithm.setAdapter(adapter);
// make RSA the default
- for (int i = 0; i < choices.length; ++i) {
- if (choices[i].getId() == KeyEditor.AlgorithmChoice.RSA) {
+ for (int i = 0; i < choices.size(); ++i) {
+ if (choices.get(i).getId() == Id.choice.algorithm.rsa) {
algorithm.setSelection(i);
break;
}
@@ -217,8 +221,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mNewKeySize = 0;
}
- mNewKeyAlgorithmChoice =
- (KeyEditor.AlgorithmChoice) algorithm.getSelectedItem();
+ mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem();
createKey();
}
});
@@ -244,7 +247,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
}
public void setUserIds(Vector<String> list) {
- if (mType != TYPE_USER_ID) {
+ if (mType != Id.type.user_id) {
return;
}
@@ -264,7 +267,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
}
public void setKeys(Vector<PGPSecretKey> list) {
- if (mType != TYPE_KEY) {
+ if (mType != Id.type.key) {
return;
}
@@ -283,7 +286,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
private void createKey() {
mProgressDialog = new ProgressDialog(getContext());
- mProgressDialog.setMessage("Generating key, this can take a while...");
+ mProgressDialog.setMessage(getContext().getString(R.string.progress_generating));
mProgressDialog.setCancelable(false);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mProgressDialog.show();
@@ -294,7 +297,18 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
public void run() {
String error = null;
try {
- mNewKey = Apg.createKey(mNewKeyAlgorithmChoice, mNewKeySize, Apg.getPassPhrase());
+ PGPSecretKey masterKey = null;
+ String passPhrase;
+ if (mEditors.getChildCount() > 0) {
+ masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue();
+ passPhrase = Apg.getCachedPassPhrase(masterKey.getKeyID());
+ } else {
+ passPhrase = "";
+ }
+ mNewKey = Apg.createKey(getContext(),
+ mNewKeyAlgorithmChoice.getId(),
+ mNewKeySize, passPhrase,
+ masterKey);
} catch (NoSuchProviderException e) {
error = e.getMessage();
} catch (NoSuchAlgorithmException e) {
diff --git a/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java b/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java
index a7e762b4f..5d710257a 100644
--- a/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java
+++ b/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java
@@ -81,9 +81,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
setDrawingCacheEnabled(true);
setAlwaysDrawnWithCacheEnabled(true);
- mDeleteButton = (ImageButton) findViewById(R.id.edit_delete);
+ mDeleteButton = (ImageButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
- mIsMainUserId = (RadioButton) findViewById(R.id.is_main_user_id);
+ mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
mIsMainUserId.setOnClickListener(this);
mName = (EditText) findViewById(R.id.name);
@@ -124,7 +124,8 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
if (email.length() > 0) {
Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
if (!emailMatcher.matches()) {
- throw new InvalidEmailException("invalid email '" + email + "'");
+ throw new InvalidEmailException(
+ getContext().getString(R.string.error_invalidEmail, email));
}
}