From 630780cae4683e8ba403b28da84f963221d80ee3 Mon Sep 17 00:00:00 2001
From: Dominik
Date: Sat, 10 Mar 2012 20:22:29 +0100
Subject: new directory structure
---
.gitignore | 26 +-
AndroidManifest.xml | 232 --
build.xml | 85 -
libs/android-integration-supportv4.jar | Bin 8878 -> 0 bytes
libs/android-support-v4.jar | Bin 247894 -> 0 bytes
libs/bcprov-jdk16-146.jar | Bin 3759267 -> 0 bytes
org_apg/.gitignore | 23 +
org_apg/AndroidManifest.xml | 232 ++
org_apg/build.xml | 85 +
org_apg/libs/android-integration-supportv4.jar | Bin 0 -> 8878 bytes
org_apg/libs/android-support-v4.jar | Bin 0 -> 247894 bytes
org_apg/libs/bcprov-jdk16-146.jar | Bin 0 -> 3759267 bytes
org_apg/pom.xml | 67 +
org_apg/project.properties | 11 +
org_apg/res/anim/push_left_in.xml | 20 +
org_apg/res/anim/push_left_out.xml | 20 +
org_apg/res/anim/push_right_in.xml | 20 +
org_apg/res/anim/push_right_out.xml | 20 +
org_apg/res/drawable-finger/btn_circle.xml | 32 +
.../drawable-hdpi-finger/btn_circle_disable.png | Bin 0 -> 2631 bytes
.../btn_circle_disable_focused.png | Bin 0 -> 3001 bytes
.../res/drawable-hdpi-finger/btn_circle_normal.png | Bin 0 -> 1974 bytes
.../drawable-hdpi-finger/btn_circle_pressed.png | Bin 0 -> 2624 bytes
.../drawable-hdpi-finger/btn_circle_selected.png | Bin 0 -> 2554 bytes
.../drawable-hdpi-finger/ic_btn_round_minus.png | Bin 0 -> 536 bytes
.../res/drawable-hdpi-finger/ic_btn_round_plus.png | Bin 0 -> 1316 bytes
org_apg/res/drawable-hdpi/encrypted.png | Bin 0 -> 3561 bytes
org_apg/res/drawable-hdpi/encrypted_large.png | Bin 0 -> 5244 bytes
org_apg/res/drawable-hdpi/encrypted_small.png | Bin 0 -> 2187 bytes
org_apg/res/drawable-hdpi/ic_next.png | Bin 0 -> 1722 bytes
org_apg/res/drawable-hdpi/ic_previous.png | Bin 0 -> 1712 bytes
org_apg/res/drawable-hdpi/icon.png | Bin 0 -> 3949 bytes
org_apg/res/drawable-hdpi/key.png | Bin 0 -> 3675 bytes
org_apg/res/drawable-hdpi/key_large.png | Bin 0 -> 5550 bytes
org_apg/res/drawable-hdpi/key_small.png | Bin 0 -> 2088 bytes
org_apg/res/drawable-hdpi/overlay_error.png | Bin 0 -> 1986 bytes
org_apg/res/drawable-hdpi/overlay_ok.png | Bin 0 -> 1702 bytes
org_apg/res/drawable-hdpi/signed.png | Bin 0 -> 3858 bytes
org_apg/res/drawable-hdpi/signed_large.png | Bin 0 -> 5928 bytes
org_apg/res/drawable-hdpi/signed_small.png | Bin 0 -> 2219 bytes
org_apg/res/drawable-ldpi/encrypted.png | Bin 0 -> 1513 bytes
org_apg/res/drawable-ldpi/encrypted_large.png | Bin 0 -> 2486 bytes
org_apg/res/drawable-ldpi/encrypted_small.png | Bin 0 -> 1176 bytes
org_apg/res/drawable-ldpi/ic_next.png | Bin 0 -> 916 bytes
org_apg/res/drawable-ldpi/ic_previous.png | Bin 0 -> 922 bytes
org_apg/res/drawable-ldpi/icon.png | Bin 0 -> 1795 bytes
org_apg/res/drawable-ldpi/key.png | Bin 0 -> 1484 bytes
org_apg/res/drawable-ldpi/key_large.png | Bin 0 -> 2462 bytes
org_apg/res/drawable-ldpi/key_small.png | Bin 0 -> 1074 bytes
org_apg/res/drawable-ldpi/overlay_error.png | Bin 0 -> 1192 bytes
org_apg/res/drawable-ldpi/overlay_ok.png | Bin 0 -> 1038 bytes
org_apg/res/drawable-ldpi/signed.png | Bin 0 -> 1576 bytes
org_apg/res/drawable-ldpi/signed_large.png | Bin 0 -> 2611 bytes
org_apg/res/drawable-ldpi/signed_small.png | Bin 0 -> 1149 bytes
.../drawable-mdpi-finger/btn_circle_disable.png | Bin 0 -> 938 bytes
.../btn_circle_disable_focused.png | Bin 0 -> 1436 bytes
.../res/drawable-mdpi-finger/btn_circle_normal.png | Bin 0 -> 1249 bytes
.../drawable-mdpi-finger/btn_circle_pressed.png | Bin 0 -> 1613 bytes
.../drawable-mdpi-finger/btn_circle_selected.png | Bin 0 -> 1645 bytes
.../drawable-mdpi-finger/ic_btn_round_minus.png | Bin 0 -> 288 bytes
.../res/drawable-mdpi-finger/ic_btn_round_plus.png | Bin 0 -> 526 bytes
org_apg/res/drawable-mdpi/encrypted.png | Bin 0 -> 2486 bytes
org_apg/res/drawable-mdpi/encrypted_large.png | Bin 0 -> 3561 bytes
org_apg/res/drawable-mdpi/encrypted_small.png | Bin 0 -> 1513 bytes
org_apg/res/drawable-mdpi/ic_next.png | Bin 0 -> 1360 bytes
org_apg/res/drawable-mdpi/ic_previous.png | Bin 0 -> 1352 bytes
org_apg/res/drawable-mdpi/icon.png | Bin 0 -> 2516 bytes
org_apg/res/drawable-mdpi/key.png | Bin 0 -> 2462 bytes
org_apg/res/drawable-mdpi/key_large.png | Bin 0 -> 3675 bytes
org_apg/res/drawable-mdpi/key_small.png | Bin 0 -> 1484 bytes
org_apg/res/drawable-mdpi/overlay_error.png | Bin 0 -> 1539 bytes
org_apg/res/drawable-mdpi/overlay_ok.png | Bin 0 -> 1305 bytes
org_apg/res/drawable-mdpi/signed.png | Bin 0 -> 2611 bytes
org_apg/res/drawable-mdpi/signed_large.png | Bin 0 -> 3858 bytes
org_apg/res/drawable-mdpi/signed_small.png | Bin 0 -> 1576 bytes
org_apg/res/drawable-xhdpi/icon.png | Bin 0 -> 5909 bytes
org_apg/res/drawable/btn_circle_disable.png | Bin 0 -> 938 bytes
.../res/drawable/btn_circle_disable_focused.png | Bin 0 -> 1436 bytes
org_apg/res/drawable/btn_circle_normal.png | Bin 0 -> 1249 bytes
org_apg/res/drawable/btn_circle_pressed.png | Bin 0 -> 1613 bytes
org_apg/res/drawable/btn_circle_selected.png | Bin 0 -> 1645 bytes
org_apg/res/drawable/encrypted.png | Bin 0 -> 2486 bytes
org_apg/res/drawable/encrypted_large.png | Bin 0 -> 3561 bytes
org_apg/res/drawable/encrypted_small.png | Bin 0 -> 1513 bytes
org_apg/res/drawable/ic_btn_round_minus.png | Bin 0 -> 288 bytes
org_apg/res/drawable/ic_btn_round_plus.png | Bin 0 -> 526 bytes
org_apg/res/drawable/ic_launcher_folder.png | Bin 0 -> 2235 bytes
org_apg/res/drawable/ic_launcher_folder_small.png | Bin 0 -> 1522 bytes
org_apg/res/drawable/ic_next.png | Bin 0 -> 1360 bytes
org_apg/res/drawable/ic_previous.png | Bin 0 -> 1352 bytes
org_apg/res/drawable/key.png | Bin 0 -> 2462 bytes
org_apg/res/drawable/key_large.png | Bin 0 -> 3675 bytes
org_apg/res/drawable/key_small.png | Bin 0 -> 1484 bytes
org_apg/res/drawable/overlay_error.png | Bin 0 -> 1539 bytes
org_apg/res/drawable/overlay_ok.png | Bin 0 -> 1305 bytes
org_apg/res/drawable/signed.png | Bin 0 -> 2611 bytes
org_apg/res/drawable/signed_large.png | Bin 0 -> 3858 bytes
org_apg/res/drawable/signed_small.png | Bin 0 -> 1576 bytes
org_apg/res/layout/about_activity.xml | 98 +
org_apg/res/layout/account_item.xml | 35 +
org_apg/res/layout/add_account_dialog.xml | 31 +
org_apg/res/layout/create_key.xml | 64 +
org_apg/res/layout/decrypt.xml | 216 ++
org_apg/res/layout/edit_key.xml | 87 +
org_apg/res/layout/edit_key_key_item.xml | 143 ++
org_apg/res/layout/edit_key_section.xml | 66 +
org_apg/res/layout/edit_key_user_id_item.xml | 115 +
org_apg/res/layout/encrypt.xml | 382 ++++
org_apg/res/layout/file_dialog.xml | 50 +
org_apg/res/layout/filter_info.xml | 38 +
org_apg/res/layout/general.xml | 59 +
org_apg/res/layout/info.xml | 30 +
org_apg/res/layout/key_list.xml | 31 +
.../res/layout/key_list_child_item_master_key.xml | 80 +
org_apg/res/layout/key_list_child_item_sub_key.xml | 72 +
org_apg/res/layout/key_list_child_item_user_id.xml | 33 +
org_apg/res/layout/key_list_group_item.xml | 53 +
org_apg/res/layout/key_server_editor.xml | 52 +
org_apg/res/layout/key_server_export_layout.xml | 42 +
org_apg/res/layout/key_server_preference.xml | 115 +
org_apg/res/layout/key_server_query_layout.xml | 53 +
.../res/layout/key_server_query_result_item.xml | 97 +
.../res/layout/key_server_query_result_user_id.xml | 26 +
org_apg/res/layout/mailbox_message_item.xml | 57 +
org_apg/res/layout/main.xml | 89 +
org_apg/res/layout/pass_phrase.xml | 37 +
org_apg/res/layout/select_public_key.xml | 55 +
org_apg/res/layout/select_public_key_item.xml | 82 +
org_apg/res/layout/select_secret_key.xml | 31 +
org_apg/res/layout/select_secret_key_item.xml | 75 +
org_apg/res/layout/sign_key_layout.xml | 40 +
org_apg/res/values-da/strings.xml | 261 +++
org_apg/res/values-de/strings.xml | 269 +++
org_apg/res/values-es/strings.xml | 303 +++
org_apg/res/values-it/strings.xml | 314 +++
org_apg/res/values-no/strings.xml | 313 +++
org_apg/res/values-pt/strings.xml | 312 +++
org_apg/res/values-sl/strings.xml | 304 +++
org_apg/res/values-zh/strings.xml | 309 +++
org_apg/res/values/arrays.xml | 214 ++
org_apg/res/values/static_strings.xml | 7 +
org_apg/res/values/strings.xml | 333 +++
org_apg/res/values/styles.xml | 30 +
org_apg/res/xml/apg_preferences.xml | 70 +
org_apg/res/xml/searchable_public_keys.xml | 22 +
org_apg/res/xml/searchable_secret_keys.xml | 22 +
org_apg/src/org/apg/Apg.java | 2283 ++++++++++++++++++++
org_apg/src/org/apg/ApgService.java | 643 ++++++
org_apg/src/org/apg/AskForSecretKeyPassPhrase.java | 115 +
org_apg/src/org/apg/CachedPassPhrase.java | 48 +
org_apg/src/org/apg/Constants.java | 54 +
org_apg/src/org/apg/DataDestination.java | 81 +
org_apg/src/org/apg/DataSource.java | 104 +
org_apg/src/org/apg/FileDialog.java | 133 ++
org_apg/src/org/apg/HkpKeyServer.java | 242 +++
org_apg/src/org/apg/IApgService.aidl | 125 ++
org_apg/src/org/apg/Id.java | 185 ++
org_apg/src/org/apg/InputData.java | 25 +
org_apg/src/org/apg/KeyServer.java | 46 +
org_apg/src/org/apg/PausableThread.java | 35 +
org_apg/src/org/apg/PositionAwareInputStream.java | 67 +
org_apg/src/org/apg/Preferences.java | 170 ++
org_apg/src/org/apg/Primes.java | 185 ++
org_apg/src/org/apg/ProgressDialogUpdater.java | 25 +
org_apg/src/org/apg/Service.java | 78 +
org_apg/src/org/apg/provider/Accounts.java | 27 +
.../org/apg/provider/ApgServiceBlobDatabase.java | 54 +
.../org/apg/provider/ApgServiceBlobProvider.java | 138 ++
org_apg/src/org/apg/provider/DataProvider.java | 381 ++++
org_apg/src/org/apg/provider/Database.java | 605 ++++++
org_apg/src/org/apg/provider/KeyRings.java | 33 +
org_apg/src/org/apg/provider/Keys.java | 51 +
org_apg/src/org/apg/provider/UserIds.java | 31 +
org_apg/src/org/apg/ui/AboutActivity.java | 51 +
org_apg/src/org/apg/ui/BaseActivity.java | 436 ++++
org_apg/src/org/apg/ui/DecryptActivity.java | 807 +++++++
org_apg/src/org/apg/ui/EditKeyActivity.java | 292 +++
org_apg/src/org/apg/ui/EncryptActivity.java | 998 +++++++++
org_apg/src/org/apg/ui/GeneralActivity.java | 177 ++
.../src/org/apg/ui/ImportFromQRCodeActivity.java | 138 ++
org_apg/src/org/apg/ui/KeyListActivity.java | 768 +++++++
.../org/apg/ui/KeyServerPreferenceActivity.java | 125 ++
org_apg/src/org/apg/ui/KeyServerQueryActivity.java | 297 +++
org_apg/src/org/apg/ui/MailListActivity.java | 222 ++
org_apg/src/org/apg/ui/MainActivity.java | 419 ++++
org_apg/src/org/apg/ui/PreferencesActivity.java | 274 +++
org_apg/src/org/apg/ui/PublicKeyListActivity.java | 191 ++
org_apg/src/org/apg/ui/SecretKeyListActivity.java | 203 ++
.../org/apg/ui/SelectPublicKeyListActivity.java | 172 ++
.../src/org/apg/ui/SelectPublicKeyListAdapter.java | 226 ++
.../org/apg/ui/SelectSecretKeyListActivity.java | 115 +
.../src/org/apg/ui/SelectSecretKeyListAdapter.java | 176 ++
org_apg/src/org/apg/ui/SendKeyActivity.java | 100 +
org_apg/src/org/apg/ui/SignKeyActivity.java | 294 +++
org_apg/src/org/apg/ui/widget/Editor.java | 25 +
.../org/apg/ui/widget/IntegerListPreference.java | 95 +
org_apg/src/org/apg/ui/widget/KeyEditor.java | 233 ++
org_apg/src/org/apg/ui/widget/KeyServerEditor.java | 78 +
org_apg/src/org/apg/ui/widget/SectionView.java | 335 +++
org_apg/src/org/apg/ui/widget/UserIdEditor.java | 192 ++
org_apg/src/org/apg/util/ApgCon.java | 836 +++++++
org_apg/src/org/apg/util/ApgConInterface.java | 7 +
org_apg/src/org/apg/util/Choice.java | 45 +
org_apg/src/org/apg/util/Compatibility.java | 79 +
org_apg/src/org/apg/util/IterableIterator.java | 31 +
pom.xml | 67 -
project.properties | 11 -
res/anim/push_left_in.xml | 20 -
res/anim/push_left_out.xml | 20 -
res/anim/push_right_in.xml | 20 -
res/anim/push_right_out.xml | 20 -
res/drawable-finger/btn_circle.xml | 32 -
res/drawable-hdpi-finger/btn_circle_disable.png | Bin 2631 -> 0 bytes
.../btn_circle_disable_focused.png | Bin 3001 -> 0 bytes
res/drawable-hdpi-finger/btn_circle_normal.png | Bin 1974 -> 0 bytes
res/drawable-hdpi-finger/btn_circle_pressed.png | Bin 2624 -> 0 bytes
res/drawable-hdpi-finger/btn_circle_selected.png | Bin 2554 -> 0 bytes
res/drawable-hdpi-finger/ic_btn_round_minus.png | Bin 536 -> 0 bytes
res/drawable-hdpi-finger/ic_btn_round_plus.png | Bin 1316 -> 0 bytes
res/drawable-hdpi/encrypted.png | Bin 3561 -> 0 bytes
res/drawable-hdpi/encrypted_large.png | Bin 5244 -> 0 bytes
res/drawable-hdpi/encrypted_small.png | Bin 2187 -> 0 bytes
res/drawable-hdpi/ic_next.png | Bin 1722 -> 0 bytes
res/drawable-hdpi/ic_previous.png | Bin 1712 -> 0 bytes
res/drawable-hdpi/icon.png | Bin 3949 -> 0 bytes
res/drawable-hdpi/key.png | Bin 3675 -> 0 bytes
res/drawable-hdpi/key_large.png | Bin 5550 -> 0 bytes
res/drawable-hdpi/key_small.png | Bin 2088 -> 0 bytes
res/drawable-hdpi/overlay_error.png | Bin 1986 -> 0 bytes
res/drawable-hdpi/overlay_ok.png | Bin 1702 -> 0 bytes
res/drawable-hdpi/signed.png | Bin 3858 -> 0 bytes
res/drawable-hdpi/signed_large.png | Bin 5928 -> 0 bytes
res/drawable-hdpi/signed_small.png | Bin 2219 -> 0 bytes
res/drawable-ldpi/encrypted.png | Bin 1513 -> 0 bytes
res/drawable-ldpi/encrypted_large.png | Bin 2486 -> 0 bytes
res/drawable-ldpi/encrypted_small.png | Bin 1176 -> 0 bytes
res/drawable-ldpi/ic_next.png | Bin 916 -> 0 bytes
res/drawable-ldpi/ic_previous.png | Bin 922 -> 0 bytes
res/drawable-ldpi/icon.png | Bin 1795 -> 0 bytes
res/drawable-ldpi/key.png | Bin 1484 -> 0 bytes
res/drawable-ldpi/key_large.png | Bin 2462 -> 0 bytes
res/drawable-ldpi/key_small.png | Bin 1074 -> 0 bytes
res/drawable-ldpi/overlay_error.png | Bin 1192 -> 0 bytes
res/drawable-ldpi/overlay_ok.png | Bin 1038 -> 0 bytes
res/drawable-ldpi/signed.png | Bin 1576 -> 0 bytes
res/drawable-ldpi/signed_large.png | Bin 2611 -> 0 bytes
res/drawable-ldpi/signed_small.png | Bin 1149 -> 0 bytes
res/drawable-mdpi-finger/btn_circle_disable.png | Bin 938 -> 0 bytes
.../btn_circle_disable_focused.png | Bin 1436 -> 0 bytes
res/drawable-mdpi-finger/btn_circle_normal.png | Bin 1249 -> 0 bytes
res/drawable-mdpi-finger/btn_circle_pressed.png | Bin 1613 -> 0 bytes
res/drawable-mdpi-finger/btn_circle_selected.png | Bin 1645 -> 0 bytes
res/drawable-mdpi-finger/ic_btn_round_minus.png | Bin 288 -> 0 bytes
res/drawable-mdpi-finger/ic_btn_round_plus.png | Bin 526 -> 0 bytes
res/drawable-mdpi/encrypted.png | Bin 2486 -> 0 bytes
res/drawable-mdpi/encrypted_large.png | Bin 3561 -> 0 bytes
res/drawable-mdpi/encrypted_small.png | Bin 1513 -> 0 bytes
res/drawable-mdpi/ic_next.png | Bin 1360 -> 0 bytes
res/drawable-mdpi/ic_previous.png | Bin 1352 -> 0 bytes
res/drawable-mdpi/icon.png | Bin 2516 -> 0 bytes
res/drawable-mdpi/key.png | Bin 2462 -> 0 bytes
res/drawable-mdpi/key_large.png | Bin 3675 -> 0 bytes
res/drawable-mdpi/key_small.png | Bin 1484 -> 0 bytes
res/drawable-mdpi/overlay_error.png | Bin 1539 -> 0 bytes
res/drawable-mdpi/overlay_ok.png | Bin 1305 -> 0 bytes
res/drawable-mdpi/signed.png | Bin 2611 -> 0 bytes
res/drawable-mdpi/signed_large.png | Bin 3858 -> 0 bytes
res/drawable-mdpi/signed_small.png | Bin 1576 -> 0 bytes
res/drawable-xhdpi/icon.png | Bin 5909 -> 0 bytes
res/drawable/btn_circle_disable.png | Bin 938 -> 0 bytes
res/drawable/btn_circle_disable_focused.png | Bin 1436 -> 0 bytes
res/drawable/btn_circle_normal.png | Bin 1249 -> 0 bytes
res/drawable/btn_circle_pressed.png | Bin 1613 -> 0 bytes
res/drawable/btn_circle_selected.png | Bin 1645 -> 0 bytes
res/drawable/encrypted.png | Bin 2486 -> 0 bytes
res/drawable/encrypted_large.png | Bin 3561 -> 0 bytes
res/drawable/encrypted_small.png | Bin 1513 -> 0 bytes
res/drawable/ic_btn_round_minus.png | Bin 288 -> 0 bytes
res/drawable/ic_btn_round_plus.png | Bin 526 -> 0 bytes
res/drawable/ic_launcher_folder.png | Bin 2235 -> 0 bytes
res/drawable/ic_launcher_folder_small.png | Bin 1522 -> 0 bytes
res/drawable/ic_next.png | Bin 1360 -> 0 bytes
res/drawable/ic_previous.png | Bin 1352 -> 0 bytes
res/drawable/key.png | Bin 2462 -> 0 bytes
res/drawable/key_large.png | Bin 3675 -> 0 bytes
res/drawable/key_small.png | Bin 1484 -> 0 bytes
res/drawable/overlay_error.png | Bin 1539 -> 0 bytes
res/drawable/overlay_ok.png | Bin 1305 -> 0 bytes
res/drawable/signed.png | Bin 2611 -> 0 bytes
res/drawable/signed_large.png | Bin 3858 -> 0 bytes
res/drawable/signed_small.png | Bin 1576 -> 0 bytes
res/layout/about_activity.xml | 98 -
res/layout/account_item.xml | 35 -
res/layout/add_account_dialog.xml | 31 -
res/layout/create_key.xml | 64 -
res/layout/decrypt.xml | 216 --
res/layout/edit_key.xml | 87 -
res/layout/edit_key_key_item.xml | 143 --
res/layout/edit_key_section.xml | 66 -
res/layout/edit_key_user_id_item.xml | 115 -
res/layout/encrypt.xml | 382 ----
res/layout/file_dialog.xml | 50 -
res/layout/filter_info.xml | 38 -
res/layout/general.xml | 59 -
res/layout/info.xml | 30 -
res/layout/key_list.xml | 31 -
res/layout/key_list_child_item_master_key.xml | 80 -
res/layout/key_list_child_item_sub_key.xml | 72 -
res/layout/key_list_child_item_user_id.xml | 33 -
res/layout/key_list_group_item.xml | 53 -
res/layout/key_server_editor.xml | 52 -
res/layout/key_server_export_layout.xml | 42 -
res/layout/key_server_preference.xml | 115 -
res/layout/key_server_query_layout.xml | 53 -
res/layout/key_server_query_result_item.xml | 97 -
res/layout/key_server_query_result_user_id.xml | 26 -
res/layout/mailbox_message_item.xml | 57 -
res/layout/main.xml | 89 -
res/layout/pass_phrase.xml | 37 -
res/layout/select_public_key.xml | 55 -
res/layout/select_public_key_item.xml | 82 -
res/layout/select_secret_key.xml | 31 -
res/layout/select_secret_key_item.xml | 75 -
res/layout/sign_key_layout.xml | 40 -
res/values-da/strings.xml | 261 ---
res/values-de/strings.xml | 269 ---
res/values-es/strings.xml | 303 ---
res/values-it/strings.xml | 314 ---
res/values-no/strings.xml | 313 ---
res/values-pt/strings.xml | 312 ---
res/values-sl/strings.xml | 304 ---
res/values-zh/strings.xml | 309 ---
res/values/arrays.xml | 214 --
res/values/static_strings.xml | 7 -
res/values/strings.xml | 333 ---
res/values/styles.xml | 30 -
res/xml/apg_preferences.xml | 70 -
res/xml/searchable_public_keys.xml | 22 -
res/xml/searchable_secret_keys.xml | 22 -
src/org/apg/Apg.java | 2283 --------------------
src/org/apg/ApgService.java | 643 ------
src/org/apg/AskForSecretKeyPassPhrase.java | 115 -
src/org/apg/CachedPassPhrase.java | 48 -
src/org/apg/Constants.java | 54 -
src/org/apg/DataDestination.java | 81 -
src/org/apg/DataSource.java | 104 -
src/org/apg/FileDialog.java | 133 --
src/org/apg/HkpKeyServer.java | 242 ---
src/org/apg/IApgService.aidl | 125 --
src/org/apg/Id.java | 185 --
src/org/apg/InputData.java | 25 -
src/org/apg/KeyServer.java | 46 -
src/org/apg/PausableThread.java | 35 -
src/org/apg/PositionAwareInputStream.java | 67 -
src/org/apg/Preferences.java | 170 --
src/org/apg/Primes.java | 185 --
src/org/apg/ProgressDialogUpdater.java | 25 -
src/org/apg/Service.java | 78 -
src/org/apg/provider/Accounts.java | 27 -
src/org/apg/provider/ApgServiceBlobDatabase.java | 54 -
src/org/apg/provider/ApgServiceBlobProvider.java | 138 --
src/org/apg/provider/DataProvider.java | 381 ----
src/org/apg/provider/Database.java | 605 ------
src/org/apg/provider/KeyRings.java | 33 -
src/org/apg/provider/Keys.java | 51 -
src/org/apg/provider/UserIds.java | 31 -
src/org/apg/ui/AboutActivity.java | 51 -
src/org/apg/ui/BaseActivity.java | 436 ----
src/org/apg/ui/DecryptActivity.java | 807 -------
src/org/apg/ui/EditKeyActivity.java | 292 ---
src/org/apg/ui/EncryptActivity.java | 998 ---------
src/org/apg/ui/GeneralActivity.java | 177 --
src/org/apg/ui/ImportFromQRCodeActivity.java | 138 --
src/org/apg/ui/KeyListActivity.java | 768 -------
src/org/apg/ui/KeyServerPreferenceActivity.java | 125 --
src/org/apg/ui/KeyServerQueryActivity.java | 297 ---
src/org/apg/ui/MailListActivity.java | 222 --
src/org/apg/ui/MainActivity.java | 419 ----
src/org/apg/ui/PreferencesActivity.java | 274 ---
src/org/apg/ui/PublicKeyListActivity.java | 191 --
src/org/apg/ui/SecretKeyListActivity.java | 203 --
src/org/apg/ui/SelectPublicKeyListActivity.java | 172 --
src/org/apg/ui/SelectPublicKeyListAdapter.java | 226 --
src/org/apg/ui/SelectSecretKeyListActivity.java | 115 -
src/org/apg/ui/SelectSecretKeyListAdapter.java | 176 --
src/org/apg/ui/SendKeyActivity.java | 100 -
src/org/apg/ui/SignKeyActivity.java | 294 ---
src/org/apg/ui/widget/Editor.java | 25 -
src/org/apg/ui/widget/IntegerListPreference.java | 95 -
src/org/apg/ui/widget/KeyEditor.java | 233 --
src/org/apg/ui/widget/KeyServerEditor.java | 78 -
src/org/apg/ui/widget/SectionView.java | 335 ---
src/org/apg/ui/widget/UserIdEditor.java | 192 --
src/org/apg/util/ApgCon.java | 836 -------
src/org/apg/util/ApgConInterface.java | 7 -
src/org/apg/util/Choice.java | 45 -
src/org/apg/util/Compatibility.java | 79 -
src/org/apg/util/IterableIterator.java | 31 -
398 files changed, 20551 insertions(+), 20548 deletions(-)
delete mode 100644 AndroidManifest.xml
delete mode 100644 build.xml
delete mode 100644 libs/android-integration-supportv4.jar
delete mode 100644 libs/android-support-v4.jar
delete mode 100644 libs/bcprov-jdk16-146.jar
create mode 100644 org_apg/.gitignore
create mode 100644 org_apg/AndroidManifest.xml
create mode 100644 org_apg/build.xml
create mode 100644 org_apg/libs/android-integration-supportv4.jar
create mode 100644 org_apg/libs/android-support-v4.jar
create mode 100644 org_apg/libs/bcprov-jdk16-146.jar
create mode 100644 org_apg/pom.xml
create mode 100644 org_apg/project.properties
create mode 100644 org_apg/res/anim/push_left_in.xml
create mode 100644 org_apg/res/anim/push_left_out.xml
create mode 100644 org_apg/res/anim/push_right_in.xml
create mode 100644 org_apg/res/anim/push_right_out.xml
create mode 100644 org_apg/res/drawable-finger/btn_circle.xml
create mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_disable.png
create mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png
create mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_normal.png
create mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png
create mode 100644 org_apg/res/drawable-hdpi-finger/btn_circle_selected.png
create mode 100644 org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png
create mode 100644 org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png
create mode 100644 org_apg/res/drawable-hdpi/encrypted.png
create mode 100644 org_apg/res/drawable-hdpi/encrypted_large.png
create mode 100644 org_apg/res/drawable-hdpi/encrypted_small.png
create mode 100644 org_apg/res/drawable-hdpi/ic_next.png
create mode 100644 org_apg/res/drawable-hdpi/ic_previous.png
create mode 100644 org_apg/res/drawable-hdpi/icon.png
create mode 100644 org_apg/res/drawable-hdpi/key.png
create mode 100644 org_apg/res/drawable-hdpi/key_large.png
create mode 100644 org_apg/res/drawable-hdpi/key_small.png
create mode 100644 org_apg/res/drawable-hdpi/overlay_error.png
create mode 100644 org_apg/res/drawable-hdpi/overlay_ok.png
create mode 100644 org_apg/res/drawable-hdpi/signed.png
create mode 100644 org_apg/res/drawable-hdpi/signed_large.png
create mode 100644 org_apg/res/drawable-hdpi/signed_small.png
create mode 100644 org_apg/res/drawable-ldpi/encrypted.png
create mode 100644 org_apg/res/drawable-ldpi/encrypted_large.png
create mode 100644 org_apg/res/drawable-ldpi/encrypted_small.png
create mode 100644 org_apg/res/drawable-ldpi/ic_next.png
create mode 100644 org_apg/res/drawable-ldpi/ic_previous.png
create mode 100644 org_apg/res/drawable-ldpi/icon.png
create mode 100644 org_apg/res/drawable-ldpi/key.png
create mode 100644 org_apg/res/drawable-ldpi/key_large.png
create mode 100644 org_apg/res/drawable-ldpi/key_small.png
create mode 100644 org_apg/res/drawable-ldpi/overlay_error.png
create mode 100644 org_apg/res/drawable-ldpi/overlay_ok.png
create mode 100644 org_apg/res/drawable-ldpi/signed.png
create mode 100644 org_apg/res/drawable-ldpi/signed_large.png
create mode 100644 org_apg/res/drawable-ldpi/signed_small.png
create mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_disable.png
create mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png
create mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_normal.png
create mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png
create mode 100644 org_apg/res/drawable-mdpi-finger/btn_circle_selected.png
create mode 100644 org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png
create mode 100644 org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png
create mode 100644 org_apg/res/drawable-mdpi/encrypted.png
create mode 100644 org_apg/res/drawable-mdpi/encrypted_large.png
create mode 100644 org_apg/res/drawable-mdpi/encrypted_small.png
create mode 100644 org_apg/res/drawable-mdpi/ic_next.png
create mode 100644 org_apg/res/drawable-mdpi/ic_previous.png
create mode 100644 org_apg/res/drawable-mdpi/icon.png
create mode 100644 org_apg/res/drawable-mdpi/key.png
create mode 100644 org_apg/res/drawable-mdpi/key_large.png
create mode 100644 org_apg/res/drawable-mdpi/key_small.png
create mode 100644 org_apg/res/drawable-mdpi/overlay_error.png
create mode 100644 org_apg/res/drawable-mdpi/overlay_ok.png
create mode 100644 org_apg/res/drawable-mdpi/signed.png
create mode 100644 org_apg/res/drawable-mdpi/signed_large.png
create mode 100644 org_apg/res/drawable-mdpi/signed_small.png
create mode 100644 org_apg/res/drawable-xhdpi/icon.png
create mode 100644 org_apg/res/drawable/btn_circle_disable.png
create mode 100644 org_apg/res/drawable/btn_circle_disable_focused.png
create mode 100644 org_apg/res/drawable/btn_circle_normal.png
create mode 100644 org_apg/res/drawable/btn_circle_pressed.png
create mode 100644 org_apg/res/drawable/btn_circle_selected.png
create mode 100644 org_apg/res/drawable/encrypted.png
create mode 100644 org_apg/res/drawable/encrypted_large.png
create mode 100644 org_apg/res/drawable/encrypted_small.png
create mode 100644 org_apg/res/drawable/ic_btn_round_minus.png
create mode 100644 org_apg/res/drawable/ic_btn_round_plus.png
create mode 100644 org_apg/res/drawable/ic_launcher_folder.png
create mode 100644 org_apg/res/drawable/ic_launcher_folder_small.png
create mode 100644 org_apg/res/drawable/ic_next.png
create mode 100644 org_apg/res/drawable/ic_previous.png
create mode 100644 org_apg/res/drawable/key.png
create mode 100644 org_apg/res/drawable/key_large.png
create mode 100644 org_apg/res/drawable/key_small.png
create mode 100644 org_apg/res/drawable/overlay_error.png
create mode 100644 org_apg/res/drawable/overlay_ok.png
create mode 100644 org_apg/res/drawable/signed.png
create mode 100644 org_apg/res/drawable/signed_large.png
create mode 100644 org_apg/res/drawable/signed_small.png
create mode 100644 org_apg/res/layout/about_activity.xml
create mode 100644 org_apg/res/layout/account_item.xml
create mode 100644 org_apg/res/layout/add_account_dialog.xml
create mode 100644 org_apg/res/layout/create_key.xml
create mode 100644 org_apg/res/layout/decrypt.xml
create mode 100644 org_apg/res/layout/edit_key.xml
create mode 100644 org_apg/res/layout/edit_key_key_item.xml
create mode 100644 org_apg/res/layout/edit_key_section.xml
create mode 100644 org_apg/res/layout/edit_key_user_id_item.xml
create mode 100644 org_apg/res/layout/encrypt.xml
create mode 100644 org_apg/res/layout/file_dialog.xml
create mode 100644 org_apg/res/layout/filter_info.xml
create mode 100644 org_apg/res/layout/general.xml
create mode 100644 org_apg/res/layout/info.xml
create mode 100644 org_apg/res/layout/key_list.xml
create mode 100644 org_apg/res/layout/key_list_child_item_master_key.xml
create mode 100644 org_apg/res/layout/key_list_child_item_sub_key.xml
create mode 100644 org_apg/res/layout/key_list_child_item_user_id.xml
create mode 100644 org_apg/res/layout/key_list_group_item.xml
create mode 100644 org_apg/res/layout/key_server_editor.xml
create mode 100644 org_apg/res/layout/key_server_export_layout.xml
create mode 100644 org_apg/res/layout/key_server_preference.xml
create mode 100644 org_apg/res/layout/key_server_query_layout.xml
create mode 100644 org_apg/res/layout/key_server_query_result_item.xml
create mode 100644 org_apg/res/layout/key_server_query_result_user_id.xml
create mode 100644 org_apg/res/layout/mailbox_message_item.xml
create mode 100644 org_apg/res/layout/main.xml
create mode 100644 org_apg/res/layout/pass_phrase.xml
create mode 100644 org_apg/res/layout/select_public_key.xml
create mode 100644 org_apg/res/layout/select_public_key_item.xml
create mode 100644 org_apg/res/layout/select_secret_key.xml
create mode 100644 org_apg/res/layout/select_secret_key_item.xml
create mode 100644 org_apg/res/layout/sign_key_layout.xml
create mode 100644 org_apg/res/values-da/strings.xml
create mode 100644 org_apg/res/values-de/strings.xml
create mode 100644 org_apg/res/values-es/strings.xml
create mode 100644 org_apg/res/values-it/strings.xml
create mode 100644 org_apg/res/values-no/strings.xml
create mode 100644 org_apg/res/values-pt/strings.xml
create mode 100644 org_apg/res/values-sl/strings.xml
create mode 100644 org_apg/res/values-zh/strings.xml
create mode 100644 org_apg/res/values/arrays.xml
create mode 100644 org_apg/res/values/static_strings.xml
create mode 100644 org_apg/res/values/strings.xml
create mode 100644 org_apg/res/values/styles.xml
create mode 100644 org_apg/res/xml/apg_preferences.xml
create mode 100644 org_apg/res/xml/searchable_public_keys.xml
create mode 100644 org_apg/res/xml/searchable_secret_keys.xml
create mode 100644 org_apg/src/org/apg/Apg.java
create mode 100644 org_apg/src/org/apg/ApgService.java
create mode 100644 org_apg/src/org/apg/AskForSecretKeyPassPhrase.java
create mode 100644 org_apg/src/org/apg/CachedPassPhrase.java
create mode 100644 org_apg/src/org/apg/Constants.java
create mode 100644 org_apg/src/org/apg/DataDestination.java
create mode 100644 org_apg/src/org/apg/DataSource.java
create mode 100644 org_apg/src/org/apg/FileDialog.java
create mode 100644 org_apg/src/org/apg/HkpKeyServer.java
create mode 100644 org_apg/src/org/apg/IApgService.aidl
create mode 100644 org_apg/src/org/apg/Id.java
create mode 100644 org_apg/src/org/apg/InputData.java
create mode 100644 org_apg/src/org/apg/KeyServer.java
create mode 100644 org_apg/src/org/apg/PausableThread.java
create mode 100644 org_apg/src/org/apg/PositionAwareInputStream.java
create mode 100644 org_apg/src/org/apg/Preferences.java
create mode 100644 org_apg/src/org/apg/Primes.java
create mode 100644 org_apg/src/org/apg/ProgressDialogUpdater.java
create mode 100644 org_apg/src/org/apg/Service.java
create mode 100644 org_apg/src/org/apg/provider/Accounts.java
create mode 100644 org_apg/src/org/apg/provider/ApgServiceBlobDatabase.java
create mode 100644 org_apg/src/org/apg/provider/ApgServiceBlobProvider.java
create mode 100644 org_apg/src/org/apg/provider/DataProvider.java
create mode 100644 org_apg/src/org/apg/provider/Database.java
create mode 100644 org_apg/src/org/apg/provider/KeyRings.java
create mode 100644 org_apg/src/org/apg/provider/Keys.java
create mode 100644 org_apg/src/org/apg/provider/UserIds.java
create mode 100644 org_apg/src/org/apg/ui/AboutActivity.java
create mode 100644 org_apg/src/org/apg/ui/BaseActivity.java
create mode 100644 org_apg/src/org/apg/ui/DecryptActivity.java
create mode 100644 org_apg/src/org/apg/ui/EditKeyActivity.java
create mode 100644 org_apg/src/org/apg/ui/EncryptActivity.java
create mode 100644 org_apg/src/org/apg/ui/GeneralActivity.java
create mode 100644 org_apg/src/org/apg/ui/ImportFromQRCodeActivity.java
create mode 100644 org_apg/src/org/apg/ui/KeyListActivity.java
create mode 100644 org_apg/src/org/apg/ui/KeyServerPreferenceActivity.java
create mode 100644 org_apg/src/org/apg/ui/KeyServerQueryActivity.java
create mode 100644 org_apg/src/org/apg/ui/MailListActivity.java
create mode 100644 org_apg/src/org/apg/ui/MainActivity.java
create mode 100644 org_apg/src/org/apg/ui/PreferencesActivity.java
create mode 100644 org_apg/src/org/apg/ui/PublicKeyListActivity.java
create mode 100644 org_apg/src/org/apg/ui/SecretKeyListActivity.java
create mode 100644 org_apg/src/org/apg/ui/SelectPublicKeyListActivity.java
create mode 100644 org_apg/src/org/apg/ui/SelectPublicKeyListAdapter.java
create mode 100644 org_apg/src/org/apg/ui/SelectSecretKeyListActivity.java
create mode 100644 org_apg/src/org/apg/ui/SelectSecretKeyListAdapter.java
create mode 100644 org_apg/src/org/apg/ui/SendKeyActivity.java
create mode 100644 org_apg/src/org/apg/ui/SignKeyActivity.java
create mode 100644 org_apg/src/org/apg/ui/widget/Editor.java
create mode 100644 org_apg/src/org/apg/ui/widget/IntegerListPreference.java
create mode 100644 org_apg/src/org/apg/ui/widget/KeyEditor.java
create mode 100644 org_apg/src/org/apg/ui/widget/KeyServerEditor.java
create mode 100644 org_apg/src/org/apg/ui/widget/SectionView.java
create mode 100644 org_apg/src/org/apg/ui/widget/UserIdEditor.java
create mode 100644 org_apg/src/org/apg/util/ApgCon.java
create mode 100644 org_apg/src/org/apg/util/ApgConInterface.java
create mode 100644 org_apg/src/org/apg/util/Choice.java
create mode 100644 org_apg/src/org/apg/util/Compatibility.java
create mode 100644 org_apg/src/org/apg/util/IterableIterator.java
delete mode 100644 pom.xml
delete mode 100644 project.properties
delete mode 100644 res/anim/push_left_in.xml
delete mode 100644 res/anim/push_left_out.xml
delete mode 100644 res/anim/push_right_in.xml
delete mode 100644 res/anim/push_right_out.xml
delete mode 100644 res/drawable-finger/btn_circle.xml
delete mode 100644 res/drawable-hdpi-finger/btn_circle_disable.png
delete mode 100644 res/drawable-hdpi-finger/btn_circle_disable_focused.png
delete mode 100644 res/drawable-hdpi-finger/btn_circle_normal.png
delete mode 100644 res/drawable-hdpi-finger/btn_circle_pressed.png
delete mode 100644 res/drawable-hdpi-finger/btn_circle_selected.png
delete mode 100644 res/drawable-hdpi-finger/ic_btn_round_minus.png
delete mode 100644 res/drawable-hdpi-finger/ic_btn_round_plus.png
delete mode 100644 res/drawable-hdpi/encrypted.png
delete mode 100644 res/drawable-hdpi/encrypted_large.png
delete mode 100644 res/drawable-hdpi/encrypted_small.png
delete mode 100644 res/drawable-hdpi/ic_next.png
delete mode 100644 res/drawable-hdpi/ic_previous.png
delete mode 100644 res/drawable-hdpi/icon.png
delete mode 100644 res/drawable-hdpi/key.png
delete mode 100644 res/drawable-hdpi/key_large.png
delete mode 100644 res/drawable-hdpi/key_small.png
delete mode 100644 res/drawable-hdpi/overlay_error.png
delete mode 100644 res/drawable-hdpi/overlay_ok.png
delete mode 100644 res/drawable-hdpi/signed.png
delete mode 100644 res/drawable-hdpi/signed_large.png
delete mode 100644 res/drawable-hdpi/signed_small.png
delete mode 100644 res/drawable-ldpi/encrypted.png
delete mode 100644 res/drawable-ldpi/encrypted_large.png
delete mode 100644 res/drawable-ldpi/encrypted_small.png
delete mode 100644 res/drawable-ldpi/ic_next.png
delete mode 100644 res/drawable-ldpi/ic_previous.png
delete mode 100644 res/drawable-ldpi/icon.png
delete mode 100644 res/drawable-ldpi/key.png
delete mode 100644 res/drawable-ldpi/key_large.png
delete mode 100644 res/drawable-ldpi/key_small.png
delete mode 100644 res/drawable-ldpi/overlay_error.png
delete mode 100644 res/drawable-ldpi/overlay_ok.png
delete mode 100644 res/drawable-ldpi/signed.png
delete mode 100644 res/drawable-ldpi/signed_large.png
delete mode 100644 res/drawable-ldpi/signed_small.png
delete mode 100644 res/drawable-mdpi-finger/btn_circle_disable.png
delete mode 100644 res/drawable-mdpi-finger/btn_circle_disable_focused.png
delete mode 100644 res/drawable-mdpi-finger/btn_circle_normal.png
delete mode 100644 res/drawable-mdpi-finger/btn_circle_pressed.png
delete mode 100644 res/drawable-mdpi-finger/btn_circle_selected.png
delete mode 100644 res/drawable-mdpi-finger/ic_btn_round_minus.png
delete mode 100644 res/drawable-mdpi-finger/ic_btn_round_plus.png
delete mode 100644 res/drawable-mdpi/encrypted.png
delete mode 100644 res/drawable-mdpi/encrypted_large.png
delete mode 100644 res/drawable-mdpi/encrypted_small.png
delete mode 100644 res/drawable-mdpi/ic_next.png
delete mode 100644 res/drawable-mdpi/ic_previous.png
delete mode 100644 res/drawable-mdpi/icon.png
delete mode 100644 res/drawable-mdpi/key.png
delete mode 100644 res/drawable-mdpi/key_large.png
delete mode 100644 res/drawable-mdpi/key_small.png
delete mode 100644 res/drawable-mdpi/overlay_error.png
delete mode 100644 res/drawable-mdpi/overlay_ok.png
delete mode 100644 res/drawable-mdpi/signed.png
delete mode 100644 res/drawable-mdpi/signed_large.png
delete mode 100644 res/drawable-mdpi/signed_small.png
delete mode 100644 res/drawable-xhdpi/icon.png
delete mode 100644 res/drawable/btn_circle_disable.png
delete mode 100644 res/drawable/btn_circle_disable_focused.png
delete mode 100644 res/drawable/btn_circle_normal.png
delete mode 100644 res/drawable/btn_circle_pressed.png
delete mode 100644 res/drawable/btn_circle_selected.png
delete mode 100644 res/drawable/encrypted.png
delete mode 100644 res/drawable/encrypted_large.png
delete mode 100644 res/drawable/encrypted_small.png
delete mode 100644 res/drawable/ic_btn_round_minus.png
delete mode 100644 res/drawable/ic_btn_round_plus.png
delete mode 100644 res/drawable/ic_launcher_folder.png
delete mode 100644 res/drawable/ic_launcher_folder_small.png
delete mode 100644 res/drawable/ic_next.png
delete mode 100644 res/drawable/ic_previous.png
delete mode 100644 res/drawable/key.png
delete mode 100644 res/drawable/key_large.png
delete mode 100644 res/drawable/key_small.png
delete mode 100644 res/drawable/overlay_error.png
delete mode 100644 res/drawable/overlay_ok.png
delete mode 100644 res/drawable/signed.png
delete mode 100644 res/drawable/signed_large.png
delete mode 100644 res/drawable/signed_small.png
delete mode 100644 res/layout/about_activity.xml
delete mode 100644 res/layout/account_item.xml
delete mode 100644 res/layout/add_account_dialog.xml
delete mode 100644 res/layout/create_key.xml
delete mode 100644 res/layout/decrypt.xml
delete mode 100644 res/layout/edit_key.xml
delete mode 100644 res/layout/edit_key_key_item.xml
delete mode 100644 res/layout/edit_key_section.xml
delete mode 100644 res/layout/edit_key_user_id_item.xml
delete mode 100644 res/layout/encrypt.xml
delete mode 100644 res/layout/file_dialog.xml
delete mode 100644 res/layout/filter_info.xml
delete mode 100644 res/layout/general.xml
delete mode 100644 res/layout/info.xml
delete mode 100644 res/layout/key_list.xml
delete mode 100644 res/layout/key_list_child_item_master_key.xml
delete mode 100644 res/layout/key_list_child_item_sub_key.xml
delete mode 100644 res/layout/key_list_child_item_user_id.xml
delete mode 100644 res/layout/key_list_group_item.xml
delete mode 100644 res/layout/key_server_editor.xml
delete mode 100644 res/layout/key_server_export_layout.xml
delete mode 100644 res/layout/key_server_preference.xml
delete mode 100644 res/layout/key_server_query_layout.xml
delete mode 100644 res/layout/key_server_query_result_item.xml
delete mode 100644 res/layout/key_server_query_result_user_id.xml
delete mode 100644 res/layout/mailbox_message_item.xml
delete mode 100644 res/layout/main.xml
delete mode 100644 res/layout/pass_phrase.xml
delete mode 100644 res/layout/select_public_key.xml
delete mode 100644 res/layout/select_public_key_item.xml
delete mode 100644 res/layout/select_secret_key.xml
delete mode 100644 res/layout/select_secret_key_item.xml
delete mode 100644 res/layout/sign_key_layout.xml
delete mode 100644 res/values-da/strings.xml
delete mode 100644 res/values-de/strings.xml
delete mode 100644 res/values-es/strings.xml
delete mode 100644 res/values-it/strings.xml
delete mode 100644 res/values-no/strings.xml
delete mode 100644 res/values-pt/strings.xml
delete mode 100644 res/values-sl/strings.xml
delete mode 100644 res/values-zh/strings.xml
delete mode 100644 res/values/arrays.xml
delete mode 100644 res/values/static_strings.xml
delete mode 100644 res/values/strings.xml
delete mode 100644 res/values/styles.xml
delete mode 100644 res/xml/apg_preferences.xml
delete mode 100644 res/xml/searchable_public_keys.xml
delete mode 100644 res/xml/searchable_secret_keys.xml
delete mode 100644 src/org/apg/Apg.java
delete mode 100644 src/org/apg/ApgService.java
delete mode 100644 src/org/apg/AskForSecretKeyPassPhrase.java
delete mode 100644 src/org/apg/CachedPassPhrase.java
delete mode 100644 src/org/apg/Constants.java
delete mode 100644 src/org/apg/DataDestination.java
delete mode 100644 src/org/apg/DataSource.java
delete mode 100644 src/org/apg/FileDialog.java
delete mode 100644 src/org/apg/HkpKeyServer.java
delete mode 100644 src/org/apg/IApgService.aidl
delete mode 100644 src/org/apg/Id.java
delete mode 100644 src/org/apg/InputData.java
delete mode 100644 src/org/apg/KeyServer.java
delete mode 100644 src/org/apg/PausableThread.java
delete mode 100644 src/org/apg/PositionAwareInputStream.java
delete mode 100644 src/org/apg/Preferences.java
delete mode 100644 src/org/apg/Primes.java
delete mode 100644 src/org/apg/ProgressDialogUpdater.java
delete mode 100644 src/org/apg/Service.java
delete mode 100644 src/org/apg/provider/Accounts.java
delete mode 100644 src/org/apg/provider/ApgServiceBlobDatabase.java
delete mode 100644 src/org/apg/provider/ApgServiceBlobProvider.java
delete mode 100644 src/org/apg/provider/DataProvider.java
delete mode 100644 src/org/apg/provider/Database.java
delete mode 100644 src/org/apg/provider/KeyRings.java
delete mode 100644 src/org/apg/provider/Keys.java
delete mode 100644 src/org/apg/provider/UserIds.java
delete mode 100644 src/org/apg/ui/AboutActivity.java
delete mode 100644 src/org/apg/ui/BaseActivity.java
delete mode 100644 src/org/apg/ui/DecryptActivity.java
delete mode 100644 src/org/apg/ui/EditKeyActivity.java
delete mode 100644 src/org/apg/ui/EncryptActivity.java
delete mode 100644 src/org/apg/ui/GeneralActivity.java
delete mode 100644 src/org/apg/ui/ImportFromQRCodeActivity.java
delete mode 100644 src/org/apg/ui/KeyListActivity.java
delete mode 100644 src/org/apg/ui/KeyServerPreferenceActivity.java
delete mode 100644 src/org/apg/ui/KeyServerQueryActivity.java
delete mode 100644 src/org/apg/ui/MailListActivity.java
delete mode 100644 src/org/apg/ui/MainActivity.java
delete mode 100644 src/org/apg/ui/PreferencesActivity.java
delete mode 100644 src/org/apg/ui/PublicKeyListActivity.java
delete mode 100644 src/org/apg/ui/SecretKeyListActivity.java
delete mode 100644 src/org/apg/ui/SelectPublicKeyListActivity.java
delete mode 100644 src/org/apg/ui/SelectPublicKeyListAdapter.java
delete mode 100644 src/org/apg/ui/SelectSecretKeyListActivity.java
delete mode 100644 src/org/apg/ui/SelectSecretKeyListAdapter.java
delete mode 100644 src/org/apg/ui/SendKeyActivity.java
delete mode 100644 src/org/apg/ui/SignKeyActivity.java
delete mode 100644 src/org/apg/ui/widget/Editor.java
delete mode 100644 src/org/apg/ui/widget/IntegerListPreference.java
delete mode 100644 src/org/apg/ui/widget/KeyEditor.java
delete mode 100644 src/org/apg/ui/widget/KeyServerEditor.java
delete mode 100644 src/org/apg/ui/widget/SectionView.java
delete mode 100644 src/org/apg/ui/widget/UserIdEditor.java
delete mode 100644 src/org/apg/util/ApgCon.java
delete mode 100644 src/org/apg/util/ApgConInterface.java
delete mode 100644 src/org/apg/util/Choice.java
delete mode 100644 src/org/apg/util/Compatibility.java
delete mode 100644 src/org/apg/util/IterableIterator.java
diff --git a/.gitignore b/.gitignore
index 2e423e1a3..a1fa2dab4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,3 @@
-#Android generated
-bin
-gen
-obj
-libs/armeabi
-lint.xml
-local.properties
-
-#Eclipse
-.project
-.classpath
-.settings
-
-#IntelliJ IDEA
-.idea
-*.iml
-
-#Maven
-target
-release.properties
-
-#Mac
-.DS_Store
\ No newline at end of file
+*.class
+*.apk
+.metadata
\ No newline at end of file
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
deleted file mode 100644
index 4ab346b7d..000000000
--- a/AndroidManifest.xml
+++ /dev/null
@@ -1,232 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build.xml b/build.xml
deleted file mode 100644
index f05ca7cf7..000000000
--- a/build.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/libs/android-integration-supportv4.jar b/libs/android-integration-supportv4.jar
deleted file mode 100644
index 4a7f1a39c..000000000
Binary files a/libs/android-integration-supportv4.jar and /dev/null differ
diff --git a/libs/android-support-v4.jar b/libs/android-support-v4.jar
deleted file mode 100644
index d006198e6..000000000
Binary files a/libs/android-support-v4.jar and /dev/null differ
diff --git a/libs/bcprov-jdk16-146.jar b/libs/bcprov-jdk16-146.jar
deleted file mode 100644
index 66cb1a73b..000000000
Binary files a/libs/bcprov-jdk16-146.jar and /dev/null differ
diff --git a/org_apg/.gitignore b/org_apg/.gitignore
new file mode 100644
index 000000000..2e423e1a3
--- /dev/null
+++ b/org_apg/.gitignore
@@ -0,0 +1,23 @@
+#Android generated
+bin
+gen
+obj
+libs/armeabi
+lint.xml
+local.properties
+
+#Eclipse
+.project
+.classpath
+.settings
+
+#IntelliJ IDEA
+.idea
+*.iml
+
+#Maven
+target
+release.properties
+
+#Mac
+.DS_Store
\ No newline at end of file
diff --git a/org_apg/AndroidManifest.xml b/org_apg/AndroidManifest.xml
new file mode 100644
index 000000000..4ab346b7d
--- /dev/null
+++ b/org_apg/AndroidManifest.xml
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/build.xml b/org_apg/build.xml
new file mode 100644
index 000000000..f05ca7cf7
--- /dev/null
+++ b/org_apg/build.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/libs/android-integration-supportv4.jar b/org_apg/libs/android-integration-supportv4.jar
new file mode 100644
index 000000000..4a7f1a39c
Binary files /dev/null and b/org_apg/libs/android-integration-supportv4.jar differ
diff --git a/org_apg/libs/android-support-v4.jar b/org_apg/libs/android-support-v4.jar
new file mode 100644
index 000000000..d006198e6
Binary files /dev/null and b/org_apg/libs/android-support-v4.jar differ
diff --git a/org_apg/libs/bcprov-jdk16-146.jar b/org_apg/libs/bcprov-jdk16-146.jar
new file mode 100644
index 000000000..66cb1a73b
Binary files /dev/null and b/org_apg/libs/bcprov-jdk16-146.jar differ
diff --git a/org_apg/pom.xml b/org_apg/pom.xml
new file mode 100644
index 000000000..cadb2a38d
--- /dev/null
+++ b/org_apg/pom.xml
@@ -0,0 +1,67 @@
+
+ 4.0.0
+ 1.0.9-SNAPSHOT
+ org.thialfihar.android
+ apg
+ apk
+ APG
+
+
+ http://code.google.com/p/android-privacy-guard/
+
+
+ UTF-8
+ 1.6_r2
+
+
+
+
+ com.google.android
+ android
+ provided
+ 1.6_r2
+
+
+ com.madgag
+ scprov-jdk15
+ 1.46.99.4-UNOFFICIAL-ROBERTO-RELEASE-SNAPSHOT
+ jar
+
+
+ com.google.zxing
+ android-integration
+ 1.6-SNAPSHOT
+ jar
+
+
+
+ src
+ test
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+ 1.6
+ 1.6
+
+
+
+ com.jayway.maven.plugins.android.generation2
+ android-maven-plugin
+ 3.0.0-alpha-11
+
+
+ ${user.home}/android-sdk-linux_x86/
+ 4
+
+ true
+ true
+
+ true
+
+
+
+
diff --git a/org_apg/project.properties b/org_apg/project.properties
new file mode 100644
index 000000000..8da376af8
--- /dev/null
+++ b/org_apg/project.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-15
diff --git a/org_apg/res/anim/push_left_in.xml b/org_apg/res/anim/push_left_in.xml
new file mode 100644
index 000000000..45fb4875a
--- /dev/null
+++ b/org_apg/res/anim/push_left_in.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/org_apg/res/anim/push_left_out.xml b/org_apg/res/anim/push_left_out.xml
new file mode 100644
index 000000000..845679f16
--- /dev/null
+++ b/org_apg/res/anim/push_left_out.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/res/anim/push_right_in.xml b/org_apg/res/anim/push_right_in.xml
new file mode 100644
index 000000000..09a244406
--- /dev/null
+++ b/org_apg/res/anim/push_right_in.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/org_apg/res/anim/push_right_out.xml b/org_apg/res/anim/push_right_out.xml
new file mode 100644
index 000000000..e8893a69a
--- /dev/null
+++ b/org_apg/res/anim/push_right_out.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/res/drawable-finger/btn_circle.xml b/org_apg/res/drawable-finger/btn_circle.xml
new file mode 100644
index 000000000..6c3c7fc1a
--- /dev/null
+++ b/org_apg/res/drawable-finger/btn_circle.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_disable.png b/org_apg/res/drawable-hdpi-finger/btn_circle_disable.png
new file mode 100644
index 000000000..ae063b545
Binary files /dev/null and b/org_apg/res/drawable-hdpi-finger/btn_circle_disable.png differ
diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png b/org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png
new file mode 100644
index 000000000..7a5d4fe4e
Binary files /dev/null and b/org_apg/res/drawable-hdpi-finger/btn_circle_disable_focused.png differ
diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_normal.png b/org_apg/res/drawable-hdpi-finger/btn_circle_normal.png
new file mode 100644
index 000000000..5eda66883
Binary files /dev/null and b/org_apg/res/drawable-hdpi-finger/btn_circle_normal.png differ
diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png b/org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png
new file mode 100644
index 000000000..88848bac2
Binary files /dev/null and b/org_apg/res/drawable-hdpi-finger/btn_circle_pressed.png differ
diff --git a/org_apg/res/drawable-hdpi-finger/btn_circle_selected.png b/org_apg/res/drawable-hdpi-finger/btn_circle_selected.png
new file mode 100644
index 000000000..74690705f
Binary files /dev/null and b/org_apg/res/drawable-hdpi-finger/btn_circle_selected.png differ
diff --git a/org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png b/org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png
new file mode 100644
index 000000000..27af3faf4
Binary files /dev/null and b/org_apg/res/drawable-hdpi-finger/ic_btn_round_minus.png differ
diff --git a/org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png b/org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png
new file mode 100644
index 000000000..b24168c32
Binary files /dev/null and b/org_apg/res/drawable-hdpi-finger/ic_btn_round_plus.png differ
diff --git a/org_apg/res/drawable-hdpi/encrypted.png b/org_apg/res/drawable-hdpi/encrypted.png
new file mode 100644
index 000000000..541781cd1
Binary files /dev/null and b/org_apg/res/drawable-hdpi/encrypted.png differ
diff --git a/org_apg/res/drawable-hdpi/encrypted_large.png b/org_apg/res/drawable-hdpi/encrypted_large.png
new file mode 100644
index 000000000..209278377
Binary files /dev/null and b/org_apg/res/drawable-hdpi/encrypted_large.png differ
diff --git a/org_apg/res/drawable-hdpi/encrypted_small.png b/org_apg/res/drawable-hdpi/encrypted_small.png
new file mode 100644
index 000000000..3ff8e9b97
Binary files /dev/null and b/org_apg/res/drawable-hdpi/encrypted_small.png differ
diff --git a/org_apg/res/drawable-hdpi/ic_next.png b/org_apg/res/drawable-hdpi/ic_next.png
new file mode 100644
index 000000000..d71058055
Binary files /dev/null and b/org_apg/res/drawable-hdpi/ic_next.png differ
diff --git a/org_apg/res/drawable-hdpi/ic_previous.png b/org_apg/res/drawable-hdpi/ic_previous.png
new file mode 100644
index 000000000..d610e4667
Binary files /dev/null and b/org_apg/res/drawable-hdpi/ic_previous.png differ
diff --git a/org_apg/res/drawable-hdpi/icon.png b/org_apg/res/drawable-hdpi/icon.png
new file mode 100644
index 000000000..d36080251
Binary files /dev/null and b/org_apg/res/drawable-hdpi/icon.png differ
diff --git a/org_apg/res/drawable-hdpi/key.png b/org_apg/res/drawable-hdpi/key.png
new file mode 100644
index 000000000..af4742ec0
Binary files /dev/null and b/org_apg/res/drawable-hdpi/key.png differ
diff --git a/org_apg/res/drawable-hdpi/key_large.png b/org_apg/res/drawable-hdpi/key_large.png
new file mode 100644
index 000000000..590f7d5a4
Binary files /dev/null and b/org_apg/res/drawable-hdpi/key_large.png differ
diff --git a/org_apg/res/drawable-hdpi/key_small.png b/org_apg/res/drawable-hdpi/key_small.png
new file mode 100644
index 000000000..6966048a1
Binary files /dev/null and b/org_apg/res/drawable-hdpi/key_small.png differ
diff --git a/org_apg/res/drawable-hdpi/overlay_error.png b/org_apg/res/drawable-hdpi/overlay_error.png
new file mode 100644
index 000000000..e6d7e60ba
Binary files /dev/null and b/org_apg/res/drawable-hdpi/overlay_error.png differ
diff --git a/org_apg/res/drawable-hdpi/overlay_ok.png b/org_apg/res/drawable-hdpi/overlay_ok.png
new file mode 100644
index 000000000..0672f869d
Binary files /dev/null and b/org_apg/res/drawable-hdpi/overlay_ok.png differ
diff --git a/org_apg/res/drawable-hdpi/signed.png b/org_apg/res/drawable-hdpi/signed.png
new file mode 100644
index 000000000..ab9495e7b
Binary files /dev/null and b/org_apg/res/drawable-hdpi/signed.png differ
diff --git a/org_apg/res/drawable-hdpi/signed_large.png b/org_apg/res/drawable-hdpi/signed_large.png
new file mode 100644
index 000000000..c209f4167
Binary files /dev/null and b/org_apg/res/drawable-hdpi/signed_large.png differ
diff --git a/org_apg/res/drawable-hdpi/signed_small.png b/org_apg/res/drawable-hdpi/signed_small.png
new file mode 100644
index 000000000..54c4906e8
Binary files /dev/null and b/org_apg/res/drawable-hdpi/signed_small.png differ
diff --git a/org_apg/res/drawable-ldpi/encrypted.png b/org_apg/res/drawable-ldpi/encrypted.png
new file mode 100644
index 000000000..bcd8cfc8e
Binary files /dev/null and b/org_apg/res/drawable-ldpi/encrypted.png differ
diff --git a/org_apg/res/drawable-ldpi/encrypted_large.png b/org_apg/res/drawable-ldpi/encrypted_large.png
new file mode 100644
index 000000000..34c3d3f97
Binary files /dev/null and b/org_apg/res/drawable-ldpi/encrypted_large.png differ
diff --git a/org_apg/res/drawable-ldpi/encrypted_small.png b/org_apg/res/drawable-ldpi/encrypted_small.png
new file mode 100644
index 000000000..5e7294a4b
Binary files /dev/null and b/org_apg/res/drawable-ldpi/encrypted_small.png differ
diff --git a/org_apg/res/drawable-ldpi/ic_next.png b/org_apg/res/drawable-ldpi/ic_next.png
new file mode 100644
index 000000000..474ed8faa
Binary files /dev/null and b/org_apg/res/drawable-ldpi/ic_next.png differ
diff --git a/org_apg/res/drawable-ldpi/ic_previous.png b/org_apg/res/drawable-ldpi/ic_previous.png
new file mode 100644
index 000000000..6fd885e6b
Binary files /dev/null and b/org_apg/res/drawable-ldpi/ic_previous.png differ
diff --git a/org_apg/res/drawable-ldpi/icon.png b/org_apg/res/drawable-ldpi/icon.png
new file mode 100644
index 000000000..5b9c33f0c
Binary files /dev/null and b/org_apg/res/drawable-ldpi/icon.png differ
diff --git a/org_apg/res/drawable-ldpi/key.png b/org_apg/res/drawable-ldpi/key.png
new file mode 100644
index 000000000..c806b6041
Binary files /dev/null and b/org_apg/res/drawable-ldpi/key.png differ
diff --git a/org_apg/res/drawable-ldpi/key_large.png b/org_apg/res/drawable-ldpi/key_large.png
new file mode 100644
index 000000000..aa499a5e1
Binary files /dev/null and b/org_apg/res/drawable-ldpi/key_large.png differ
diff --git a/org_apg/res/drawable-ldpi/key_small.png b/org_apg/res/drawable-ldpi/key_small.png
new file mode 100644
index 000000000..073b95029
Binary files /dev/null and b/org_apg/res/drawable-ldpi/key_small.png differ
diff --git a/org_apg/res/drawable-ldpi/overlay_error.png b/org_apg/res/drawable-ldpi/overlay_error.png
new file mode 100644
index 000000000..e5a88e18f
Binary files /dev/null and b/org_apg/res/drawable-ldpi/overlay_error.png differ
diff --git a/org_apg/res/drawable-ldpi/overlay_ok.png b/org_apg/res/drawable-ldpi/overlay_ok.png
new file mode 100644
index 000000000..63374d47f
Binary files /dev/null and b/org_apg/res/drawable-ldpi/overlay_ok.png differ
diff --git a/org_apg/res/drawable-ldpi/signed.png b/org_apg/res/drawable-ldpi/signed.png
new file mode 100644
index 000000000..4202c3f97
Binary files /dev/null and b/org_apg/res/drawable-ldpi/signed.png differ
diff --git a/org_apg/res/drawable-ldpi/signed_large.png b/org_apg/res/drawable-ldpi/signed_large.png
new file mode 100644
index 000000000..d2917644c
Binary files /dev/null and b/org_apg/res/drawable-ldpi/signed_large.png differ
diff --git a/org_apg/res/drawable-ldpi/signed_small.png b/org_apg/res/drawable-ldpi/signed_small.png
new file mode 100644
index 000000000..19d45f8da
Binary files /dev/null and b/org_apg/res/drawable-ldpi/signed_small.png differ
diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_disable.png b/org_apg/res/drawable-mdpi-finger/btn_circle_disable.png
new file mode 100644
index 000000000..33b74a66c
Binary files /dev/null and b/org_apg/res/drawable-mdpi-finger/btn_circle_disable.png differ
diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png b/org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png
new file mode 100644
index 000000000..005ad8dca
Binary files /dev/null and b/org_apg/res/drawable-mdpi-finger/btn_circle_disable_focused.png differ
diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_normal.png b/org_apg/res/drawable-mdpi-finger/btn_circle_normal.png
new file mode 100644
index 000000000..fc5af1c9f
Binary files /dev/null and b/org_apg/res/drawable-mdpi-finger/btn_circle_normal.png differ
diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png b/org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png
new file mode 100644
index 000000000..8f40afdfc
Binary files /dev/null and b/org_apg/res/drawable-mdpi-finger/btn_circle_pressed.png differ
diff --git a/org_apg/res/drawable-mdpi-finger/btn_circle_selected.png b/org_apg/res/drawable-mdpi-finger/btn_circle_selected.png
new file mode 100644
index 000000000..c74fac227
Binary files /dev/null and b/org_apg/res/drawable-mdpi-finger/btn_circle_selected.png differ
diff --git a/org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png b/org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png
new file mode 100644
index 000000000..96dbb17d2
Binary files /dev/null and b/org_apg/res/drawable-mdpi-finger/ic_btn_round_minus.png differ
diff --git a/org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png b/org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png
new file mode 100644
index 000000000..1ec8a956a
Binary files /dev/null and b/org_apg/res/drawable-mdpi-finger/ic_btn_round_plus.png differ
diff --git a/org_apg/res/drawable-mdpi/encrypted.png b/org_apg/res/drawable-mdpi/encrypted.png
new file mode 100644
index 000000000..34c3d3f97
Binary files /dev/null and b/org_apg/res/drawable-mdpi/encrypted.png differ
diff --git a/org_apg/res/drawable-mdpi/encrypted_large.png b/org_apg/res/drawable-mdpi/encrypted_large.png
new file mode 100644
index 000000000..541781cd1
Binary files /dev/null and b/org_apg/res/drawable-mdpi/encrypted_large.png differ
diff --git a/org_apg/res/drawable-mdpi/encrypted_small.png b/org_apg/res/drawable-mdpi/encrypted_small.png
new file mode 100644
index 000000000..bcd8cfc8e
Binary files /dev/null and b/org_apg/res/drawable-mdpi/encrypted_small.png differ
diff --git a/org_apg/res/drawable-mdpi/ic_next.png b/org_apg/res/drawable-mdpi/ic_next.png
new file mode 100644
index 000000000..8271c1380
Binary files /dev/null and b/org_apg/res/drawable-mdpi/ic_next.png differ
diff --git a/org_apg/res/drawable-mdpi/ic_previous.png b/org_apg/res/drawable-mdpi/ic_previous.png
new file mode 100644
index 000000000..ef90db972
Binary files /dev/null and b/org_apg/res/drawable-mdpi/ic_previous.png differ
diff --git a/org_apg/res/drawable-mdpi/icon.png b/org_apg/res/drawable-mdpi/icon.png
new file mode 100644
index 000000000..fec2d62ce
Binary files /dev/null and b/org_apg/res/drawable-mdpi/icon.png differ
diff --git a/org_apg/res/drawable-mdpi/key.png b/org_apg/res/drawable-mdpi/key.png
new file mode 100644
index 000000000..aa499a5e1
Binary files /dev/null and b/org_apg/res/drawable-mdpi/key.png differ
diff --git a/org_apg/res/drawable-mdpi/key_large.png b/org_apg/res/drawable-mdpi/key_large.png
new file mode 100644
index 000000000..af4742ec0
Binary files /dev/null and b/org_apg/res/drawable-mdpi/key_large.png differ
diff --git a/org_apg/res/drawable-mdpi/key_small.png b/org_apg/res/drawable-mdpi/key_small.png
new file mode 100644
index 000000000..c806b6041
Binary files /dev/null and b/org_apg/res/drawable-mdpi/key_small.png differ
diff --git a/org_apg/res/drawable-mdpi/overlay_error.png b/org_apg/res/drawable-mdpi/overlay_error.png
new file mode 100644
index 000000000..5fe017433
Binary files /dev/null and b/org_apg/res/drawable-mdpi/overlay_error.png differ
diff --git a/org_apg/res/drawable-mdpi/overlay_ok.png b/org_apg/res/drawable-mdpi/overlay_ok.png
new file mode 100644
index 000000000..b4f332260
Binary files /dev/null and b/org_apg/res/drawable-mdpi/overlay_ok.png differ
diff --git a/org_apg/res/drawable-mdpi/signed.png b/org_apg/res/drawable-mdpi/signed.png
new file mode 100644
index 000000000..d2917644c
Binary files /dev/null and b/org_apg/res/drawable-mdpi/signed.png differ
diff --git a/org_apg/res/drawable-mdpi/signed_large.png b/org_apg/res/drawable-mdpi/signed_large.png
new file mode 100644
index 000000000..ab9495e7b
Binary files /dev/null and b/org_apg/res/drawable-mdpi/signed_large.png differ
diff --git a/org_apg/res/drawable-mdpi/signed_small.png b/org_apg/res/drawable-mdpi/signed_small.png
new file mode 100644
index 000000000..4202c3f97
Binary files /dev/null and b/org_apg/res/drawable-mdpi/signed_small.png differ
diff --git a/org_apg/res/drawable-xhdpi/icon.png b/org_apg/res/drawable-xhdpi/icon.png
new file mode 100644
index 000000000..ffeb8cdf3
Binary files /dev/null and b/org_apg/res/drawable-xhdpi/icon.png differ
diff --git a/org_apg/res/drawable/btn_circle_disable.png b/org_apg/res/drawable/btn_circle_disable.png
new file mode 100644
index 000000000..33b74a66c
Binary files /dev/null and b/org_apg/res/drawable/btn_circle_disable.png differ
diff --git a/org_apg/res/drawable/btn_circle_disable_focused.png b/org_apg/res/drawable/btn_circle_disable_focused.png
new file mode 100644
index 000000000..005ad8dca
Binary files /dev/null and b/org_apg/res/drawable/btn_circle_disable_focused.png differ
diff --git a/org_apg/res/drawable/btn_circle_normal.png b/org_apg/res/drawable/btn_circle_normal.png
new file mode 100644
index 000000000..fc5af1c9f
Binary files /dev/null and b/org_apg/res/drawable/btn_circle_normal.png differ
diff --git a/org_apg/res/drawable/btn_circle_pressed.png b/org_apg/res/drawable/btn_circle_pressed.png
new file mode 100644
index 000000000..8f40afdfc
Binary files /dev/null and b/org_apg/res/drawable/btn_circle_pressed.png differ
diff --git a/org_apg/res/drawable/btn_circle_selected.png b/org_apg/res/drawable/btn_circle_selected.png
new file mode 100644
index 000000000..c74fac227
Binary files /dev/null and b/org_apg/res/drawable/btn_circle_selected.png differ
diff --git a/org_apg/res/drawable/encrypted.png b/org_apg/res/drawable/encrypted.png
new file mode 100644
index 000000000..2783804bc
Binary files /dev/null and b/org_apg/res/drawable/encrypted.png differ
diff --git a/org_apg/res/drawable/encrypted_large.png b/org_apg/res/drawable/encrypted_large.png
new file mode 100644
index 000000000..6d7c616a4
Binary files /dev/null and b/org_apg/res/drawable/encrypted_large.png differ
diff --git a/org_apg/res/drawable/encrypted_small.png b/org_apg/res/drawable/encrypted_small.png
new file mode 100644
index 000000000..7f4ab803f
Binary files /dev/null and b/org_apg/res/drawable/encrypted_small.png differ
diff --git a/org_apg/res/drawable/ic_btn_round_minus.png b/org_apg/res/drawable/ic_btn_round_minus.png
new file mode 100644
index 000000000..96dbb17d2
Binary files /dev/null and b/org_apg/res/drawable/ic_btn_round_minus.png differ
diff --git a/org_apg/res/drawable/ic_btn_round_plus.png b/org_apg/res/drawable/ic_btn_round_plus.png
new file mode 100644
index 000000000..1ec8a956a
Binary files /dev/null and b/org_apg/res/drawable/ic_btn_round_plus.png differ
diff --git a/org_apg/res/drawable/ic_launcher_folder.png b/org_apg/res/drawable/ic_launcher_folder.png
new file mode 100644
index 000000000..ed31ba580
Binary files /dev/null and b/org_apg/res/drawable/ic_launcher_folder.png differ
diff --git a/org_apg/res/drawable/ic_launcher_folder_small.png b/org_apg/res/drawable/ic_launcher_folder_small.png
new file mode 100644
index 000000000..5df8d60f0
Binary files /dev/null and b/org_apg/res/drawable/ic_launcher_folder_small.png differ
diff --git a/org_apg/res/drawable/ic_next.png b/org_apg/res/drawable/ic_next.png
new file mode 100644
index 000000000..8271c1380
Binary files /dev/null and b/org_apg/res/drawable/ic_next.png differ
diff --git a/org_apg/res/drawable/ic_previous.png b/org_apg/res/drawable/ic_previous.png
new file mode 100644
index 000000000..ef90db972
Binary files /dev/null and b/org_apg/res/drawable/ic_previous.png differ
diff --git a/org_apg/res/drawable/key.png b/org_apg/res/drawable/key.png
new file mode 100644
index 000000000..de7e72524
Binary files /dev/null and b/org_apg/res/drawable/key.png differ
diff --git a/org_apg/res/drawable/key_large.png b/org_apg/res/drawable/key_large.png
new file mode 100644
index 000000000..6f18c0240
Binary files /dev/null and b/org_apg/res/drawable/key_large.png differ
diff --git a/org_apg/res/drawable/key_small.png b/org_apg/res/drawable/key_small.png
new file mode 100644
index 000000000..121803508
Binary files /dev/null and b/org_apg/res/drawable/key_small.png differ
diff --git a/org_apg/res/drawable/overlay_error.png b/org_apg/res/drawable/overlay_error.png
new file mode 100644
index 000000000..2372de59e
Binary files /dev/null and b/org_apg/res/drawable/overlay_error.png differ
diff --git a/org_apg/res/drawable/overlay_ok.png b/org_apg/res/drawable/overlay_ok.png
new file mode 100644
index 000000000..2f0005898
Binary files /dev/null and b/org_apg/res/drawable/overlay_ok.png differ
diff --git a/org_apg/res/drawable/signed.png b/org_apg/res/drawable/signed.png
new file mode 100644
index 000000000..490e94fbd
Binary files /dev/null and b/org_apg/res/drawable/signed.png differ
diff --git a/org_apg/res/drawable/signed_large.png b/org_apg/res/drawable/signed_large.png
new file mode 100644
index 000000000..92e64dc51
Binary files /dev/null and b/org_apg/res/drawable/signed_large.png differ
diff --git a/org_apg/res/drawable/signed_small.png b/org_apg/res/drawable/signed_small.png
new file mode 100644
index 000000000..590220281
Binary files /dev/null and b/org_apg/res/drawable/signed_small.png differ
diff --git a/org_apg/res/layout/about_activity.xml b/org_apg/res/layout/about_activity.xml
new file mode 100644
index 000000000..a85439ee1
--- /dev/null
+++ b/org_apg/res/layout/about_activity.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/res/layout/account_item.xml b/org_apg/res/layout/account_item.xml
new file mode 100644
index 000000000..0aa76719a
--- /dev/null
+++ b/org_apg/res/layout/account_item.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/add_account_dialog.xml b/org_apg/res/layout/add_account_dialog.xml
new file mode 100644
index 000000000..d44ce9766
--- /dev/null
+++ b/org_apg/res/layout/add_account_dialog.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/create_key.xml b/org_apg/res/layout/create_key.xml
new file mode 100644
index 000000000..d8ccc73ec
--- /dev/null
+++ b/org_apg/res/layout/create_key.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/decrypt.xml b/org_apg/res/layout/decrypt.xml
new file mode 100644
index 000000000..8bfa76f01
--- /dev/null
+++ b/org_apg/res/layout/decrypt.xml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/edit_key.xml b/org_apg/res/layout/edit_key.xml
new file mode 100644
index 000000000..88be75d86
--- /dev/null
+++ b/org_apg/res/layout/edit_key.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/edit_key_key_item.xml b/org_apg/res/layout/edit_key_key_item.xml
new file mode 100644
index 000000000..850879f64
--- /dev/null
+++ b/org_apg/res/layout/edit_key_key_item.xml
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/edit_key_section.xml b/org_apg/res/layout/edit_key_section.xml
new file mode 100644
index 000000000..d14748d4b
--- /dev/null
+++ b/org_apg/res/layout/edit_key_section.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/edit_key_user_id_item.xml b/org_apg/res/layout/edit_key_user_id_item.xml
new file mode 100644
index 000000000..6e7b552dd
--- /dev/null
+++ b/org_apg/res/layout/edit_key_user_id_item.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/encrypt.xml b/org_apg/res/layout/encrypt.xml
new file mode 100644
index 000000000..68808c8bf
--- /dev/null
+++ b/org_apg/res/layout/encrypt.xml
@@ -0,0 +1,382 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/file_dialog.xml b/org_apg/res/layout/file_dialog.xml
new file mode 100644
index 000000000..bc5a2a214
--- /dev/null
+++ b/org_apg/res/layout/file_dialog.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/filter_info.xml b/org_apg/res/layout/filter_info.xml
new file mode 100644
index 000000000..9fef6548e
--- /dev/null
+++ b/org_apg/res/layout/filter_info.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/general.xml b/org_apg/res/layout/general.xml
new file mode 100644
index 000000000..c6702efac
--- /dev/null
+++ b/org_apg/res/layout/general.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/info.xml b/org_apg/res/layout/info.xml
new file mode 100644
index 000000000..2507029c4
--- /dev/null
+++ b/org_apg/res/layout/info.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_list.xml b/org_apg/res/layout/key_list.xml
new file mode 100644
index 000000000..ac07801ab
--- /dev/null
+++ b/org_apg/res/layout/key_list.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/res/layout/key_list_child_item_master_key.xml b/org_apg/res/layout/key_list_child_item_master_key.xml
new file mode 100644
index 000000000..998ba256f
--- /dev/null
+++ b/org_apg/res/layout/key_list_child_item_master_key.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_list_child_item_sub_key.xml b/org_apg/res/layout/key_list_child_item_sub_key.xml
new file mode 100644
index 000000000..ac7c217a6
--- /dev/null
+++ b/org_apg/res/layout/key_list_child_item_sub_key.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_list_child_item_user_id.xml b/org_apg/res/layout/key_list_child_item_user_id.xml
new file mode 100644
index 000000000..598dcef22
--- /dev/null
+++ b/org_apg/res/layout/key_list_child_item_user_id.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_list_group_item.xml b/org_apg/res/layout/key_list_group_item.xml
new file mode 100644
index 000000000..35d0ab367
--- /dev/null
+++ b/org_apg/res/layout/key_list_group_item.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_server_editor.xml b/org_apg/res/layout/key_server_editor.xml
new file mode 100644
index 000000000..a02540c2c
--- /dev/null
+++ b/org_apg/res/layout/key_server_editor.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_server_export_layout.xml b/org_apg/res/layout/key_server_export_layout.xml
new file mode 100644
index 000000000..b2270417d
--- /dev/null
+++ b/org_apg/res/layout/key_server_export_layout.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_server_preference.xml b/org_apg/res/layout/key_server_preference.xml
new file mode 100644
index 000000000..2f5645f62
--- /dev/null
+++ b/org_apg/res/layout/key_server_preference.xml
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_server_query_layout.xml b/org_apg/res/layout/key_server_query_layout.xml
new file mode 100644
index 000000000..6af4f3644
--- /dev/null
+++ b/org_apg/res/layout/key_server_query_layout.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_server_query_result_item.xml b/org_apg/res/layout/key_server_query_result_item.xml
new file mode 100644
index 000000000..29c8b88f4
--- /dev/null
+++ b/org_apg/res/layout/key_server_query_result_item.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/key_server_query_result_user_id.xml b/org_apg/res/layout/key_server_query_result_user_id.xml
new file mode 100644
index 000000000..9d3a4a1ab
--- /dev/null
+++ b/org_apg/res/layout/key_server_query_result_user_id.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/org_apg/res/layout/mailbox_message_item.xml b/org_apg/res/layout/mailbox_message_item.xml
new file mode 100644
index 000000000..05a267fb6
--- /dev/null
+++ b/org_apg/res/layout/mailbox_message_item.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/main.xml b/org_apg/res/layout/main.xml
new file mode 100644
index 000000000..803b2abe9
--- /dev/null
+++ b/org_apg/res/layout/main.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/pass_phrase.xml b/org_apg/res/layout/pass_phrase.xml
new file mode 100644
index 000000000..d66ffca07
--- /dev/null
+++ b/org_apg/res/layout/pass_phrase.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/select_public_key.xml b/org_apg/res/layout/select_public_key.xml
new file mode 100644
index 000000000..9ce35a7a8
--- /dev/null
+++ b/org_apg/res/layout/select_public_key.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/select_public_key_item.xml b/org_apg/res/layout/select_public_key_item.xml
new file mode 100644
index 000000000..beca23176
--- /dev/null
+++ b/org_apg/res/layout/select_public_key_item.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/select_secret_key.xml b/org_apg/res/layout/select_secret_key.xml
new file mode 100644
index 000000000..feabc6160
--- /dev/null
+++ b/org_apg/res/layout/select_secret_key.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/select_secret_key_item.xml b/org_apg/res/layout/select_secret_key_item.xml
new file mode 100644
index 000000000..022545152
--- /dev/null
+++ b/org_apg/res/layout/select_secret_key_item.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/layout/sign_key_layout.xml b/org_apg/res/layout/sign_key_layout.xml
new file mode 100644
index 000000000..17be03b21
--- /dev/null
+++ b/org_apg/res/layout/sign_key_layout.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org_apg/res/values-da/strings.xml b/org_apg/res/values-da/strings.xml
new file mode 100644
index 000000000..418372fb9
--- /dev/null
+++ b/org_apg/res/values-da/strings.xml
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+ Mail Inbox
+ Administrér Offentlige Nøgler
+ Administrér Private Nøgler
+ Vælg Modtagere
+ Vælg Signatur
+ Kryptér
+ Afkryptér
+ Kodeord
+ Opret Nøgle
+ Redigér Nøgle
+ Indstillinger
+ Skift Kodeord
+ Sæt Kodeord
+ "Send Mail..."
+ Kryptér Til Fil
+ Afkryptér Til Fil
+ Tilføj Konto
+ Importér Nøgle
+ Eksportér Nøgle
+ Eksportér Nøgler
+ Ingen Nøgle Matchede
+
+
+ Bruger ID
+ Nøgler
+ Generelt
+ Standard Indstillinger
+
+
+ Kryptér Til Clipboard
+ Kryptér Og Mail
+ Kryptér
+ Afkryptér
+ Bekræft
+ Vælg Modtagere
+ Svar
+ Kryptér Besked
+ Afkryptér Besked
+ Kryptér Fil
+ Afkryptér Fil
+ Gem
+ Fortryd
+ Slet
+ Ingen
+ Ryd Filter
+ Skift Kodeord
+ Sæt Kodeord
+
+
+ Om APG
+ Tilføj GMail Konto
+ Slet Konto
+ Administrér Offentlige Nøgler
+ Administrér Private Nøgler
+ Indstillinger
+ Importér Nøgler
+ Eksportér Nøgler
+ Eksportér Nøgle
+ Slet Nøgle
+ Opret Nøgle
+ Redigér Nøgle
+ Søg
+
+
+ Signér
+ Besked
+ Fil
+ Kodeord
+ Igen
+ Algoritme
+ ASCII Rustning
+ Offentlig(e) Nøgle(r)
+ Slet Efter Kryptering
+ Slet Efter Afkryptering
+ Krypteringsalgoritme
+ Hash-algoritme
+ Offentlig Nøgle
+ Kodeord
+ Kodeords Cache
+ Besked Komprimering
+ Fil Komprimering
+ Vælg
+ 1 Valgt
+ Valgt
+ <ukendt>
+ <ingen>
+ <ingen nøgle>
+ -
+ <udløber ikke>
+
+ kan kryptere
+ kan signere
+ udløbet
+ ugyldig
+
+
+ Ingen
+ Kun signering
+ Kun kryptering
+ Signér og kryptér
+ 15 secs
+ 1 min
+ 3 min
+ 5 min
+ 10 min
+ 20 min
+ 40 min
+ 1 timer
+ 2 timer
+ 4 timer
+ 8 timer
+ Indtil slut
+ DSA
+ ElGamal
+ RSA
+ Åbn...
+ Gem Som...
+ Vælg Fil Som Skal Krypteres...
+ Vælg Fil Som Skal Afkrypteres...
+ Åbn
+ Gem
+ Advarsel
+ Fejl
+ Advarsel: %s
+ Fejl: %s
+
+
+ Forkert kodeord.
+ Bruger clipboardets indhold.
+ Nøgle gemt.
+ Sæt et kodeord først.
+ Der er ikke installeret en kompatibel fil håndtering.
+ Kodeordet matchede ikke.
+ Et tomt kodeord er ikke tilladt.
+ Symmetrisk kryptering.
+ %s
+ Er du sikker på at du vil slette \n%s?
+ Filen er slettet.
+ Vælg en fil først.
+ Afkryptering lykkedes.
+ Kryptering lykkedes.
+ Kryptering til clipboard lykkedes.
+ Tast kodeordet to gange.
+ Vælg mindst én krypteringsnøgle.
+ Vælg mindst én krypteringsnøgle eller signaturnøgle.
+ Angiv navnet på filen der skal krypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
+ Angiv navnet på filen der skal afkrypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
+ Angiv den Google Mail konto du ønsker at tilføje.
+ Angiv hvilken fil du ønsker at importere nøgler fra. (.asc eller .gpg)
+ Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
+ Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Du er ved at eksportere PRIVATE nøgler.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
+ Ønsker du at slette \'%s\'-nøglen?\nDu kan ikke fortryde denne handling!
+ Ønsker du at slette den PRIVATE nøgle, \'%s\'?\nDu kan ikke fortryde denne handling!
+ Tilføjelse af %1$s nøgle(r) og opdatering af %2$s nøgle(r) lykkedes."
+ Tilføjelse af %s nøgle(r) lykkedes.
+ Opdatring af %s nøgle(r) lykkedes.
+ Ingen nøgler blev tilføjet eller opdateret.
+ Eksport af 1 nøgle lykkedes.
+ Eksport af %s nøgler lykkedes.
+ Ingen nøgler blev eksporteret.
+ NB: Kun undernøgler understøtter ElGamal, og for ELGamal vil den nærmeste nøglestørrelse - 1536, 2048, 3072, 4096, eller 8192 - blive brugt.
+ Kunne ikke finde %08X nøglen.
+
+
+ \'%s\' kunne ikke slettes
+ filen findes ikke
+ ingen egnet privat nøgle fundet
+ ingen kendt krypterings måde fundet
+ ekstern storage er ikke parat
+ \'%s\'-kontoen blev ikke fundet
+ \'%s\'-kontoen kunne ikke tilføjes
+ ugyldig email \'%s\'
+ nøgle størrelsen skal være på mindst 512bit
+ hoved nøglen kan ikke være en ElGamal nøgle
+ ukendt algoritme valg
+ angiv et navn
+ angiv en email adresse
+ der skal bruges mindst ét bruger-id
+ hovedbruger-id kan ikke være tomt
+ der skal mindst bruges en hovednøgle
+ udløbsdatoen skal være senere end oprettelsesdatoen
+ hverken krypteringsnøgler eller kodeord modtaget
+ signering mislykkedes
+ intet kodeord modtaget
+ ingen signatur nøgle modtaget
+ ikke gyldig krypteringsdata
+ beskadiget data
+ kunne ikke finde en pakke med symmetrisk kryptering
+ forkert kodeord
+ der opstod en fejl da nøglen eller nøglerne skulle gemmes
+
+
+ færdig.
+ starter op...
+ gemmer...
+ importerer...
+ eksporterer...
+ genererer nøgle, dette kan tage et stykke tid...
+ opbygger nøgle...
+ forbereder hovednøgle...
+ bekræfter hovednøgle...
+ bygger hovednøglering...
+ tilføjer undernøgler...
+ gemmer nøglering...
+ importerer private nøgler...
+ importerer offentlige nøgler...
+ genloader nøgler...
+ eksporterer nøgle...
+ eksporterer nøgler...
+ henter signatur nøgle...
+ henter nøgle...
+ forbereder streams...
+ krypterer data...
+ afkrypterer data...
+ forbereder signatur...
+ skaber signatur...
+ behandler signatur...
+ bekræfter signatur...
+ signerer...
+ læser data...
+ søger nøgle...
+ afkomprimerer data...
+ bekræfter integritet...
+
+
+ Læs nøgle detaljer fra APG.
+ Læs om offentlige og private nøgler gemt i APG, f. eks. nøgle ID og bruger ID. Selve nøglerne kan IKKE læses.
+
+
+ Kryptér
+ Afkryptér
+ Importér Offentlige Nøgler
+ Importér Private Nøgler
+ Søg I Offentlige Nøgler
+ Søg I Private Nøgler
+ Filter: \"%s\"
+
+
\ No newline at end of file
diff --git a/org_apg/res/values-de/strings.xml b/org_apg/res/values-de/strings.xml
new file mode 100644
index 000000000..732d58b0a
--- /dev/null
+++ b/org_apg/res/values-de/strings.xml
@@ -0,0 +1,269 @@
+
+
+
+
+
+
+ Posteingang
+ Öffentliche Schlüssel verwalten
+ Private Schlüssel verwalten
+ Empfänger auswählen
+ Signatur auswählen
+ Verschlüsseln
+ Entschlüsseln
+ Passwort
+ Schlüssel erzeugen
+ Schlüssel bearbeiten
+ Einstellungen
+ Passwort ändern
+ Passwort wählen
+ "Mail senden ..."
+ In eine Datei verschlüsseln
+ In eine Datei entschlüsseln
+ Konto hinzufügen
+ Schlüssel importieren
+ Einzelnen Schlüssel exportieren
+ Mehrere Schlüssel exportieren
+ Kein Schlüssel gefunden
+
+
+ Benutzer-IDs
+ Schlüssel
+ Allgemein
+ Voreinstellungen
+
+
+
+ In die Zwischenablage verschlüsseln
+ Verschlüsseln und per Mail senden
+ Verschlüsseln
+ Entschlüsseln
+ Verifizieren
+ Empfänger auswählen
+ Antworten
+ Nachricht verschlüsseln
+ Nachricht entschlüsseln
+ Datei verschlüsseln
+ Datei entschlüsseln
+ Speichern
+ Abbrechen
+ Löschen
+ Niemals
+ Filter zurücksetzen
+ Passwort ändern
+ Passwort wählen
+
+
+ Über
+ GMail-Konto hinzufügen
+ Konto entfernen
+ Öffentliche Schlüssel verwalten
+ Private Schlüssel verwalten
+ Einstellungen
+ Schlüssel importieren
+ Mehrere Schlüssel exportieren
+ Einzelnen Schlüssel exportieren
+ Einzelnen Schlüssel löschen
+ Schlüssel erzeugen
+ Schlüssel bearbeiten
+ Suche
+
+
+ Signieren
+ Nachricht
+ Datei
+ Passwort
+ Wiederholen
+ Algorithmus
+ ASCII Armor
+ Öffentliche(r) Schlüssel
+ Nach Verschlüsselung löschen
+ Nach Entschlüsselung löschen
+ Verschlüsselungs-Algorithmus
+ Hash-Algorithmus
+ Öffentlicher Schlüssel
+ Passwort
+ Passwort-Cache
+ Nachrichten-Kompression
+ Datei-Kompression
+ Auswählen
+ 1 ausgewählt
+ Ausgewählt
+ <Unbekannt>
+ <Kein>
+ <Kein Schlüssel>
+ -
+ <Kein Verfallsdatum>
+
+ Kann verschlüsseln
+ Kann signieren
+ Verfallen
+ Ungültig
+
+
+ Keine Auswahl
+ Nur signieren
+ Nur verschlüsseln
+ Signieren und verschlüsseln
+ 15 Sek.
+ 1 Min.
+ 3 Min.
+ 5 Min.
+ 10 Min.
+ 20 Min.
+ 40 Min.
+ 1 Std.
+ 2 Std.
+ 4 Std.
+ 8 Std.
+ Bis Programmende
+ DSA
+ ElGamal
+ RSA
+ Öffnen ...
+ Speichern unter ...
+ Zu entschlüsselnde Datei wählen ...
+ Zu verschlüsselnde Datei wählen ...
+ Öffnen
+ Speichern
+ Achtung
+ Fehler
+ Achtung: %s
+ Fehler: %s
+
+
+ Falsches Passwort.
+ Zwischenablage wird verwendet.
+ Schlüssel gespeichert.
+ Zuerst Passwort festlegen.
+ Kein kompatibler Dateimanager installiert.
+ Passwörter stimmen nicht überein.
+ Leere Passwörter sind unzulässig.
+ Symmetrische Verschlüsselung.
+ %s
+ Soll\n%s wirklich gelöscht werden?
+ Erfolgreich gelöscht.
+ Zuerst eine Datei auswählen.
+ Entschlüsselung erfolgreich.
+ Verschlüsselung erfolgreich.
+ Verschlüsselung in die Zwischenablage erfolgreich.
+ Passwort bitte zwei Mal eingeben.
+ Bitte mindestens einen Schlüssel zur Verschlüsselung auswählen.
+ Bitte mindestens einen Schlüssel zur Verschlüsselung oder zum Signieren auswählen.
+ Bitte Zieldatei für Verschlüsselung angeben.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
+ Bitte Zieldatei für Entschlüsselung angeben.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
+ Bitte Google-Mail-Konto angeben, das hinzugefügt werden soll.
+ Bitte Datei angeben, aus der Schlüssel importiert werden sollen. (.asc oder .gpg)
+ Bitte Zieldatei zum Export angeben.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
+ Bitte Zieldatei zum Export angeben.\nACHTUNG! Es sollen private Schlüssel exportiert werden.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
+ Soll der Schlüssel \'%s\' wirklich gelöscht werden?\nDiese Aktion lässt sich nicht rückgängig machen!
+ Soll der PRIVATE Schlüssel \'%s\' wirklich gelöscht werden?\nDiese Aktion lässt sich nicht rückgängig machen!
+ Erfolgreich %1$s Schlüssel hinzugefügt und %2$s Schlüssel aktualisiert.
+ Erfolgreich %s Schlüssel hinzugefügt.
+ Erfolgreich %s Schlüssel aktualisiert.
+ Keine Schlüssel hinzugefügt oder aktualisiert.
+ Erfolgreich 1 Schlüssel exportiert.
+ Erfolgreich %s Schlüssel exportiert.
+ Kein Schlüssel exportiert.
+ Hinweis: Nur Subschlüssel unterstützen ElGamal, und für ElGamal wird die nächstmögliche Schlüssellänge 1536, 2048, 3072, 4096 oder 8192 genutzt.
+ Es konnte kein Schlüssel %08X gefunden werden.
+
+
+ Löschen von \'%s\' fehlgeschlagen
+ Datei nicht gefunden
+ Kein geeigneter privater Schlüssel gefunden
+ Keine bekannte Verschlüsselungsmethode gefunden
+ Externer Speicher nicht bereit
+ Konto \'%s\' nicht gefunden
+ Hinzufügen von Konto \'%s\' fehlgeschlagen
+ Ungültige E-Mail \'%s\'
+ Schlüssellänge von mindestens 512bit erforderlich
+ Primärschlüssel darf kein ElGamal-Schlüssel sein
+ Unbekannter Algorithmus ausgewählt
+ Angabe eines Namens erforderlich
+ Angabe einer E-Mail-Adresse erforderlich
+ Mindestens eine Benutzer-ID erforderlich
+ Haupt-Benutzer-ID darf nicht leer bleiben
+ Mindestens ein Primärschlüssel erforderlich
+ Verfallsdatum muss später als Erstellungsdatum liegen
+ Kein(e) Schlüssel oder Passwort zur Verschlüsselung angegeben
+ Signieren fehlgeschlagen
+ Kein Passwort angegeben
+ Kein Schlüssel zum Signieren angegeben
+ Keine gültigen Verschlüsselungsdaten
+ Daten nicht lesbar
+ Kein Paket mit symmetrischer Verschlüsselung gefunden
+ Falsches Passwort
+ Fehler beim Abspeichern eines oder mehrerer Schlüssel
+
+
+ Abgeschlossen.
+ Initialisierung ...
+ Speicherung ...
+ Import ...
+ Export ...
+ Schlüssel wird erzeugt, dies kann eine Weile dauern ...
+ Schlüssel wird erstellt ...
+ Primärschlüssel wird vorbereitet ...
+ Primärschlüssel wird zertifiziert ...
+ Primärschlüsselbund wird erstellt ...
+ Unterschlüssel werden hinzugefügt ...
+ Schlüsselbund wird gespeichert ...
+ Private Schlüssel werden importiert ...
+ Öffentliche Schlüssel werden importiert ...
+ Schlüssel werden neu geladen ...
+ Schlüssel wird exportiert ...
+ Schlüssel werden exportiert ...
+ Signaturschlüssel wird extrahiert ...
+ Schlüssel wird extrahiert ...
+ Daten werden vorbereitet ...
+ Daten werden verschlüsselt ...
+ Daten werden entschlüsselt ...
+ Signatur wird vorbereitet ...
+ Signatur wird erzeugt ...
+ Signatur wird verarbeitet...
+ Signatur wird verifiziert ...
+ Signieren ...
+ Daten werden gelesen ...
+ Schlüssel wird gesucht ...
+ Daten werden dekomprimiert ...
+ Integrität wird verifiziert ...
+
+
+ Schlüsseldetails von APG auslesen.
+ Details der öffentlichen und privaten Schlüssel können von APG ausgelesen werden, etwa Schlüssel-ID und Benutzer-IDs. Die Schlüssel selbst können NICHT gelesen werden.
+
+
+ Verschlüsseln
+ Entschlüsseln
+ Öffentliche Schlüssel importieren
+ Private Schlüssel importieren
+ Öffentliche Schlüssel suchen
+ Private Schlüssel suchen
+ Filter: \"%s\"
+
+
+ schnell
+ langsam
+ sehr langsam
+
+
\ No newline at end of file
diff --git a/org_apg/res/values-es/strings.xml b/org_apg/res/values-es/strings.xml
new file mode 100644
index 000000000..a2faf56d9
--- /dev/null
+++ b/org_apg/res/values-es/strings.xml
@@ -0,0 +1,303 @@
+
+
+
+
+
+
+ Bandeja de Entrada
+ Administrar Llaves Publicas
+ Administrar Llaves Privadas
+ Seleccionar Destinatarios
+ Seleccionar Firma
+ Cifrar
+ Descifrar
+ Frase de Paso
+ Crear Llave
+ Editar Llave
+ Preferencias
+ Preferencias del Servidor de Llaves
+ Cambiar Frase de Paso
+ Establecer Frase de Paso
+ "Enviar Correo..."
+ Cifrar en Fichero
+ Descifrar en Fichero
+ Añadir Cuenta
+ Importar Llaves
+ Exportar Llave
+ Exportar Llaves
+ Llave no Encontrada
+ Empezando
+ Petición al Servidor de Llaves
+
+
+ Identidades de Usuario
+ Llaves
+ General
+ Valores por Defecto
+ Avanzado
+
+
+ Firmar En Portapapeles
+ Cifrar En Portapapeles
+ Cifrar y Enviar
+ Firmar y Enviar
+ Cifrar
+ Firmar
+ Descifrar
+ Verificar
+ Seleccionar Destinatarios
+ Contestar
+ Cifrar Mensaje
+ Descifrar Mensaje
+ Cifrar Fichero
+ Descifrar Mensaje
+ Guardar
+ Cancelar
+ Borrar
+ Ninguno
+ Limpiar Filtro
+ Cambiar Frase de Paso
+ Establecer Frase de Paso
+ Buscar
+
+
+ Acerca De
+ Añadir Cuenta de Gmail
+ Borrar Cuenta
+ Administrar Llaves Publicas
+ Administrar Llaves Privadas
+ Preferencias
+ Importar Llaves
+ Exportar Llaves
+ Exportar Llave
+ Borrar Llave
+ Crear Llave
+ Editar Llave
+ Buscar
+ Ayuda
+ Servidor de Llaves
+ Actualizar
+
+
+ Firmar
+ Mensaje
+ Fichero
+ Frase de Paso
+ Otra Vez
+ Algoritmo
+ Armadura ASCII
+ Llave(s) Publica(s)
+ Borrar Una Vez Cifrado
+ Borrar Una Vez Descifrado
+ Borrar Tras Importar
+ Algoritmo De Cifrado
+ Algoritmo Hash
+ Llave Pública
+ Frase de Paso
+ Caché De La Frase de Paso
+ Compresión de Mensaje
+ Compresión de Fichero
+ Idioma
+ Forzar Firmas V3
+ Servidores De Llaves
+ Identidad de Llave
+ Creación
+ Expira
+ Uso
+ Tamaño De La Llave
+ Identidad Principal Del Usuario
+ Nombre
+ Comentario
+ Email
+ Seleccionar
+ 1 Seleccionada
+ Seleccionada
+ <desconocido>
+ <ninguno>
+ <sin llave>
+ -
+ <no expira>
+
+ puede cifrar
+ puede firmar
+ expirada
+ no valida
+ %s servidor de llave(s)
+
+
+ Ninguno
+ Sólo firmar
+ Sólo cifrar
+ Firmar y Cifrar
+ 15 segs
+ 1 min
+ 3 mins
+ 5 mins
+ 10 mins
+ 20 mins
+ 40 mins
+ hasta salir
+ Sistema por defecto
+ DSA
+ ElGamal
+ RSA
+ Abrir...
+ Guardar Como...
+ Seleccionar Fichero Para Cifrar...
+ Seleccionar Fichero Para Descifrar...
+ Abrir
+ Guardar
+ Advertencia
+ Error
+ Advertencia: %s
+ Error: %s
+
+
+ Frase de paso incorrecta.
+ Usando contenido del portapapeles.
+ Llave guardada.
+ Establece primero una frase de paso.
+ Administrador de ficheros instalado no compatible.
+ La frase de paso no concuerda.
+ No se permiten palabras de paso vacías.
+ Cifrado simetrico.
+ %s
+ ¿Seguro que quieres borrar\n%s?
+ Borrado completo.
+ Selecciona primero un fichero.
+ Descifrado correcto.
+ Cifrado correcto.
+ Cifrado en portapapeles correcto.
+ Introduce la frase de paso dos veces.
+ Selecciona al menos una llave de cifrado.
+ Selecciona al menos una llave de cifrado o de firma.
+ Por favor, indica el nombre del fichero a cifrar.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
+ Por favor, indica el nombre del fichero a descifrar.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
+ Indica la cuenta de Google Mail que quieres añadir.
+ Por favor, indica el fichero desde el cual importar las llaves. (.asc o .gpg)
+ Por favor, indica el fichero al que exportar.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
+ Por favor, indica el fichero al que exportar.\nADVERTENCIA! Estás a punto de exportar las llaves PRIVADAS.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
+ ¿Realmente quieres borrar la llave \'%s\'?\nNo puede deshacerse!
+ ¿Realmente quieres borrar la llave SECRETA \'%s\'?\nNo puede deshacerse!
+ Se han añadido %1$s llave(s) y actualizado %2$s llave(s) de forma correcta."
+ Se ha(n) añadido %s llave(s) de forma correcta.
+ Se ha(n) actualizado %s llave(s) de forma correcta.
+ No se ha añadido ni actualizado ninguna llave.
+ Se ha exportado correctamente 1 llave.
+ Se han exportado %s llaves de forma correcta.
+ No se han exportado llaves.
+ Nota: solamente las sub-llaves admiten ElGamal, y para ElGamal se usará el tamaño más cercano a 1536, 2048, 3072, 4096, o 8192.
+ No se pudo encontrar la llave %08X.
+ Se ha(n) encontrado %s llave(s).
+ Firma desconocida, pulsa para buscarla.
+ Edición de llaves aún en fase beta.
+
+
+ borrado de \'%s\' fallido
+ no se encontró el fichero
+ no se encontró la llave secreta adecuada
+ no se encontró un medio de cifrado
+ el sistema de almacenamiento externo no está preparado
+ la cuenta \'%s\' no se encontró
+ no tienes permisos para leer esta cuenta
+ la creación de la cuenta \'%s\' ha fallado
+ dirección de correo inválida \'%s\'
+ el tamaño mínimo debe ser de 512bits
+ la llave maestra no puede ser ElGamal
+ algoritmo desconocido
+ necesitas especificar un nombre
+ necesitas indicat una dirección de correo
+ necesitas al menos una identidad de usuario
+ el usuario principal no puede estar vacío
+ necesitas al menos una llae maestra
+ la fecha de expiración debe ser posterior a la fecha de creación
+ no se ha(n) indicado llave(s) de cifrado o frase de paso
+ el proceso de firma ha fallado
+ no se ha indicado frase de paso
+ no se ha indicado llave de firma
+ datos de cifrado no válidos
+ datos corruptos
+ no se ha podido encontrar un paquete con cifrado simétrico
+ frase de paso incorrecta
+ hubo un error al guardar alguna(s) llave(s)
+
+
+ hecho.
+ iniciando...
+ guardando...
+ importando...
+ exportando...
+ generando la llave, esto puede tardar un rato...
+ creando la llave...
+ preparando la llave maestra...
+ certificando la llave maestra...
+ creando el anillo maestro de llaves...
+ añadiendo sub-llaves...
+ guardando anillo de llaves...
+ importando llaves privadas...
+ importando llaves públicas...
+ re-cargando llaves...
+ exportando llave...
+ exportando llaves...
+ extrayendo llave de firma...
+ extrayendo llave...
+ preparando flujos...
+ cifrando datos...
+ descifrando datos...
+ preparando firma...
+ generando firma...
+ procesanto firma...
+ verificando firma...
+ firmando...
+ leyendo datos...
+ buscando la llave...
+ descomprimiendo datos...
+ verificando integridad...
+ borrando \'%s\' de forma segura...
+ realizando petición...
+ realizando %s petición(es)...
+
+
+ Leer detalles de llave desde APG.
+ Leer detalles de llaves pública y privada almacenados en APG, como identidades de llave y usuario. Las llaves en si NO se pueden leer.
+
+
+ Cifrar
+ Descifrar
+ Importar Llaves Públicas
+ Importar Llaves Privadas
+ Buscar Llaves Públicas
+ Buscar Llaves Privadas
+ Filtro: \"%s\"
+
+
+ rápido
+ lento
+ muy lento
+
+
+
+ uedes instalar K-9 Mail para una mejor integración, soporta APG para PGP de forma nativa y permite difrar/descifrar mensajes directamente.
+\n\nSe recomienda instalar OI File Manager o ASTRO para poder usar el botón de navegación para seleccioar ficheros en APG.
+\n\nPrimero necesitarás algunas llaves. Puedes importarlas mediante los menús de opciones en \"Administrar Llaves Públicas\" y \"Administrar Llaves Privadas\" o crearlas mediante \"Administrar Llaves Privadas\".
+\n\nTambién puedes añadir una cuenta GMail como actividad principal mediante \"Añadir Cuenta\", lo que simplifica el descifrado de mensajes recibidos en esa cuenta.
+\n\nDale un vistazo a los menús de opciones para encontrar más funciones.
+
+
\ No newline at end of file
diff --git a/org_apg/res/values-it/strings.xml b/org_apg/res/values-it/strings.xml
new file mode 100644
index 000000000..21a6d3486
--- /dev/null
+++ b/org_apg/res/values-it/strings.xml
@@ -0,0 +1,314 @@
+
+
+
+
+
+
+ Posta in arrivo
+ Gestione Chiave Pubblica
+ Gestione Chiave Privata
+ Seleziona i Destinatari
+ Seleziona Firma
+ Critta
+ Decritta
+ Pass Phrase
+ Crea Chiave
+ Modifica Chiave
+ Preferenze
+ Preferenze Key Server
+ Cambia Pass Phrase
+ Imposta Pass Phrase
+ "Invia mail..."
+ Critta su File
+ Decritta su File
+ Aggiungi Acconto
+ Importa Chiavi
+ Esporta Chiave
+ Esporta Chiavi
+ Chiave non trovata
+ Guida
+ Query Key Server
+ Firma chiave sconosciuta
+
+
+ User IDs
+ Chiavi
+ Generale
+ Defaults
+ Avanzate
+
+
+ Firma su Clipboard
+ Critta su Clipboard
+ Critta ed invia per mail
+ Firma ed invia per mail
+ Critta
+ Firma
+ Decritta
+ Verifica
+ Seleziona i Destinatari
+ Rispondi
+ Critta Messaggio
+ Decritta Messaggio
+ Critta File
+ Decritta File
+ Salva
+ Annulla
+ Cancella
+ Nessuno
+ Cancella Filtro
+ Cambia Pass Phrase
+ Imposta Pass Phrase
+ Cerca
+
+
+ Informazioni
+ Aggiungi Acconto GMail
+ Rimuovi Acconto
+ Gestisci Chiavi Pubbliche
+ Gestisci Chiavi Private
+ Impostazioni
+ Importa Chiavi
+ Esporta Chiavi
+ Esporta Chiave
+ Rimuovi Chiave
+ Crea Chiave
+ Modifica Chiave
+ Cerca
+ Aiuto
+ Key Server
+ Aggiorna
+
+
+ Firma
+ Messaggio
+ File
+ Pass Phrase
+ Conferma Pass Phrase
+ Algoritmo
+ ASCII Armour
+ Chiavi Pubbliche
+ Cancella dopo la crittazione
+ Cancella dopo la decrittazione
+ Cancella dopo l\'importazione
+ Algoritmo di Crittazione
+ Algoritmo di Hash
+ Chiave Pubblica
+ Pass Phrase
+ Pass Phrase Cache
+ Compressione Messaggio
+ Compressione File
+ Lingua
+ Forza l\'uso di firme V3
+ Key Servers
+ ID chiave
+ Creazione
+ Scadenza
+ Utilizzo
+ Dimensione chiave
+ User ID principale
+ Nome
+ Commento
+ Email
+ Seleziona
+ 1 Selezionato
+ Selezionato
+ <sconosciuto>
+ <nessuno>
+ <nessuna chiave>
+ -
+ <nessuna scadenza>
+
+ crittazione consentita
+ firma consentita
+ scaduto
+ non valido
+ %s key server(s)
+ fingerprint
+
+
+ Nessuno
+ Solo Firma
+ Critta solamente
+ Firma e critta
+ 15 sec
+ 1 min
+ 3 min
+ 5 min
+ 10 min
+ 20 min
+ 40 min
+ 1 ora
+ 2 ore
+ 4 ore
+ 8 ore
+ tutta la sessione
+ Default di sistema
+ DSA
+ ElGamal
+ RSA
+ Apri...
+ Salva con nome...
+ Seleziona il file da crittare...
+ Seleziona il file da decrittare...
+ Apri
+ Salva
+ Attenzione
+ Errore
+ Attenzione: %s
+ Errore: %s
+
+
+ Pass Phrase errata.
+ Utilizza il contenuto della clipboard.
+ Chiave salvata..
+ Imposta prima una Pass Phrase.
+ Nessun file manager compatibile installato.
+ La Pass Phrase non è corretta
+ La Pass Phrases non può essere vuota.
+ Crittazione simmetrica.
+ %s
+ Sei sicuro di procedere con la cancellazione di\n%s?
+ Cancellazione eseguita..
+ Selezionare un file.
+ Decrittazione eseguita.
+ Crittazione eseguita.
+ Crittazione vero la clipboard eseguita.
+ Scrivere la Pass Phrase due volte.
+ Selezionare almeno una chiave.
+ Selezionare almeno una chiave oppure una firma.
+ Specificare il file che si desidera crittare.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
+ Specificare il file che si desidera decrittare.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
+ Specificare l\'acconto Google Mail che si vuole aggiungere.
+ Specificare il file da cui importare le chiavi (.asc or .gpg).
+ Specificare il file per l\'esportazione.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
+ Specificare il file per l\'esportazione.\nATTENZIONE! Stai esportando la chiave privata.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
+ Confermi la cancellazione della chiave \'%s\'?\nL\'operazione è irreversibile!
+ Confermi la cancellazione della chiave PRIVATA \'%s\'?\nL\'operazione è irreversibile!
+ Sono state correttamente inserite %1$s chiavi ed aggiornate %2$s chiavi."
+ Sono state correttamente inserite %s chiavi.
+ Sono state correttamente aggiornate %s chiavi.
+ Nessuna chiave è stata aggiunta o modificata.
+ La chiave è stata correttamente esportata.
+ Sono state correttamente esportate %s chiavi.
+ Nessuna chiave è stata esportata.
+ Nota: solo le sotto-chiavi supportano ElGamal, e per ElGamal sarà utilizzata la dimensione più vicina a 1536, 2048, 3072, 4096, oppure 8192.
+ Non è stato possibile trovare la chiave %08X.
+ Trovate %s chiavi.
+ Firma sconosciuta, premi per controllare la chiave.
+ L\'editing delle chiavi è una funzionalità ancora in fase beta.
+ %s chiavi segrete ignorate. Se hai esportato le chiavi con l\'opzione\n --export-secret-subkeys\nprovare ad utilizzare il parametro\n --export-secret-keys\n.
+ La chiave %s è sconosciuta; si desidera provare a cercarla su un keyserver?
+
+
+ cancellazione di \'%s\' fallita
+ file non trovato
+ non è stata trovata nessuna chiave privata utilizzabile
+ non è stato trovato nessun tipo di crittazione valido
+ la scheda di memoria non è pronta o non è utilizzabile
+ acconto \'%s\' non trovato
+ accesso in lettura all\'account negato
+ non è stato possibile aggiungere l\'acconto \'%s\'
+ mail non valida \'%s\'
+ la dimensione della chiave deve essere almeno di 512bit
+ la chiave master non può essere una chiave ElGamal
+ algoritmo selezionato sconosciuto
+ è necessario specificare un nome
+ è necessario specificare una email
+ è necessario almeno uno user id
+ lo user id principale non può essere vuoto
+ è necessaria almeno una chiave master
+ la data di scadenza deve essere successiva alla data di creazione
+ non è stata specifica una chiave oppure una Pass Phrase
+ firma fallita
+ Pass Phrase non specificata
+ firma non specificata
+ dati di crittazione non validi
+ dati corrotti
+ non è stato possibile trovare pacchetto con crittazione simmetrica
+ Pass Phrase errata
+ errore durante il salvataggio di alcune chiavi
+ non è stato possibile estrarre la chiave privata
+
+
+ eseguito.
+ inizializzazione in corso...
+ salvataggio in corso...
+ importazione in corso...
+ esportazione in corso...
+ generazione della chiave in corso; questo processo potrebbe richiedere alcuni minuti...
+ costruzione della chiave in corso...
+ impostazione della chiave master in corso...
+ certificazione della chiave master in corso...
+ costruzione del master key ring in corso...
+ aggiunta delle sotto-chiavi in corso...
+ salvataggio del key ring in corso...
+ importazione delle chiavi private in corso...
+ importazione delle chiavi pubbliche in corso...
+ ricaricamento chiavi in corso...
+ esportazione chiave in corso...
+ esportazione chiavi in corso...
+ estrazione firma in corso...
+ estrazione chiave in corso...
+ preparazione del flusso dati in corso...
+ crittazione in corso...
+ decrittazione in corso...
+ impostazione della firma in corso...
+ generazione della firma in corso...
+ elaborazione della firma in corso...
+ verifica della firma in corso...
+ applicazione della firma in corso...
+ lettura in corso...
+ ricerca chiave in corso...
+ decompressione dei dati in corso...
+ verifica integrità dati in corso...
+ rimozione sicura di \'%s\'...
+ interrogazione in corso...
+ interrogazione di %s in corso...
+
+
+ Accesso in lettura ai dettagli della chiave da APG.
+ Accesso in lettura ai dettagli delle chiavi pubbliche e private memorizzate in APG, compresi ID della chiave e ID dell\'utente. Non è possibile accedere in lettura alle chiavi in sè.
+
+
+ Critta
+ Decritta
+ Importa Chiavi Pubbliche
+ Importa Chiavi Private
+ Cerca Chiavi Pubbliche
+ Cerca Chiavi Private
+ Filtra: \"%s\"
+
+
+ veloce
+ lenta
+ molto lenta
+
+
+
+ er una migliore integrazione, si consiglia di installare K-9 Mail; questo applicativo supporta infatti APG e consente di crittare/decrittare direttamente le mail.
+\n\nPer poter utilizzare il bottone \"sfoglia\" per la selezione dei file su APG, si consiglia di installare OI File Manager oppure ASTRO.
+\n\nCome prima cosa, è necessario disporre di una o più chiavi. E\' possibile importare chiavi esistenti tramite l\'opzione \"Manage Public Keys\" e \"Manage Secret Keys\", oppure crearne una nuova tramite l\'opzione \"Manage Secret Keys\".
+\n\nE\' possibile aggiungere un account GMail tramite l\'opzione \"Aggiungi Acconto GMail\"; questo semplifica il processo di decrittaggio delle mail ricevute sulla propria casella di posta su GMail.
+\n\nProva e controlla le opzioni dei vari menù per esplorare e conoscere tutte le funzionalità di APG.
+
+
\ No newline at end of file
diff --git a/org_apg/res/values-no/strings.xml b/org_apg/res/values-no/strings.xml
new file mode 100644
index 000000000..825701f38
--- /dev/null
+++ b/org_apg/res/values-no/strings.xml
@@ -0,0 +1,313 @@
+
+
+
+
+
+
+ Epost innboks
+ Håndter Offentlige Nøkler
+ Håndter Private Nøkler
+ Velg Mottakere
+ SVelg Signatur
+ Krypter
+ Dekrypter
+ Passord
+ Lag Nøkkel
+ Rediger Nøkkel
+ Egenskaper
+ Nøkkelserver Egenskaper
+ Endre Passord
+ Sett Passord
+ "Send Epost..."
+ Krypter Til Fil
+ Dekrypter Til Fil
+ Legg Til Konto
+ Importer Nøkler
+ Eksporter Nøkkel
+ Eksporter Nøkler
+ Finner Ikke Nøkkel
+ Kom I Gang
+ Søk Nøkkelserver
+
+
+ Bruker IDer
+ Nøkler
+ Generelt
+ Standard Innstillinger
+ Avansert
+
+
+ Signer Til Utklippstavle
+ Krypter Til Utklippstavle
+ Krypter Og Send Epost
+ Signer Og Send Epost
+ Krypter
+ Signer
+ Dekrypter
+ Verifiser
+ Velg Mottakere
+ Svar
+ Krypter Melding
+ Dekrypter Melding
+ Krypter Fil
+ Dekrypter Fil
+ Lagre
+ Avbryt
+ Slett
+ Ingen
+ Tøm Filter
+ Endre Passord
+ Sett Passord
+ Søk
+
+
+ Om APG
+ Legg til GMail Konto
+ Slett Konto
+ Håndter Offentlige Nøkler
+ Håndter Private Nøkler
+ Instillinger
+ Importer Nøkler
+ Eksporter Nøkler
+ Eksporter Nøkkel
+ Slett Nøkkel
+ Opprett Nøkkel
+ Rediger Nøkkel
+ Søk
+ Hjelp
+ Nøkkelserver
+ Oppdater
+
+
+ Signer
+ Melding
+ Fil
+ Passord
+ Igjen
+ Algoritme
+ ASCII Armour
+ Offentlig(e) Nøkkel(/Nøkler)
+ Slett Etter Kryptering
+ Slett Etter Dekryptering
+ Slett Etter Import
+ Krypteringsalgoritme
+ Hash Algoritme
+ Offentlig Nøkkel
+ Passord
+ Passord Mellomlager
+ Melding komprimering
+ Fil Komprimering
+ Språk
+ Tving V3 Signaturer
+ Nøkkelserver
+ Nøkkel ID
+ Laget
+ Utløper
+ Bruk
+ Nøkkelstørrelse
+ Hoved Bruker ID
+ Navn
+ Kommentar
+ Epost
+ Velg
+ 1 Valgt
+ Valgt
+ <ukjent>
+ <ingen>
+ <ingen nøkkel>
+ -
+ <utløper ikke>
+
+ kan kryptere
+ kan signere
+ utløpt
+ ugyldig
+ %s nøkkelserver(e)
+
+
+ Ingen
+ Kun Signer
+ Kun Krypter
+ Signer Og Krypter
+ 15 sek
+ 1 min
+ 3 min
+ 5 min
+ 10 min
+ 20 min
+ 40 min
+ 1 time
+ 2 timer
+ 4 timer
+ 8 timer
+ til avsluttning
+ System
+ DSA
+ ElGamal
+ RSA
+ Åpne...
+ Lagre Som...
+ Velg Fil Som Skal Krypteres...
+ Velg Fil Som Skal Dekrypteres...
+ Åpne
+ Lagre
+ Advarsel
+ Feil
+ Advarsel: %s
+ Feil: %s
+
+
+ Feil passord.
+ Bruker innhold i utklippstavle.
+ Nøkkel lagret.
+ Sett en passord først.
+ Det er ikke installert en kompitabel filhåndtering.
+ Passordet matchet ikke.
+ Tomme passord er ikke lov.
+ Symmetrisk kryptering.
+ %s
+ Er du sikker på at du vil slette\n%s?
+ Filen er slettet.
+ Velg en fil først.
+ Dekryptering lykktes.
+ Krytering lyktes.
+ Kryptering til utklippstavle lykktes.
+ Tast passordet to ganger.
+ Velg minst én krypteringsnøkkel.
+ Velg minst én krypteringsnøkkel eller signaturnøkkel.
+ Vennligst angi hvilken fil det skal krypteres til.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
+ Vennligst angi hvilken fil det skal dekrypteres til.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
+ Angi hvilken Google Mail konto du vil legge til
+ Vennligst angi hvilken fil du vil importere fra. (.asc eller .gpg)
+ Vennligst angi hvilken fil du vil eksportere til.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
+ Vennligst angi hvilken fil du vil eksportere til.\nADVARSEL! Du er på vei til p eksportere HEMMELIGE nøkkler.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
+ Vil du virkelig slette nøkkelen? \'%s\'?\nDu kan ikke gjøre om dette!
+ Vil du virkelig slette den HEMMELIGE nøkkelen? \'%s\'?\nDu kan ikke gjøre om dette!
+ Lykktes å legge til %1$s nøkkel(er) og oppdatere %2$s nøkkel(er)."
+ Lykktes å legge til %s nøkkel(er).
+ Lykktes å oppdatere %s nøkkel(er).
+ Ingen nøkler lagt til eller oppdatert.
+ Lykktes å eksportere 1 nøkkel.
+ Lykktes å eksportere %s nøkler.
+ Ingen nøkler eksportert.
+ Noter: kun undernøkler støtter ElGamal, og for ElGamal vil den nærmeste nøkkelstørrelsen 1536, 2048, 3072, 4096, eller 8192 bli brukt.
+ Kunne ikke finne nøkkel %08X.
+ Fang %s nøkkel(er).
+ Ukjent signatur, trykk for å sjekke nøkkel.
+ Nøkkelredigering er enda beta.
+
+
+ sletting \'%s\' mislyktes
+ finner ikke fil
+ finner ingen passende hemmelig nøkkel
+ ingen kjente krypteringsmåter funnet
+ ekstern lagringsplass ikke klar
+ finner ikke kontoen - \'%s\'
+ ikke tillatelse til lese konto
+ kunne ikke legge til konto - \'%s\'
+ ugyldig epost \'%s\'
+ størrelsen på nøkkel må være minst 512bit
+ hovednøkkelen kan ikke være en ElGamal nøkkel
+ ukjent algoritme valgt
+ Du må spesifisere et navn
+ du må spesifisere en epost adresse
+ må ha minst en bruker id
+ hovedbruker id kan ikke være tom
+ trenger minst én hovednøkkel
+ utløpsdatoen må komme etter opprettelsesdatoen
+ ingen krypteringsnøkkel(er) eller passord gitt
+ signering feilet
+ ingen passord gitt
+ ingen signaturnøkkel gitt
+ ikke gyldig krypteringsdata
+ korrupt data
+ kunne ikke finne en pakke med symmetrisk kryptering
+ Feil passord
+ Feil under lagring av noen nøkkel(er)
+
+
+ ferdig.
+ initialiserer...
+ lagrer...
+ importerer...
+ eksporterer...
+ genererer nøkkel, dette kan ta en stund...
+ bygger nøkkel...
+ forbereder hovednøkkel...
+ berefter hovednøkkel...
+ bygger hovednøkkelring...
+ legger til undernøkler...
+ lagrer nøkkelring...
+ importerer hemmelige nøkler...
+ importerer offentlige nøkler...
+ gjennlaster nøkler...
+ eksporterer nøkkel...
+ eksporterer nøkler...
+ henter signaturnøkkel...
+ henter nøkkel...
+ forereder strømmer...
+ krypterer data...
+ dekrypterer data...
+ forbereder signatur...
+ genererer signatur...
+ behandler signatur...
+ bekrefter signatur...
+ signerer...
+ leser data...
+ finner nøkkel...
+ dekomprimerer data...
+ bekrefter integritet...
+ sletter \'%s\' sikkert...
+ søker...
+ søker %s...
+
+
+ Les nøkkel-detaljer fra APG.
+ Les nøkkel-detaljer fra APG om offentlige og private nøkler lagret i APG, som f.eks. nøkkel-ID og bruker-IDer. Nøklene i seg selv kan IKKE bli lest.
+
+
+ Krypter
+ Dekrypter
+ Importer Offentlige Nøkler
+ Importer Private Nøkler
+ Søk I Offentlige Nøkler
+ Søk I Private Nøkler
+ Filter: \"%s\"
+
+
+ rask
+ treg
+ veldig treg
+
+
+
+ Installer K-9 Mail for den beste integrasjonen, det støtter APG for PGP/INLINE og lar deg kryptere/dekryptere epost direkte.
+\n\nDet er anbefalt at du installerer OI File Manager eller ASTRO for å kunne velge filer igjennom APG, med manuelt søk.
+\n\nFørst trenger du noen nøkler. Du kan enten importere dem via menyene i \"Håndter Offentlige Nøkler\" og \"Håndter Private Nøkler\" eller lage dem selv i \"Håndter Private Nøkler\".
+\n\nDu kan også legge til en GMail-konto i hovedmenyen via \"Legg Til Konto\", noe som forenkler dekryptering av epost mottatt der.
+\n\nSjekk menyene under de forskjellige aktivitetene for å finne flere funksjoner.
+
+
\ No newline at end of file
diff --git a/org_apg/res/values-pt/strings.xml b/org_apg/res/values-pt/strings.xml
new file mode 100644
index 000000000..76e0cb244
--- /dev/null
+++ b/org_apg/res/values-pt/strings.xml
@@ -0,0 +1,312 @@
+
+
+
+
+
+
+ Caixa de Entrada
+ Gerir Chaves Públicas
+ Gerir Chaves Privadas
+ Escolher Destinatários
+ Escolher Assinatura
+ Cifrar
+ Decifrar
+ Senha
+ Criar Chave
+ Editar Chave
+ Preferências
+ Preferências de Servidor de Chaves
+ Alterar Senha
+ Definir Senha
+ "Enviar Email..."
+ Cifrar Para Arquivo
+ Descifrar Para Arquivo
+ Adicionar Conta
+ Importar Chaves
+ Exportar Chave
+ Exportar Chaves
+ Chave Não Encontrada
+ Conhecendo
+ Busca no Servidor de Chaves
+ Assinatura Desconhecida
+
+
+ IDs de Usuários
+ Chaves
+ Geral
+ Padrões
+ Avançado
+
+
+ Assinar Para Clipboard
+ Cifrar Para Clipboard
+ Cifrar Para Email
+ Assinar Para Email
+ Cifrar
+ Assinar
+ Decifrar
+ Verificar
+ Escolher Destinatários
+ Responder
+ Cifrar Mensagem
+ Decifrar Mesagem
+ Cifrar Arquivo
+ Decifrar Arquivo
+ Salvar
+ Cancelar
+ Apagar
+ Nenhuma
+ Limpar Filtro
+ Alterar Senha
+ Definir Senha
+ Buscar
+
+
+ Sobre
+ Adicionar Conta GMail
+ Apagar Conta
+ Gerir Chaves Públicas
+ Gerir Chaves Privadas
+ Configurações
+ Importar Chaves
+ Exportar Chaves
+ Exportar Chave
+ Apagar Chave
+ Criar Chave
+ Editar Chave
+ Buscar
+ Ajuda
+ Servidor de Chaves
+ Atualizar
+
+
+ Assinar
+ Mensagem
+ Arquivo
+ Senha
+ Novamente
+ Algoritmo
+ Armadura ASCII
+ Chave(s) Pública(s)
+ Apagar Após Cifrar
+ Apagar Após Decifrar
+ Apagar Após Importar
+ Algoritmo de Cifragem
+ Algoritmo de Hash
+ Chave Pública
+ Senha
+ Cache de Senhas
+ Compressão de Mensagem
+ Compressão de Arquivo
+ Língua
+ Forçar Assinaturas V3
+ Servidores de Chave
+ ID da Chave
+ Criação
+ Expiração
+ Uso
+ Tamanho da Chave
+ ID do Usuário Principal
+ Nome
+ Comentário
+ Email
+ Selecionar
+ 1 Selecionada
+ Selecionada
+ <desconhecido>
+ <ninguém>
+ <sem chave>
+ -
+ <não expira>
+
+ pode cifrar
+ pode assinar
+ expirada
+ não válida
+ %s servidor(es) de chave(s)
+ fingerprint
+
+
+ Nenhum
+ Assinar apenas
+ Cifrar apenas
+ Assinar e Cifrar
+ 15 segs
+ 1 min
+ 3 mins
+ 5 mins
+ 10 mins
+ 20 mins
+ 40 mins
+ 1 hora
+ 2 horas
+ 4 horas
+ 8 horas
+ até sair
+ Padrões do sistema
+ DSA
+ ElGamal
+ RSA
+ Abrir...
+ Salvar Como...
+ Selecionar Arquivo para Cifrar...
+ Selecionar Arquivo para Decifrar...
+ Abrir
+ Salvar
+ Advertência
+ Erro
+ Advertência: %s
+ Erro: %s
+
+
+ Chave errada.
+ Usando conteúdo do clipboard.
+ Chave salva.
+ Defina uma senha antes.
+ Não há gerenciador de arquivos compatível instalado.
+ A senha não confere.
+ Não são permitidas senhas vazias.
+ Cifragem simétrica.
+ %s
+ Tem certeza que deseja apagar\n%s?
+ Apagado com sucesso.
+ Selecione um arquivo primeiro.
+ Decifrado com sucesso.
+ Cifrado com sucesso.
+ Cifrado para clipboard com sucesso.
+ Insira a senha duas vezes.
+ Selecione pelo menos uma chave de cifragem.
+ Selecione pelo menos uma chave de cifragem ou uma chave de assinatura.
+ Especifique o arquivo a cifrar sobre.\nATENÇÃO! O arquivo, se existir, será sobrescrito!
+ Especifique o arquivo a decifrar sobre.\nATENÇÃO! O arquivo, se existir, será sobrescrito!
+ Especifique a conta de Email do Google que deseja adicionar.
+ Especifique o arquivo para importação de chaves. (.asc or .gpg)
+ Especifique o arquivo de exportação.\nATENÇÃO! O arquivo, se existir, será sobrescrito!
+ Especifique o arquivo de exportação.\nATENÇÃO! Você está exportando suas chaves PRIVADAS (SECRETAS).\nATENÇÃO! O arquivo, se existir, será sobrescrito.
+ Você realmente deseja apagar a chave \'%s\'?\nEssa ação não pode ser desfeita!
+ Você realmente deseja apagar a chave PRIVADA \'%s\'?\nEssa ação não pode ser desfeita!
+ Adicionada(s) com sucesso %1$s chave(s) e atualizada(s) %2$s chave(s).
+ Adicionada(s) %s chave(s) com sucesso.
+ Atualizada(s) %s chaves(s) com sucesso.
+ Nenhuma chave adicionada ou atualizada.
+ 1 chave exportada com sucesso.
+ %s chaves exportadas com sucesso.
+ Nenhuma chave exportada.
+ Nota: apenas as sub-chaves suportam ElGamal, e para ElGamal se usuára o tamanho mais próximo a 1536, 2048, 3072, 4096, ou 8192.
+ Não se pode encontrar a chave %08X.
+ %s chave(s) encontrada(s).
+ Assinatura desconhecida, toque para buscar a chave.
+ A edição de chaves ainda está na versão beta.
+ %s chave(s) secreta(s) ignoradas. Provavelmente foram exportadas com a opção\n --export-secret-subkeys\nCertifique-se de exporta com a opção\n --export-secret-keys
+ Chave %s desconhecida, você gostaria de procurá-la em um servidor de chaves?
+
+
+ erro ao apagar \'%s\'
+ arquivo não encontrado
+ não foi encontrado uma chave secreta adequada
+ não foi encontrado tipo de cifragem conhecido
+ armazenamento externo não disponível
+ conta \'%s\' não encontrada
+ sem permissões para ler a conta
+ falha ao adicionar a conta \'%s\'
+ email inválido \'%s\'
+ o tamanho da chave deve ser pelo menos 512bit
+ a chave primária não pode ser do tipo ElGamal
+ algoritmo selecionado desconhecido
+ é necessário informar um nome
+ é necessário informar um endereço de email
+ é necessário pelo menos um id de usuário
+ id de usuário principal não pode ser vazio
+ é necessário pelo menos uma chave primária
+ data de expiração deve ser após a data de criação
+ não foram inseridos chave(s) de cifragem ou senha
+ falha na assinatura
+ senha não inserida
+ chave para assinatura não inserida
+ dados a serem cifrados inválidos
+ dados corrompidos
+ não foi encontrado um pacote com criptografia simétrica
+ senha inválida
+ erro ao salvar chave(s)
+ não foi possível extrair chave privada
+
+
+ pronto.
+ inicializando...
+ salvando...
+ importando...
+ exportando...
+ criando chave, isso pode demorar um pouco...
+ criando chave...
+ preparando chave primária...
+ certificando chave primária...
+ criando anel de chave primária...
+ adicionando sub-chaves...
+ salvando anel de chaves...
+ importando chaves secretas...
+ importando chaves públicas...
+ recarregando chaves...
+ exportando chave...
+ exportando chaves...
+ extraindo chave de assinatura...
+ extraindo chave...
+ preparando fluxos...
+ cifrando dados...
+ decifrando dados...
+ preparando assinatura...
+ gerando assinatura...
+ processando assinatura...
+ verificando assinatura...
+ assinando...
+ carregando dados...
+ buscando chave...
+ descompactando dados...
+ verificando integridade...
+ apagando \'%s\' de forma segura...
+ buscando...
+ buscando %s...
+
+
+ Ler detalhes de chaves do APG.
+ Ler detalhes de chaves públicas e privadas salvas no APG, tais como ID da chave e ID do usuário. As chaves NÃO podem ser lidas.
+
+
+ Cifrar
+ Decifrar
+ Importar Chaves Públicas
+ Importar Chaves Privadas
+ Buscar Chaves Públicas
+ Buscar Chaves Privadas
+ Filter: \"%s\"
+
+
+ rápido
+ lento
+ muito lento
+
+
+
+ Instale o leitor de emails K-9 para uma melhor integração. Ele suporta o APG para PGP/INLINE e permite cifragem e decifragem de emails diretamente.
+\n\nÉ recomendado o uso dos Gerenciadores de Arquivos OI ou ASTRO para ser possível a utilização do botão de buscar arquivos no APG.
+\n\nSão necessárias chaves para o APG. Importe-as pela opção do menu \"Gerir Chaves Públicas\" ou \"Gerir Chaves Privadas\" ou crie-as no menu \"Gerir Chaves Privadas\".
+\n\nVocê também pode adicionar uma conta GMail pela opção \"Adicionar Conta\", isso simplifica a decifragem de emails.
+\n\nVerifique as opções de menus para conhecer as funcionalides disponíveis.
+
+
\ No newline at end of file
diff --git a/org_apg/res/values-sl/strings.xml b/org_apg/res/values-sl/strings.xml
new file mode 100644
index 000000000..576d6e56b
--- /dev/null
+++ b/org_apg/res/values-sl/strings.xml
@@ -0,0 +1,304 @@
+
+
+
+
+
+
+ Poštni nabiralnik
+ Upravljanje javnih ključev
+ Upravljanje zasebnih ključev
+ Izberi prejemnike
+ Izberi podpis
+ Šifriraj
+ Dešifriraj
+ Geslo
+ Ustvari ključ
+ Uredi ključ
+ Nastavitve
+ Nastavitve strežnikov s ključi
+ Spremeni geslo
+ Določi geslo
+ "Pošlji e-pošto..."
+ Šifriraj v datoteko
+ Dešifriraj v datoteko
+ Dodaj račun
+ Uvozi ključe
+ Izvozi ključ
+ Izvozi ključe
+ Ključ ni bil najden
+ Kako začeti
+ Iskanje po strežnikih s ključi
+
+
+ Uporabniške identitete
+ Ključi
+ Splošno
+ Privzete nastavitve
+ Napredno
+
+
+ Podpiši in kopiraj v odložišče
+ Šifriraj in kopiraj v odložišče
+ Šifriraj in pošlji
+ Podpiši in pošlji
+ Šifriraj
+ Podpiši
+ Dešifriraj
+ Overi
+ Izberi prejemnike
+ Odgovori
+ Šifriraj sporočilo
+ Dešifriraj sporočilo
+ Šifriraj datoteko
+ Dešifriraj datoteko
+ Shrani
+ Prekliči
+ Izbriši
+ Brez
+ Umakni filtriranje
+ Spremeni geslo
+ Določi geslo
+ Išči
+
+
+ O programu
+ Dodaj GMail račun
+ Izbriši račun
+ Upravljanje javnih ključev
+ Upravljanje zasebnih ključev
+ Nastavitve
+ Uvozi ključe
+ Izvozi ključe
+ Izvozi ključ
+ Izbriši ključ
+ Ustvari ključ
+ Uredi ključ
+ Išči
+ Pomoč
+ Strežnik s ključi
+ Posodobi
+
+
+ Podpiši
+ Sporočilo
+ Datoteka
+ Geslo
+ Ponovi
+ Algoritem
+ ASCII Armour
+ Javni ključ(i)
+ Po šifriranju izbriši
+ Po dešifriranju izbriši
+ Po uvozu izbriši
+ Šifrirni algoritem
+ Hash algoritem
+ Javni ključ
+ Geslo
+ Predpomnilnik gesel
+ Zgoščevanje sporočil
+ Zgoščevanje datotek
+ Jezik
+ Vsili podpis V3
+ Strežniki s ključi
+ Identiteta ključa
+ Izdelava
+ Pretek
+ Uporaba
+ Velikost ključa
+ Glavna uporabniška identiteta
+ Ime
+ Komentar
+ E-pošta
+ Izberi
+ 1 izbran
+ Izbrani
+ <nepoznan>
+ <brez>
+ <brez ključa>
+ -
+ <nikoli>
+
+ lahko šifrira
+ lahko podpiše
+ potečeno
+ neveljavno
+ %s strežnik(i) s ključi
+
+
+ Brez
+ Samo podpis
+ Samo šifriranje
+ Podpis in šifriranje
+ 15 sek
+ 1 min
+ 3 min
+ 5 min
+ 10 min
+ 20 min
+ 40 min
+ do izhoda
+ Sistemsko nastavljeno
+ DSA
+ ElGamal
+ RSA
+ Odpri...
+ Shrani kot...
+ Izberi datoteko za šifriranje...
+ Izberi datoteko za dešifriranje...
+ Odpri
+ Shrani
+ Opozorilo
+ Napaka
+ Opozorilo: %s
+ Napaka: %s
+
+
+ Napačno geslo.
+ Uporabljam vsebino odložišča.
+ Ključ shranjen.
+ Najprej določite geslo.
+ Nameščen ni noben združljiv upravitelj datotek.
+ Gesli se ne ujemata.
+ Prazna gesla niso dovoljena.
+ Simetrična enkripcija.
+ %s
+ Ali ste prepričani, da želite izbrisati\n%s?
+ Uspešno izbrisano.
+ Najprej izberite datoteko.
+ Uspešno dešifrirano.
+ Uspešno šifrirano.
+ Uspešno šifrirano v odložišče.
+ Vstavite geslo dvakrat.
+ Izberite vsaj en šifrirni ključ.
+ Izberite vsaj en šifrirni ključ ali ključ za podpis.
+ Določite datoteko v katero želite šifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
+ Določite datoteko v katero želite dešifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
+ Določite Google Mail račun, ki ga želite dodati.
+ Določite iz katere datoteke želite uvoziti ključe. (.asc ali .gpg)
+ Določite v katero datoteko želite izvoziti.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
+ Določite v katero datoteko želite izvoziti.\nPOZOR! Izvozili boste ZASEBNI ključ.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
+ Ali zares želite izbrisati ključ \'%s\'?\nTega ne boste mogli popraviti!
+ Ali zares želite izbrisati ZASEBNI ključ \'%s\'?\nTega ne boste mogli popraviti!
+ Uspešno dodani ključi: %1$s. Uspešno posodobljeni ključi: %2$s."
+ Uspešno dodani ključi: %s.
+ Uspešno posodobljeni ključi: %s.
+ Noben ključ ni bil dodan ali posodobljen.
+ Uspešno izvožen 1 ključ.
+ Uspešno izvoženi ključi: %s
+ Noben ključ ni bil izvožen.
+ Opomba: le podključi podpirajo ElGamal. Za ElGamal bo uporabljena velikost najbližja 1536, 2048, 3072, 4096, ali 8192.
+ Ne najdem ključa %08X.
+ Najdeni ključi: %s
+ Neznan podpis, za ogled pritisni.
+ Urejanje ključev je še vedno v precej testni fazi.
+
+
+ izbris \'%s\' ni uspel
+ ne najdem datoteke
+ najden ni bil noben ustrezen zasebni kluč
+ najdena ni bila nobena poznana vrsta enkripcije
+ zunanji pomnilnik ni pripravljen
+ račun \'%s\' ni najden
+ branje računa ni dovoljeno
+ dodajanje računa \'%s\' ni uspelo
+ neveljaven e-naslov \'%s\'
+ velikost ključa mora biti vsaj 512bit
+ statični ključ ne more biti ključ ElGamal
+ neznana izbira algoritma
+ določiti morate ime
+ določiti morate naslov e-pošte
+ potrebujem vsaj eno uporabniško identiteto
+ glavna uporabniška identiteta ne more biti prazna
+ potrebujem vsaj statični ključ
+ datum poteka mora biti kasnejši od datuma nastanka
+ dan ni bil noben šifrirni ključ ali geslo
+ podpis ni bil uspešen
+ dano ni bilo nobeno geslo
+ dan ni bil noben podpisni ključ
+ neveljavni šifrirni podatki
+ pokvarjeni podatki
+ ne najdem podatkov s simetrično enkripcijo
+ napačno geslo
+ napaka pri shranjevanju nakaterih ključev
+
+
+ končano.
+ inicializiram...
+ shranjujem...
+ uvažam...
+ izvažam...
+ generiram ključ, to lahko traja nekaj časa...
+ gradim ključ...
+ pripravljam statični ključ...
+ potrjujem statični ključ...
+ gradim datoteko s statičnimi ključi...
+ dodajam podključe...
+ shranjujem datoteko s ključi...
+ uvažam zasebne ključe...
+ uvažam javne ključe...
+ reloading keys...
+ izvažam ključ...
+ izvažam ključe...
+ izvlačim podpisni kluč...
+ izvlačim ključ...
+ pripravljam tok...
+ šifriram podatke...
+ dešifriram podatke...
+ pripravljam podpis...
+ generiram podpis...
+ obdelujem podpis...
+ overovljam podpis...
+ podpisujem...
+ berem podatke...
+ iščem ključ...
+ raztezam podatke...
+ overovljam integriteto...
+ varno brišem \'%s\'...
+ poizvedujem na %s...
+
+
+ Preverite podrobnosti ključev v APG.
+ Preverite podrobnosti javnih in zasebnih ključev shranjenih v APG, kot so identiteta ključev in identiteta uporabnikov. Samih ključev NI MOGOČE preveriti.
+
+
+ Šifriraj
+ Dešifriraj
+ Uvozi javne ključe
+ Uvozi zasebne ključe
+ Poišči javne ključe
+ Poišči zasebne ključe
+ Filter: \"%s\"
+
+
+ hitro
+ počasi
+ zelo počasi
+
+
+
+ Za boljšo integracijo namestite program K-9 Mail, ki omogoča način PGP/INLINE in neposredno šifriranje/dešifriranje e-pošte.
+ \n\nZaželjeno je, da namestite programa OI File Manager ali ASTRO, ki omogočata iskanje, izbiro in vnos datotek v APG.
+ \n\nZa začetek potrebujete nekaj ključev. Lahko jih uvozite s klikom na menija \"Upravljanje javnih ključev\" in \"Upravljanje zasebnih ključev\" ali jih ustvarite v meniju \"Upravljanje zasebnih ključev\".
+ \n\nPreko menija \"Dodaj račun\" lahko dodate vaše GMail račune in tako poenostavite dešifriranje e-pošte prejete nanje.
+ \n\nDa bi odkrili dodatne funkcije in zmožnosti programa APG, se sprehodite skozi njegove menije.
+
+
\ No newline at end of file
diff --git a/org_apg/res/values-zh/strings.xml b/org_apg/res/values-zh/strings.xml
new file mode 100644
index 000000000..8fa3d23fc
--- /dev/null
+++ b/org_apg/res/values-zh/strings.xml
@@ -0,0 +1,309 @@
+
+
+
+
+
+
+ 收件箱
+ 管理公钥
+ 管理私钥
+ 选择接收人
+ 选择签名
+ 加密
+ 解密
+ 密码口令
+ 创建密钥
+ 编辑密钥
+ 设置
+ 密钥服务器设置
+ 更改密码口令
+ 设置密码口令
+ \"发送邮件...\"
+ 加密至文件
+ 解密至文件
+ 添加账户
+ 导入密钥
+ 导出密钥
+ 导出密钥
+ 密钥未发现
+ 准备开始
+ 查询密钥服务器
+ 未知签名密钥
+
+
+ 用户标识
+ 密钥
+ 常规
+ 默认
+ 高级
+
+
+ 签名至剪贴板
+ 加密至剪贴板
+ 加密并发送邮件
+ 签名并发送邮件
+ 加密
+ 签名
+ 解密
+ 验证
+ 选择接收人
+ 回复
+ 加密信息
+ 解密信息
+ 加密文件
+ 解密文件
+ 保存
+ 取消
+ 删除
+ 无
+ 清空过滤器
+ 更改密码口令
+ 设置密码口令
+ 搜索
+
+
+ 关于
+ 添加GMail账户
+ 删除账户
+ 管理公钥
+ 管理私钥
+ 设置
+ 导入密钥
+ 导出密钥
+ 导出密钥
+ 删除密钥
+ 创建密钥
+ 编辑密钥
+ 搜索
+ 帮助
+ 密钥服务器
+ 更新密钥
+
+
+ 签名
+ 信息
+ 文件
+ 密码口令
+ 确认口令
+ 算法
+ 二进制转文本编码
+ 选择公钥
+ 加密后删除
+ 解密后删除
+ 导入后删除
+ 加密算法
+ 哈希算法
+ 非对称算法
+ 对称算法
+ 口令缓存
+ 信息压缩
+ 文件压缩
+ 语言
+ 强制V3签名
+ 密钥服务器
+ 密钥标识
+ 创建日期
+ 期限
+ 用途
+ 密钥尺寸
+ 主用户标识
+ 姓名
+ 注释
+ 电子邮件
+ 选择
+ 1个选定
+ 个选定
+ <未知>
+ <无>
+ <无密钥>
+ -
+ <不过期>
+
+ 可以加密
+ 可以签名
+ 已过期
+ 不合法
+ %s 个密钥服务器
+
+
+ 无
+ 仅签名
+ 仅加密
+ 签名且加密
+ 15 秒
+ 1 分钟
+ 3 分钟
+ 5 分钟
+ 10 分钟
+ 20 分钟
+ 40 分钟
+ 直到退出
+ 系统默认
+ DSA
+ ElGamal
+ RSA
+ 打开...
+ 另存为...
+ 选择要加密的文件...
+ 选择要解密的文件...
+ 打开
+ 保存
+ 警告
+ 错误
+ 警告: %s
+ 错误: %s
+
+
+ 密码口令错误.
+ 使用剪贴板内容.
+ 密钥已保存.
+ 请先设置密码口令.
+ 没有兼容的文件管理器被安装.
+ 密码口令不匹配.
+ 不允许空的密码口令.
+ 对称加密.
+ %s
+ 确定要删除\n%s?
+ 删除成功.
+ 请先选择文件.
+ 解密成功.
+ 加密成功.
+ 成功加密至剪贴板.
+ 输入密码口令两次.
+ 至少选择一个加密密钥.
+ 至少选择一个加密密钥或一个签名密钥.
+ 请指定加密生成的文件.\n警告! 文件如存在会被覆盖.
+ 请指定解密生成的文件.\n警告! 文件如存在会被覆盖.
+ 指定你想添加的Google Mail帐号.
+ 请指定导入密钥的文件. (.asc or .gpg)
+ 请指定导出文件.\n警告! 文件如存在会被覆盖.
+ 请指定导出文件.\n警告! 你正准备导出私钥.\n警告! 文件如存在会被覆盖.
+ 确认需要删除 \'%s\'?\n操作无法被恢复!
+ 确认需要删除私钥 \'%s\'?\n操作无法被恢复!
+ 成功添加 %1$s 个密钥和更新 %2$s 个密钥.
+ 成功添加 %s 个密钥.
+ 成功更新 %s 个密钥.
+ 无密钥被添加或更新.
+ 成功导出 1 个密钥.
+ 成功导出 %s 个密钥.
+ 无密钥导出.
+ 注意: 只有子密钥支持ElGamal, 最接近的密钥大小如1536, 2048, 3072, 4096, 8192会被其使用.
+ 无法找到密钥 %08X.
+ 发现 %s 个密钥.
+ 未知签名, 请查找密钥.
+ 密钥编辑仍处于测试阶段.
+ 忽略 %s 个错误的密钥. 或许导出时你使用了\n --export-secret-subkeys\n请确保使用\n --export-secret-keys\n代替前者.
+ 未知密钥 %s, 是否需要在密钥服务器上查找?
+
+
+ 删除 \'%s\' 失败
+ 文件没有找到
+ 未发现合适的私钥
+ 未知加密方式
+ 外部存储设备未准备就绪
+ 账户 \'%s\' 未发现
+ 无读取账户权限
+ 添加账户 \'%s\' 失败
+ 无效电子邮件 \'%s\'
+ 文件尺寸至少需512比特
+ 主密钥不可以是ElGamal
+ 选择了未知算法
+ 需指定姓名
+ 需指定电子邮件
+ 至少需要一个用户标识
+ 主用户标识不能为空
+ 至少需要选择一个主密钥
+ 期限日期必需在创建日期之后
+ 无加密密钥或密码口令被提供
+ 签名失败
+ 无密码口令被提供
+ 无签名密钥被提供
+ 加密数据不合法
+ 数据损坏
+ 未发现对称加密包
+ 密码口令错误
+ 保存密钥出错
+ 无法提取私钥
+
+
+ 完成.
+ 初始化中...
+ 保存中...
+ 导入中...
+ 导出中...
+ 正在创建密钥, 请等待...
+ 生成密钥中...
+ 正在准备主密钥...
+ 正在认证主密钥...
+ 正在生成主密钥圈...
+ 正在添加子密钥...
+ 正在保存密钥圈...
+ 导入私钥中...
+ 导入公钥中...
+ 重新加载密钥...
+ 导出密钥中...
+ 导出密钥中...
+ 提取签名密钥中...
+ 提取密钥中...
+ 准备数据流...
+ 加密数据中...
+ 解密数据中...
+ 正在准备签名...
+ 正在生成签名...
+ 处理签名中...
+ 验证签名中...
+ 正在签名...
+ 读取数据中...
+ 正在搜索密钥...
+ 解压数据中...
+ 正在验证集成...
+ 正在安全删除 \'%s\' ...
+ 正在查询...
+ 正在查询 %s...
+
+
+ 从APG读取密钥信息.
+ 读取保存在APG中的公钥和私钥信息, 比如密钥标识和用户标识. 这些密钥本身无法被读取.
+
+
+ 加密
+ 解密
+ 导入公钥
+ 导入私钥
+ 搜索公钥
+ 搜索私钥
+ 过滤信息: \"%s\"
+
+
+ 快
+ 慢
+ 非常慢
+
+
+
+ 装 K-9 Mail 以便达到最好的集成, K9支持APG处理PGP/INLINE并让你直接加密或解密邮件.
+\n\n建议安装 OI File Manager 或 ASTRO 以便在APG中浏览文件夹.
+\n\n首先你需要一些密钥. 可以通过菜单 \"管理公钥\" 和 \"管理私钥\" 来导入, 或者在菜单 \"管理私钥\" 中创建.
+\n\n你也可以通过菜单 \"添加账户\" 来添加GMail账户, 这样可以简化解密那些账户中邮件的过程.
+\n\n可以通过查看选项菜单寻找更多的功能.
+
+
\ No newline at end of file
diff --git a/org_apg/res/values/arrays.xml b/org_apg/res/values/arrays.xml
new file mode 100644
index 000000000..25a4e66df
--- /dev/null
+++ b/org_apg/res/values/arrays.xml
@@ -0,0 +1,214 @@
+
+
+
+
+
+
+ @string/choice_15secs
+ @string/choice_1min
+ @string/choice_3mins
+ @string/choice_5mins
+ @string/choice_10mins
+ @string/choice_20mins
+ @string/choice_40mins
+ @string/choice_1hour
+ @string/choice_2hours
+ @string/choice_4hours
+ @string/choice_8hours
+
+
+
+ 15
+ 60
+ 180
+ 300
+ 600
+ 1200
+ 2400
+ 3600
+ 7200
+ 14400
+ 28800
+
+
+
+ @string/choice_language_system
+ Afrikaans
+ Bahasa indonesia
+ Bahasa melayu
+ Bamanankan
+ Català
+ Cymraeg
+ Dansk
+ Deutsch
+ Dholuo
+ Eesti
+ English
+ Español
+ Fulfulde, Pulaar, Pular
+ Gaeilge
+ Galego
+ Hausa
+ Hrvatski
+ Kinyarwanda
+ Kirundi
+ Kiswahili
+ Latviešu
+ Lietuviškai
+ Magyar
+ Malti
+ Nederlands
+ Norsk bokmål
+ Pyccĸий
+ Română
+ Slovenčina
+ Slovenščina
+ Somali
+ Српски
+ Tiếng Việt
+ Tϋrkçe
+ Wolof
+ Yorùbá
+ Azərbaycan
+ Euskera
+ Français
+ isiXhosa
+ isiZulu
+ Italiano
+ O\'zbek
+ Polski
+ Português
+ Shqip
+ Suomi
+ Svenska
+ Íslenska
+ Čeština
+ Ɛʋɛ
+ Ελληνικά
+ Беларуская
+ Български
+ Кыргыз
+ Македонски
+ Українська
+ аҧсуа бызшәа
+ Қазақ
+ Հայերեն
+ עברית
+ اردو
+ العربية
+ فارسی
+ پښتو
+ हिंदी
+ తెలుగు
+ ಕನ್ನಡ
+ ภาษาไทย
+ አማርኛ
+ 中文
+ 日本語
+ 한국어
+
+
+
+
+ af
+ id
+ ms
+ bm
+ ca
+ cy
+ da
+ de
+ luo
+ et
+ en
+ es
+ ff
+ ga
+ gl
+ ha
+ hr
+ rw
+ rn
+ sw
+ lv
+ lt
+ hu
+ mt
+ nl
+ no
+ ru
+ ro
+ sk
+ sl
+ so
+ sr
+ vi
+ tr
+ wo
+ yo
+ az
+ eu
+ fr
+ xs
+ zu
+ it
+ uz
+ pl
+ pt
+ sq
+ fi
+ sv
+ is
+ cs
+ ee
+ el
+ be
+ bg
+ ky
+ mk
+ uk
+ ab
+ kk
+ hy
+ he
+ ur
+ ar
+ fa
+ ps
+ hi
+ te
+ kn
+ th
+ am
+ zh
+ ja
+ ko
+
+
+
+
+ da
+ de
+ en
+ es
+ it
+ no
+ pt
+ sl
+ zh
+
+
+
diff --git a/org_apg/res/values/static_strings.xml b/org_apg/res/values/static_strings.xml
new file mode 100644
index 000000000..c8a6a5026
--- /dev/null
+++ b/org_apg/res/values/static_strings.xml
@@ -0,0 +1,7 @@
+
+
+
+ APG+
+ https://github.com/dschuermann/apg
+
+
\ No newline at end of file
diff --git a/org_apg/res/values/strings.xml b/org_apg/res/values/strings.xml
new file mode 100644
index 000000000..4cfc99df8
--- /dev/null
+++ b/org_apg/res/values/strings.xml
@@ -0,0 +1,333 @@
+
+
+
+
+
+
+ Mail Inbox
+ Manage Public Keys
+ Manage Secret Keys
+ Select Recipients
+ Select Signature
+ Encrypt
+ Decrypt
+ Pass Phrase
+ Create Key
+ Edit Key
+ Preferences
+ Key Server Preference
+ Change Pass Phrase
+ Set Pass Phrase
+ "Send Mail..."
+ Encrypt To File
+ Decrypt To File
+ Add Account
+ Import Keys
+ Export Key
+ Export Keys
+ Key Not Found
+ Getting Started
+ Query Key Server
+ Export to Key Server
+ Unknown Signature Key
+ Import from QR Code
+ Sign Key
+ About
+
+
+ User IDs
+ Keys
+ General
+ Defaults
+ Advanced
+
+
+ Sign To Clipboard
+ Encrypt To Clipboard
+ Encrypt And Email
+ Sign And Email
+ Encrypt
+ Sign
+ Decrypt
+ Verify
+ Select Recipients
+ Reply
+ Encrypt Message
+ Decrypt Message
+ Encrypt File
+ Decrypt File
+ Save
+ Cancel
+ Delete
+ None
+ Clear Filter
+ Change Pass Phrase
+ Set Pass Phrase
+ Search
+ Export To Server
+
+
+ About
+ Add GMail Account
+ Delete Account
+ Manage Public Keys
+ Manage Secret Keys
+ Settings
+ Import Keys
+ Export Keys
+ Export Key
+ Delete Key
+ Create Key
+ Edit Key
+ Search
+ Help
+ Key Server
+ Update
+ Export To Server
+ Share
+ Scan QR Code
+ Sign Key
+
+
+ Sign
+ Message
+ File
+ Pass Phrase
+ Again
+ Algorithm
+ ASCII Armour
+ Public Key(s)
+ Delete After Encryption
+ Delete After Decryption
+ Delete After Import
+ Encryption Algorithm
+ Hash Algorithm
+ Public Key
+ Pass Phrase
+ Pass Phrase Cache
+ Message Compression
+ File Compression
+ Language
+ Force V3 Signatures
+ Key Servers
+ Key ID
+ Creation
+ Expiry
+ Usage
+ Key Size
+ Main User ID
+ Name
+ Comment
+ Email
+ Send Key to Server?
+ Select
+ 1 Selected
+ Selected
+ <unknown>
+ <none>
+ <no key>
+ -
+ <no expiry>
+
+ can encrypt
+ can sign
+ expired
+ not valid
+ %s key server(s)
+ fingerprint
+
+
+ None
+ Sign only
+ Encrypt only
+ Sign and Encrypt
+ 15 secs
+ 1 min
+ 3 mins
+ 5 mins
+ 10 mins
+ 20 mins
+ 40 mins
+ 1 hour
+ 2 hours
+ 4 hours
+ 8 hours
+ until quit
+ System default
+ DSA
+ ElGamal
+ RSA
+ Open...
+ Save As...
+ Select File To Encrypt...
+ Select File To Decrypt...
+ Open
+ Save
+ Warning
+ Error
+ Warning: %s
+ Error: %s
+
+
+ Wrong pass phrase.
+ Using clipboard content.
+ Key saved.
+ Set a pass phrase first.
+ No compatible file manager installed.
+ The pass phrases didn\'t match.
+ Empty pass phrases are not allowed.
+ Symmetric encryption.
+ %s
+ Are you sure you want to delete\n%s?
+ Successfully deleted.
+ Select a file first.
+ Successfully decrypted.
+ Successfully encrypted.
+ Successfully encrypted to clipboard.
+ Enter the pass phrase twice.
+ Select at least one encryption key.
+ Select at least one encryption key or a signature key.
+ Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.
+ Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.
+ Specify the Google Mail account you want to add.
+ Please specify which file to import keys from. (.asc or .gpg)
+ Please specify which file to export to.\nWARNING! File will be overwritten if it exists.
+ Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.
+ Do you really want to delete the key \'%s\'?\nYou can\'t undo this!
+ Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!
+ Successfully added %1$s key(s) and updated %2$s key(s).
+ Successfully added %s key(s).
+ Successfully updated %s key(s).
+ No keys added or updated.
+ Successfully exported 1 key.
+ Successfully exported %s keys.
+ No keys exported.
+ Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.
+ Couldn\'t find key %08X.
+ Found %s key(s).
+ Unknown signature, touch to look up key.
+ Key editing is still kind of beta.
+ %s bad secret key(s) ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead.
+ Unknown key %s, do you want to try finding it on a keyserver?
+ Successfully sent key to server
+ Successfully signed key
+ Successfully validated and imported key
+
+
+ deleting \'%s\' failed
+ file not found
+ no suitable secret key found
+ no known kind of encryption found
+ external storage not ready
+ account \'%s\' not found
+ no permission to read the account
+ adding account \'%s\' failed
+ invalid email \'%s\'
+ key size must be at least 512bit
+ the master key cannot be an ElGamal key
+ unknown algorithm choice
+ you need to specify a name
+ you need to specify an email address
+ need at least one user id
+ main user id must not be empty
+ need at least a master key
+ expiry date must come after creation date
+ no encryption key(s) or pass phrase given
+ signature failed
+ no pass phrase given
+ no signature key given
+ not valid encryption data
+ corrupt data
+ couldn\'t find a packet with symmetric encryption
+ wrong pass phrase
+ error saving some key(s)
+ could not extract private key
+
+
+ done.
+ initializing...
+ saving...
+ importing...
+ exporting...
+ generating key, this can take a while...
+ building key...
+ preparing master key...
+ certifying master key...
+ building master key ring...
+ adding sub keys...
+ saving key ring...
+ importing secret keys...
+ importing public keys...
+ reloading keys...
+ exporting key...
+ exporting keys...
+ extracting signature key...
+ extracting key...
+ preparing streams...
+ encrypting data...
+ decrypting data...
+ preparing signature...
+ generating signature...
+ processing signature...
+ verifying signature...
+ signing...
+ reading data...
+ finding key...
+ decompressing data...
+ verifying integrity...
+ deleting \'%s\' securely...
+ querying...
+ querying %s...
+
+
+ Read key details from APG.
+ Read key details of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.
+ Store blobs to en/decrypt with APG.
+ Store and read files on the android file system through APG. It cannot read files of other applications.
+
+
+ Encrypt
+ Decrypt
+ Import Public Keys
+ Import Secret Keys
+ Search Public Keys
+ Search Secret Keys
+ Filter: \"%s\"
+
+
+ fast
+ slow
+ very slow
+
+
+ Android Privacy Guard (APG) is a OpenPGP implementation for Android.
+ License: Apache License 2.0
+ Version:
+ Developer: Thialfihar (Main developer), Senecaso (QRCode, send key, sign key), Markus Doits (AIDL), Oliver Runge, Dominik Schürmann
+
+
+
+ Install K-9 Mail for the best integration, it supports APG for PGP/INLINE and lets you directly encrypt/decrypt emails.
+\n\nIt is recommended that you install OI File Manager or ASTRO to be able to use the browse button for file selection in APG.
+\n\nFirst you need some keys. Either import them via the option menus in \"Manage Public Keys\" and \"Manage Secret Keys\" or create them in \"Manage Secret Keys\".
+\n\nYou can also add a GMail account in the main activity via \"Add Account\", which simplifies decrypting emails received there.
+\n\nCheck out the option menus in the various activities to find more functions.
+
+
\ No newline at end of file
diff --git a/org_apg/res/values/styles.xml b/org_apg/res/values/styles.xml
new file mode 100644
index 000000000..2a2a69341
--- /dev/null
+++ b/org_apg/res/values/styles.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/res/xml/apg_preferences.xml b/org_apg/res/xml/apg_preferences.xml
new file mode 100644
index 000000000..7c0dd2206
--- /dev/null
+++ b/org_apg/res/xml/apg_preferences.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/res/xml/searchable_public_keys.xml b/org_apg/res/xml/searchable_public_keys.xml
new file mode 100644
index 000000000..e9602b121
--- /dev/null
+++ b/org_apg/res/xml/searchable_public_keys.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/res/xml/searchable_secret_keys.xml b/org_apg/res/xml/searchable_secret_keys.xml
new file mode 100644
index 000000000..a7e8873d6
--- /dev/null
+++ b/org_apg/res/xml/searchable_secret_keys.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org_apg/src/org/apg/Apg.java b/org_apg/src/org/apg/Apg.java
new file mode 100644
index 000000000..2bfdb31e6
--- /dev/null
+++ b/org_apg/src/org/apg/Apg.java
@@ -0,0 +1,2283 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg;
+
+import org.apg.KeyServer.AddKeyException;
+import org.apg.provider.DataProvider;
+import org.apg.provider.Database;
+import org.apg.provider.KeyRings;
+import org.apg.provider.Keys;
+import org.apg.provider.UserIds;
+import org.apg.ui.BaseActivity;
+import org.apg.ui.widget.KeyEditor;
+import org.apg.ui.widget.SectionView;
+import org.apg.ui.widget.UserIdEditor;
+import org.apg.util.IterableIterator;
+import org.spongycastle.bcpg.ArmoredInputStream;
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.bcpg.BCPGOutputStream;
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.jce.spec.ElGamalParameterSpec;
+import org.spongycastle.openpgp.PGPCompressedData;
+import org.spongycastle.openpgp.PGPCompressedDataGenerator;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
+import org.spongycastle.openpgp.PGPEncryptedDataList;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyPair;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPKeyRingGenerator;
+import org.spongycastle.openpgp.PGPLiteralData;
+import org.spongycastle.openpgp.PGPLiteralDataGenerator;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPOnePassSignature;
+import org.spongycastle.openpgp.PGPOnePassSignatureList;
+import org.spongycastle.openpgp.PGPPBEEncryptedData;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.PGPV3SignatureGenerator;
+import org.apg.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Message;
+import android.view.ViewGroup;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.math.BigInteger;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.SignatureException;
+import java.util.Calendar;
+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;
+
+public class Apg {
+ private static final String mApgPackageName = "org.thialfihar.android.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 final String DECRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN";
+ public static final String ENCRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN";
+ public static final String SELECT_PUBLIC_KEYS = "org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS";
+ public static final String SELECT_SECRET_KEY = "org.thialfihar.android.apg.intent.SELECT_SECRET_KEY";
+ public static final String IMPORT = "org.thialfihar.android.apg.intent.IMPORT";
+ public static final String LOOK_UP_KEY_ID = "org.thialfihar.android.apg.intent.LOOK_UP_KEY_ID";
+ public static final String LOOK_UP_KEY_ID_AND_RETURN = "org.thialfihar.android.apg.intent.LOOK_UP_KEY_ID_AND_RETURN";
+ public static final String GENERATE_SIGNATURE = "org.thialfihar.android.apg.intent.GENERATE_SIGNATURE";
+ public static final String EXPORT_KEY_TO_SERVER = "org.thialfihar.android.apg.intent.EXPORT_KEY_TO_SERVER";
+ public static final String IMPORT_FROM_QR_CODE = "org.thialfihar.android.apg.intent.IMPORT_FROM_QR_CODE";
+ }
+
+ public static final String EXTRA_TEXT = "text";
+ public static final String EXTRA_DATA = "data";
+ public static final String EXTRA_ERROR = "error";
+ public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
+ public static final String EXTRA_DECRYPTED_DATA = "decryptedData";
+ public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage";
+ public static final String EXTRA_ENCRYPTED_DATA = "encryptedData";
+ public static final String EXTRA_RESULT_URI = "resultUri";
+ public static final String EXTRA_SIGNATURE = "signature";
+ public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
+ public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
+ public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
+ public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
+ public static final String EXTRA_SIGNATURE_DATA = "signatureData";
+ public static final String EXTRA_SIGNATURE_TEXT = "signatureText";
+ public static final String EXTRA_USER_ID = "userId";
+ public static final String EXTRA_USER_IDS = "userIds";
+ public static final String EXTRA_KEY_ID = "keyId";
+ public static final String EXTRA_REPLY_TO = "replyTo";
+ public static final String EXTRA_SEND_TO = "sendTo";
+ public static final String EXTRA_SUBJECT = "subject";
+ public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
+ public static final String EXTRA_SELECTION = "selection";
+ public static final String EXTRA_ASCII_ARMOUR = "asciiArmour";
+ public static final String EXTRA_BINARY = "binary";
+ public static final String EXTRA_KEY_SERVERS = "keyServers";
+ public static final String EXTRA_EXPECTED_FINGERPRINT = "org.thialfihar.android.apg.EXPECTED_FINGERPRINT";
+
+ public static final String AUTHORITY = DataProvider.AUTHORITY;
+
+ public static final Uri CONTENT_URI_SECRET_KEY_RINGS = Uri.parse("content://" + AUTHORITY
+ + "/key_rings/secret/");
+ public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID = Uri.parse("content://"
+ + AUTHORITY + "/key_rings/secret/key_id/");
+ public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_EMAILS = Uri.parse("content://"
+ + AUTHORITY + "/key_rings/secret/emails/");
+
+ public static final Uri CONTENT_URI_PUBLIC_KEY_RINGS = Uri.parse("content://" + AUTHORITY
+ + "/key_rings/public/");
+ public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID = Uri.parse("content://"
+ + AUTHORITY + "/key_rings/public/key_id/");
+ public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS = Uri.parse("content://"
+ + AUTHORITY + "/key_rings/public/emails/");
+
+ private static String VERSION = null;
+
+ private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] {
+ SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
+ SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
+ SymmetricKeyAlgorithmTags.TRIPLE_DES };
+ private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1,
+ HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 };
+ private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] {
+ CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
+ CompressionAlgorithmTags.ZIP };
+
+ public static Pattern PGP_MESSAGE = Pattern.compile(
+ ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
+
+ public static Pattern PGP_SIGNED_MESSAGE = Pattern
+ .compile(
+ ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
+ Pattern.DOTALL);
+
+ public static Pattern PGP_PUBLIC_KEY = Pattern.compile(
+ ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
+ Pattern.DOTALL);
+
+ private static HashMap mPassPhraseCache = new HashMap();
+ private static String mEditPassPhrase = null;
+
+ private static Database mDatabase = null;
+
+ public static class GeneralException extends Exception {
+ static final long serialVersionUID = 0xf812773342L;
+
+ public GeneralException(String message) {
+ super(message);
+ }
+ }
+
+ public static class NoAsymmetricEncryptionException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public NoAsymmetricEncryptionException() {
+ super();
+ }
+ }
+
+ public static void initialize(Context context) {
+ if (mDatabase == null) {
+ mDatabase = new Database(context);
+ }
+ }
+
+ public static Database getDatabase() {
+ return mDatabase;
+ }
+
+ public static void setEditPassPhrase(String passPhrase) {
+ mEditPassPhrase = passPhrase;
+ }
+
+ public static String getEditPassPhrase() {
+ return mEditPassPhrase;
+ }
+
+ public static void setCachedPassPhrase(long keyId, String passPhrase) {
+ mPassPhraseCache.put(keyId, new CachedPassPhrase(new Date().getTime(), passPhrase));
+ }
+
+ public static String getCachedPassPhrase(long keyId) {
+ long realId = keyId;
+ if (realId != Id.key.symmetric) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(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 int cleanUpCache(int ttl, int initialDelay) {
+ int delay = initialDelay;
+ long realTtl = ttl * 1000;
+ long now = new Date().getTime();
+ Vector oldKeys = new Vector();
+ for (Map.Entry pair : mPassPhraseCache.entrySet()) {
+ long lived = now - pair.getValue().timestamp;
+ if (lived >= realTtl) {
+ oldKeys.add(pair.getKey());
+ } else {
+ // see, whether the remaining time for this cache entry improves our
+ // check delay
+ long nextCheck = realTtl - lived + 1000;
+ if (nextCheck < delay) {
+ delay = (int) nextCheck;
+ }
+ }
+ }
+
+ for (long keyId : oldKeys) {
+ mPassPhraseCache.remove(keyId);
+ }
+
+ return delay;
+ }
+
+ public static PGPSecretKey createKey(Context context, int algorithmChoice, int keySize,
+ String passPhrase, PGPSecretKey masterKey) throws NoSuchAlgorithmException,
+ PGPException, NoSuchProviderException, GeneralException,
+ InvalidAlgorithmParameterException {
+
+ if (keySize < 512) {
+ throw new GeneralException(context.getString(R.string.error_keySizeMinimum512bit));
+ }
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ if (passPhrase == null) {
+ passPhrase = "";
+ }
+
+ int algorithm = 0;
+ KeyPairGenerator keyGen = null;
+
+ switch (algorithmChoice) {
+ case Id.choice.algorithm.dsa: {
+ keyGen = KeyPairGenerator.getInstance("DSA", new BouncyCastleProvider());
+ keyGen.initialize(keySize, new SecureRandom());
+ algorithm = PGPPublicKey.DSA;
+ break;
+ }
+
+ 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 = Primes.getBestPrime(keySize);
+ BigInteger g = new BigInteger("2");
+
+ ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
+
+ keyGen.initialize(elParams);
+ algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
+ break;
+ }
+
+ case Id.choice.algorithm.rsa: {
+ keyGen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
+ keyGen.initialize(keySize, new SecureRandom());
+
+ algorithm = PGPPublicKey.RSA_GENERAL;
+ break;
+ }
+
+ default: {
+ throw new GeneralException(context.getString(R.string.error_unknownAlgorithmChoice));
+ }
+ }
+
+ PGPKeyPair keyPair = new PGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
+
+ 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 = it.next();
+ }
+
+ return secretKey;
+ }
+
+ private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
+ GregorianCalendar tmp = new GregorianCalendar();
+ tmp.setTime(first.getTime());
+ long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
+ tmp.add(Calendar.DAY_OF_MONTH, (int) numDays);
+ while (tmp.before(second)) {
+ tmp.add(Calendar.DAY_OF_MONTH, 1);
+ ++numDays;
+ }
+ return numDays;
+ }
+
+ public static void buildSecretKey(Activity context, SectionView userIdsView,
+ SectionView keysView, String oldPassPhrase, String newPassPhrase,
+ ProgressDialogUpdater progress) throws Apg.GeneralException, NoSuchProviderException,
+ PGPException, NoSuchAlgorithmException, SignatureException, IOException,
+ Database.GeneralException {
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_buildingKey, 0, 100);
+
+ Security.addProvider(new BouncyCastleProvider());
+
+ if (oldPassPhrase == null || oldPassPhrase.equals("")) {
+ oldPassPhrase = "";
+ }
+
+ if (newPassPhrase == null || newPassPhrase.equals("")) {
+ newPassPhrase = "";
+ }
+
+ Vector userIds = new Vector();
+ Vector keys = new Vector();
+
+ ViewGroup userIdEditors = userIdsView.getEditors();
+ ViewGroup keyEditors = keysView.getEditors();
+
+ boolean gotMainUserId = false;
+ for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
+ String userId = null;
+ try {
+ userId = editor.getValue();
+ } catch (UserIdEditor.NoNameException e) {
+ throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAName));
+ } catch (UserIdEditor.NoEmailException e) {
+ throw new Apg.GeneralException(
+ context.getString(R.string.error_userIdNeedsAnEmailAddress));
+ } catch (UserIdEditor.InvalidEmailException e) {
+ throw new Apg.GeneralException("" + e);
+ }
+
+ if (userId.equals("")) {
+ continue;
+ }
+
+ if (editor.isMainUserId()) {
+ userIds.insertElementAt(userId, 0);
+ gotMainUserId = true;
+ } else {
+ userIds.add(userId);
+ }
+ }
+
+ if (userIds.size() == 0) {
+ throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsAUserId));
+ }
+
+ if (!gotMainUserId) {
+ throw new Apg.GeneralException(
+ context.getString(R.string.error_mainUserIdMustNotBeEmpty));
+ }
+
+ if (keyEditors.getChildCount() == 0) {
+ throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsMasterKey));
+ }
+
+ for (int i = 0; i < keyEditors.getChildCount(); ++i) {
+ KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
+ keys.add(editor.getValue());
+ }
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingMasterKey, 10, 100);
+ KeyEditor keyEditor = (KeyEditor) keyEditors.getChildAt(0);
+ 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);
+
+ PGPSecretKey masterKey = keys.get(0);
+ PGPPublicKey tmpKey = masterKey.getPublicKey();
+ PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(),
+ tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime());
+ PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(oldPassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_certifyingMasterKey, 20, 100);
+ for (int i = 0; i < userIds.size(); ++i) {
+ String userId = userIds.get(i);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(masterPublicKey.getAlgorithm(),
+ HashAlgorithmTags.SHA1, new BouncyCastleProvider());
+
+ sGen.initSign(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
+ }
+
+ // TODO: cross-certify the master key with every sub key
+
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
+ if (canEncrypt) {
+ keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ }
+ hashedPacketsGen.setKeyFlags(true, keyFlags);
+
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+
+ // TODO: this doesn't work quite right yet
+ if (keyEditor.getExpiryDate() != null) {
+ GregorianCalendar creationDate = new GregorianCalendar();
+ creationDate.setTime(getCreationDate(masterKey));
+ GregorianCalendar expiryDate = keyEditor.getExpiryDate();
+ long numDays = getNumDaysBetween(creationDate, expiryDate);
+ if (numDays <= 0) {
+ throw new GeneralException(
+ context.getString(R.string.error_expiryMustComeAfterCreation));
+ }
+ hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
+ }
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_buildingMasterKeyRing, 30, 100);
+ PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, mainUserId, PGPEncryptedData.CAST5, newPassPhrase.toCharArray(),
+ hashedPacketsGen.generate(), unhashedPacketsGen.generate(), new SecureRandom(),
+ new BouncyCastleProvider().getName());
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_addingSubKeys, 40, 100);
+ for (int i = 1; i < keys.size(); ++i) {
+ if (progress != null)
+ progress.setProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100);
+ PGPSecretKey subKey = keys.get(i);
+ keyEditor = (KeyEditor) keyEditors.getChildAt(i);
+ PGPPublicKey subPublicKey = subKey.getPublicKey();
+ PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(oldPassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey.getAlgorithm(),
+ subPublicKey.getKey(new BouncyCastleProvider()), subPrivateKey.getKey(),
+ subPublicKey.getCreationTime());
+
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ keyFlags = 0;
+ 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;
+ }
+ if (canEncrypt) {
+ keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ }
+ hashedPacketsGen.setKeyFlags(true, keyFlags);
+
+ // TODO: this doesn't work quite right yet
+ if (keyEditor.getExpiryDate() != null) {
+ GregorianCalendar creationDate = new GregorianCalendar();
+ creationDate.setTime(getCreationDate(masterKey));
+ GregorianCalendar expiryDate = keyEditor.getExpiryDate();
+ long numDays = getNumDaysBetween(creationDate, expiryDate);
+ if (numDays <= 0) {
+ throw new GeneralException(
+ context.getString(R.string.error_expiryMustComeAfterCreation));
+ }
+ hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
+ }
+
+ keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate());
+ }
+
+ PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
+ PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
+ mDatabase.saveKeyRing(secretKeyRing);
+ mDatabase.saveKeyRing(publicKeyRing);
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+ }
+
+ public static PGPKeyRing decodeKeyRing(InputStream is) throws IOException {
+ InputStream in = PGPUtil.getDecoderStream(is);
+ PGPObjectFactory objectFactory = new PGPObjectFactory(in);
+ Object obj = objectFactory.nextObject();
+
+ if (obj instanceof PGPKeyRing) {
+ return (PGPKeyRing) obj;
+ }
+
+ return null;
+ }
+
+ public static int storeKeyRingInCache(PGPKeyRing keyring) {
+ int status = Integer.MIN_VALUE; // out of bounds value (Id.retrun_value.*)
+ try {
+ if (keyring instanceof PGPSecretKeyRing) {
+ PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
+ boolean save = true;
+ try {
+ PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey(
+ new char[] {}, new BouncyCastleProvider());
+ if (testKey == null) {
+ // this is bad, something is very wrong... likely a --export-secret-subkeys
+ // export
+ save = false;
+ status = Id.return_value.bad;
+ }
+ } catch (PGPException e) {
+ // all good if this fails, we likely didn't use the right password
+ }
+
+ if (save) {
+ status = mDatabase.saveKeyRing(secretKeyRing);
+ }
+ } else if (keyring instanceof PGPPublicKeyRing) {
+ PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
+ status = mDatabase.saveKeyRing(publicKeyRing);
+ }
+ } catch (IOException e) {
+ status = Id.return_value.error;
+ } catch (Database.GeneralException e) {
+ status = Id.return_value.error;
+ }
+
+ return status;
+ }
+
+ public static boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ArmoredOutputStream aos = new ArmoredOutputStream(bos);
+ try {
+ aos.write(keyring.getEncoded());
+ aos.close();
+
+ String armouredKey = bos.toString("UTF-8");
+ server.add(armouredKey);
+
+ return true;
+ } catch (IOException e) {
+ return false;
+ } catch (AddKeyException e) {
+ // TODO: tell the user?
+ return false;
+ } finally {
+ try {
+ bos.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ public static Bundle importKeyRings(Activity context, int type, InputData data,
+ ProgressDialogUpdater progress) throws GeneralException, FileNotFoundException,
+ PGPException, IOException {
+ Bundle returnData = new Bundle();
+
+ if (type == Id.type.secret_key) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
+ } else {
+ if (progress != null)
+ progress.setProgress(R.string.progress_importingPublicKeys, 0, 100);
+ }
+
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
+ }
+
+ PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream());
+ // need to have access to the bufferedInput, so we can reuse it for the possible
+ // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
+ // armour blocks
+ BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
+ int newKeys = 0;
+ int oldKeys = 0;
+ int badKeys = 0;
+ try {
+ PGPKeyRing keyring = decodeKeyRing(bufferedInput);
+ while (keyring != null) {
+ int status = Integer.MIN_VALUE; // out of bounds value
+
+ // if this key is what we expect it to be, save it
+ if ((type == Id.type.secret_key && keyring instanceof PGPSecretKeyRing)
+ || (type == Id.type.public_key && keyring instanceof PGPPublicKeyRing)) {
+ status = storeKeyRingInCache(keyring);
+ }
+
+ if (status == Id.return_value.error) {
+ throw new GeneralException(context.getString(R.string.error_savingKeys));
+ }
+
+ // update the counts to display to the user at the end
+ if (status == Id.return_value.updated) {
+ ++oldKeys;
+ } else if (status == Id.return_value.ok) {
+ ++newKeys;
+ } else if (status == Id.return_value.bad) {
+ ++badKeys;
+ }
+
+ if (progress != null) {
+ progress.setProgress((int) (100 * progressIn.position() / data.getSize()), 100);
+ }
+ // TODO: needed?
+ // obj = objectFactory.nextObject();
+
+ keyring = decodeKeyRing(bufferedInput);
+ }
+ } catch (EOFException e) {
+ // nothing to do, we are done
+ }
+
+ returnData.putInt("added", newKeys);
+ returnData.putInt("updated", oldKeys);
+ returnData.putInt("bad", badKeys);
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+
+ return returnData;
+ }
+
+ public static Bundle exportKeyRings(Activity context, Vector keyRingIds,
+ OutputStream outStream, ProgressDialogUpdater progress) throws GeneralException,
+ FileNotFoundException, PGPException, IOException {
+ Bundle returnData = new Bundle();
+
+ if (keyRingIds.size() == 1) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_exportingKey, 0, 100);
+ } else {
+ if (progress != null)
+ progress.setProgress(R.string.progress_exportingKeys, 0, 100);
+ }
+
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
+ }
+ ArmoredOutputStream out = new ArmoredOutputStream(outStream);
+
+ int numKeys = 0;
+ for (int i = 0; i < keyRingIds.size(); ++i) {
+ if (progress != null)
+ progress.setProgress(i * 100 / keyRingIds.size(), 100);
+ Object obj = mDatabase.getKeyRing(keyRingIds.get(i));
+ PGPPublicKeyRing publicKeyRing;
+ PGPSecretKeyRing secretKeyRing;
+
+ if (obj instanceof PGPSecretKeyRing) {
+ secretKeyRing = (PGPSecretKeyRing) obj;
+ secretKeyRing.encode(out);
+ } else if (obj instanceof PGPPublicKeyRing) {
+ publicKeyRing = (PGPPublicKeyRing) obj;
+ publicKeyRing.encode(out);
+ } else {
+ continue;
+ }
+ ++numKeys;
+ }
+ out.close();
+ returnData.putInt("exported", numKeys);
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+
+ return returnData;
+ }
+
+ public static Date getCreationDate(PGPPublicKey key) {
+ return key.getCreationTime();
+ }
+
+ public static Date getCreationDate(PGPSecretKey key) {
+ return key.getPublicKey().getCreationTime();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
+ for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) {
+ if (key.isMasterKey()) {
+ return key;
+ }
+ }
+
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
+ if (keyRing == null) {
+ return null;
+ }
+ for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) {
+ if (key.isMasterKey()) {
+ return key;
+ }
+ }
+
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Vector getEncryptKeys(PGPPublicKeyRing keyRing) {
+ Vector encryptKeys = new Vector();
+
+ for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) {
+ if (isEncryptionKey(key)) {
+ encryptKeys.add(key);
+ }
+ }
+
+ return encryptKeys;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static Vector getSigningKeys(PGPSecretKeyRing keyRing) {
+ Vector signingKeys = new Vector();
+
+ for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) {
+ if (isSigningKey(key)) {
+ signingKeys.add(key);
+ }
+ }
+
+ return signingKeys;
+ }
+
+ public static Vector getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
+ Vector usableKeys = new Vector();
+ Vector encryptKeys = getEncryptKeys(keyRing);
+ PGPPublicKey masterKey = null;
+ for (int i = 0; i < encryptKeys.size(); ++i) {
+ PGPPublicKey key = encryptKeys.get(i);
+ if (!isExpired(key)) {
+ if (key.isMasterKey()) {
+ masterKey = key;
+ } else {
+ usableKeys.add(key);
+ }
+ }
+ }
+ if (masterKey != null) {
+ usableKeys.add(masterKey);
+ }
+ return usableKeys;
+ }
+
+ public static boolean isExpired(PGPPublicKey key) {
+ Date creationDate = getCreationDate(key);
+ Date expiryDate = getExpiryDate(key);
+ Date now = new Date();
+ if (now.compareTo(creationDate) >= 0
+ && (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean isExpired(PGPSecretKey key) {
+ return isExpired(key.getPublicKey());
+ }
+
+ public static Vector getUsableSigningKeys(PGPSecretKeyRing keyRing) {
+ Vector usableKeys = new Vector();
+ Vector signingKeys = getSigningKeys(keyRing);
+ PGPSecretKey masterKey = null;
+ for (int i = 0; i < signingKeys.size(); ++i) {
+ PGPSecretKey key = signingKeys.get(i);
+ if (key.isMasterKey()) {
+ masterKey = key;
+ } else {
+ usableKeys.add(key);
+ }
+ }
+ if (masterKey != null) {
+ usableKeys.add(masterKey);
+ }
+ return usableKeys;
+ }
+
+ public static Date getExpiryDate(PGPPublicKey key) {
+ Date creationDate = getCreationDate(key);
+ if (key.getValidDays() == 0) {
+ // no expiry
+ return null;
+ }
+ Calendar calendar = GregorianCalendar.getInstance();
+ calendar.setTime(creationDate);
+ calendar.add(Calendar.DATE, key.getValidDays());
+ Date expiryDate = calendar.getTime();
+
+ return expiryDate;
+ }
+
+ public static Date getExpiryDate(PGPSecretKey key) {
+ return getExpiryDate(key.getPublicKey());
+ }
+
+ public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
+ PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
+ if (keyRing == null) {
+ return null;
+ }
+ Vector encryptKeys = getUsableEncryptKeys(keyRing);
+ if (encryptKeys.size() == 0) {
+ return null;
+ }
+ return encryptKeys.get(0);
+ }
+
+ public static PGPSecretKey getSigningKey(long masterKeyId) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
+ if (keyRing == null) {
+ return null;
+ }
+ Vector signingKeys = getUsableSigningKeys(keyRing);
+ if (signingKeys.size() == 0) {
+ return null;
+ }
+ return signingKeys.get(0);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static String getMainUserId(PGPPublicKey key) {
+ for (String userId : new IterableIterator(key.getUserIDs())) {
+ return userId;
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static String getMainUserId(PGPSecretKey key) {
+ for (String userId : new IterableIterator(key.getUserIDs())) {
+ return userId;
+ }
+ return null;
+ }
+
+ public static String getMainUserIdSafe(Context context, PGPPublicKey key) {
+ String userId = getMainUserId(key);
+ if (userId == null) {
+ userId = context.getResources().getString(R.string.unknownUserId);
+ }
+ return userId;
+ }
+
+ public static String getMainUserIdSafe(Context context, PGPSecretKey key) {
+ String userId = getMainUserId(key);
+ if (userId == null) {
+ userId = context.getResources().getString(R.string.unknownUserId);
+ }
+ return userId;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static boolean isEncryptionKey(PGPPublicKey key) {
+ if (!key.isEncryptionKey()) {
+ return false;
+ }
+
+ if (key.getVersion() <= 3) {
+ // this must be true now
+ return key.isEncryptionKey();
+ }
+
+ // special cases
+ if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
+ return true;
+ }
+
+ if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ 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;
+ }
+
+ public static boolean isEncryptionKey(PGPSecretKey key) {
+ return isEncryptionKey(key.getPublicKey());
+ }
+
+ @SuppressWarnings("unchecked")
+ public static boolean isSigningKey(PGPPublicKey key) {
+ if (key.getVersion() <= 3) {
+ return true;
+ }
+
+ // special case
+ if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ 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;
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean isSigningKey(PGPSecretKey key) {
+ return isSigningKey(key.getPublicKey());
+ }
+
+ public static String getAlgorithmInfo(PGPPublicKey key) {
+ return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength());
+ }
+
+ public static String getAlgorithmInfo(PGPSecretKey key) {
+ return getAlgorithmInfo(key.getPublicKey());
+ }
+
+ public static String getAlgorithmInfo(int algorithm, int keySize) {
+ String algorithmStr = null;
+
+ switch (algorithm) {
+ case PGPPublicKey.RSA_ENCRYPT:
+ case PGPPublicKey.RSA_GENERAL:
+ case PGPPublicKey.RSA_SIGN: {
+ algorithmStr = "RSA";
+ break;
+ }
+
+ case PGPPublicKey.DSA: {
+ algorithmStr = "DSA";
+ break;
+ }
+
+ case PGPPublicKey.ELGAMAL_ENCRYPT:
+ case PGPPublicKey.ELGAMAL_GENERAL: {
+ algorithmStr = "ElGamal";
+ break;
+ }
+
+ default: {
+ algorithmStr = "???";
+ break;
+ }
+ }
+ return algorithmStr + ", " + keySize + "bit";
+ }
+
+ public static String convertToHex(byte[] fp) {
+ String fingerPrint = "";
+ for (int i = 0; i < fp.length; ++i) {
+ if (i != 0 && i % 10 == 0) {
+ fingerPrint += " ";
+ } else if (i != 0 && i % 2 == 0) {
+ fingerPrint += " ";
+ }
+ String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase();
+ while (chunk.length() < 2) {
+ chunk = "0" + chunk;
+ }
+ fingerPrint += chunk;
+ }
+
+ return fingerPrint;
+
+ }
+
+ public static String getFingerPrint(long keyId) {
+ PGPPublicKey key = Apg.getPublicKey(keyId);
+ if (key == null) {
+ PGPSecretKey secretKey = Apg.getSecretKey(keyId);
+ if (secretKey == null) {
+ return "";
+ }
+ key = secretKey.getPublicKey();
+ }
+
+ return convertToHex(key.getFingerprint());
+ }
+
+ public static String getSmallFingerPrint(long keyId) {
+ String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase();
+ while (fingerPrint.length() < 8) {
+ fingerPrint = "0" + fingerPrint;
+ }
+ return fingerPrint;
+ }
+
+ public static String keyToHex(long keyId) {
+ return getSmallFingerPrint(keyId >> 32) + getSmallFingerPrint(keyId);
+ }
+
+ public static long keyFromHex(String data) {
+ int len = data.length();
+ String s2 = data.substring(len - 8);
+ String s1 = data.substring(0, len - 8);
+ return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
+ }
+
+ public static void deleteKey(int keyRingId) {
+ mDatabase.deleteKeyRing(keyRingId);
+ }
+
+ public static PGPKeyRing getKeyRing(int keyRingId) {
+ return (PGPKeyRing) mDatabase.getKeyRing(keyRingId);
+ }
+
+ public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
+ byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_secret, keyId);
+ if (data == null) {
+ return null;
+ }
+ try {
+ return new PGPSecretKeyRing(data);
+ } catch (IOException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
+ } catch (PGPException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
+ }
+ return null;
+ }
+
+ public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
+ byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_public, keyId);
+ if (data == null) {
+ return null;
+ }
+ try {
+ return new PGPPublicKeyRing(data);
+ } catch (IOException e) {
+ // no good way to handle this, return null
+ // TODO: some info?
+ }
+ return null;
+ }
+
+ public static PGPSecretKey getSecretKey(long keyId) {
+ PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
+ }
+ return keyRing.getSecretKey(keyId);
+ }
+
+ public static PGPPublicKey getPublicKey(long keyId) {
+ PGPPublicKeyRing keyRing = getPublicKeyRing(keyId);
+ if (keyRing == null) {
+ return null;
+ }
+
+ return keyRing.getPublicKey(keyId);
+ }
+
+ public static Vector getKeyRingIds(int type) {
+ SQLiteDatabase db = mDatabase.db();
+ Vector keyIds = new Vector();
+ Cursor c = db.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID }, KeyRings.TYPE
+ + " = ?", new String[] { "" + type }, null, null, null);
+ if (c != null && c.moveToFirst()) {
+ do {
+ keyIds.add(c.getInt(0));
+ } while (c.moveToNext());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return keyIds;
+ }
+
+ public static String getMainUserId(long keyId, int type) {
+ SQLiteDatabase db = mDatabase.db();
+ Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON ("
+ + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
+ + Keys.KEY_RING_ID + ") " + " INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON ("
+ + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + "masterKey."
+ + Keys.KEY_RING_ID + " AND " + "masterKey." + Keys.IS_MASTER_KEY + " = '1') "
+ + " INNER JOIN " + UserIds.TABLE_NAME + " ON (" + UserIds.TABLE_NAME + "."
+ + UserIds.KEY_ID + " = " + "masterKey." + Keys._ID + " AND " + UserIds.TABLE_NAME
+ + "." + UserIds.RANK + " = '0')", new String[] { UserIds.USER_ID }, Keys.TABLE_NAME
+ + "." + Keys.KEY_ID + " = ? AND " + KeyRings.TABLE_NAME + "." + KeyRings.TYPE
+ + " = ?", new String[] { "" + keyId, "" + type, }, null, null, null);
+ String userId = "";
+ if (c != null && c.moveToFirst()) {
+ do {
+ userId = c.getString(0);
+ } while (c.moveToNext());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return userId;
+ }
+
+ public static void encrypt(Context context, InputData data, OutputStream outStream,
+ boolean armored, long encryptionKeyIds[], long signatureKeyId,
+ String signaturePassPhrase, ProgressDialogUpdater progress, int symmetricAlgorithm,
+ int hashAlgorithm, int compression, boolean forceV3Signature, String passPhrase)
+ throws IOException, GeneralException, PGPException, NoSuchProviderException,
+ NoSuchAlgorithmException, SignatureException {
+ Security.addProvider(new BouncyCastleProvider());
+
+ 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", getFullVersion(context));
+ out = armorOut;
+ } else {
+ out = outStream;
+ }
+ PGPSecretKey signingKey = null;
+ PGPSecretKeyRing signingKeyRing = null;
+ PGPPrivateKey signaturePrivateKey = null;
+
+ if (encryptionKeyIds.length == 0 && passPhrase == null) {
+ throw new GeneralException(
+ context.getString(R.string.error_noEncryptionKeysOrPassPhrase));
+ }
+
+ if (signatureKeyId != 0) {
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
+ signingKey = getSigningKey(signatureKeyId);
+ if (signingKey == null) {
+ throw new GeneralException(context.getString(R.string.error_signatureFailed));
+ }
+
+ if (signaturePassPhrase == null) {
+ throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
+ }
+ if (progress != null)
+ progress.setProgress(R.string.progress_extractingSignatureKey, 0, 100);
+ signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ if (signaturePrivateKey == null) {
+ throw new GeneralException(
+ context.getString(R.string.error_couldNotExtractPrivateKey));
+ }
+ }
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingStreams, 5, 100);
+
+ // encrypt and compress input file content
+ PGPEncryptedDataGenerator cPk = 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) {
+ cPk.addMethod(key);
+ }
+ }
+ encryptOut = cPk.open(out, new byte[1 << 16]);
+
+ PGPSignatureGenerator signatureGenerator = null;
+ PGPV3SignatureGenerator signatureV3Generator = null;
+
+ if (signatureKeyId != 0) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingSignature, 10, 100);
+ if (forceV3Signature) {
+ signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
+ .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
+ signatureV3Generator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
+ } else {
+ signatureGenerator = new PGPSignatureGenerator(signingKey.getPublicKey()
+ .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
+ signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
+
+ String userId = getMainUserId(getMasterKey(signingKeyRing));
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ spGen.setSignerUserID(false, userId);
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ }
+ }
+
+ PGPCompressedDataGenerator compressGen = null;
+ BCPGOutputStream bcpgOut = null;
+ if (compression == Id.choice.compression.none) {
+ bcpgOut = new BCPGOutputStream(encryptOut);
+ } else {
+ compressGen = new PGPCompressedDataGenerator(compression);
+ bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
+ }
+ if (signatureKeyId != 0) {
+ if (forceV3Signature) {
+ signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
+ } else {
+ signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
+ }
+ }
+
+ PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
+ // file name not needed, so empty string
+ OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
+ new byte[1 << 16]);
+ if (progress != null)
+ progress.setProgress(R.string.progress_encrypting, 20, 100);
+
+ long done = 0;
+ int n = 0;
+ byte[] buffer = new byte[1 << 16];
+ InputStream in = data.getInputStream();
+ while ((n = in.read(buffer)) > 0) {
+ pOut.write(buffer, 0, n);
+ if (signatureKeyId != 0) {
+ if (forceV3Signature) {
+ signatureV3Generator.update(buffer, 0, n);
+ } else {
+ signatureGenerator.update(buffer, 0, n);
+ }
+ }
+ done += n;
+ if (data.getSize() != 0) {
+ if (progress != null)
+ progress.setProgress((int) (20 + (95 - 20) * done / data.getSize()), 100);
+ }
+ }
+
+ literalGen.close();
+
+ if (signatureKeyId != 0) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_generatingSignature, 95, 100);
+ if (forceV3Signature) {
+ signatureV3Generator.generate().encode(pOut);
+ } else {
+ signatureGenerator.generate().encode(pOut);
+ }
+ }
+ if (compressGen != null) {
+ compressGen.close();
+ }
+ encryptOut.close();
+ if (armored) {
+ armorOut.close();
+ }
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+ }
+
+ public static void signText(Context context, InputData data, OutputStream outStream,
+ long signatureKeyId, String signaturePassPhrase, int hashAlgorithm,
+ boolean forceV3Signature, ProgressDialogUpdater progress) throws GeneralException,
+ PGPException, IOException, NoSuchAlgorithmException, SignatureException {
+ Security.addProvider(new BouncyCastleProvider());
+
+ ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
+ armorOut.setHeader("Version", getFullVersion(context));
+
+ PGPSecretKey signingKey = null;
+ PGPSecretKeyRing signingKeyRing = null;
+ PGPPrivateKey signaturePrivateKey = null;
+
+ if (signatureKeyId == 0) {
+ throw new GeneralException(context.getString(R.string.error_noSignatureKey));
+ }
+
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
+ signingKey = getSigningKey(signatureKeyId);
+ if (signingKey == null) {
+ throw new GeneralException(context.getString(R.string.error_signatureFailed));
+ }
+
+ if (signaturePassPhrase == null) {
+ throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
+ }
+ signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ if (signaturePrivateKey == null) {
+ throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey));
+ }
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingStreams, 0, 100);
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingSignature, 30, 100);
+
+ PGPSignatureGenerator signatureGenerator = null;
+ PGPV3SignatureGenerator signatureV3Generator = null;
+
+ if (forceV3Signature) {
+ signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
+ .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
+ signatureV3Generator
+ .initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
+ } else {
+ signatureGenerator = new PGPSignatureGenerator(
+ signingKey.getPublicKey().getAlgorithm(), hashAlgorithm,
+ new BouncyCastleProvider());
+ signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ String userId = getMainUserId(getMasterKey(signingKeyRing));
+ spGen.setSignerUserID(false, userId);
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ }
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_signing, 40, 100);
+
+ armorOut.beginClearText(hashAlgorithm);
+
+ InputStream inStream = data.getInputStream();
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
+
+ final byte[] newline = "\r\n".getBytes("UTF-8");
+
+ if (forceV3Signature) {
+ processLine(reader.readLine(), armorOut, signatureV3Generator);
+ } else {
+ processLine(reader.readLine(), armorOut, signatureGenerator);
+ }
+
+ while (true) {
+ final String line = reader.readLine();
+
+ if (line == null) {
+ armorOut.write(newline);
+ break;
+ }
+
+ armorOut.write(newline);
+ if (forceV3Signature) {
+ signatureV3Generator.update(newline);
+ processLine(line, armorOut, signatureV3Generator);
+ } else {
+ signatureGenerator.update(newline);
+ processLine(line, armorOut, signatureGenerator);
+ }
+ }
+
+ armorOut.endClearText();
+
+ BCPGOutputStream bOut = new BCPGOutputStream(armorOut);
+ if (forceV3Signature) {
+ signatureV3Generator.generate().encode(bOut);
+ } else {
+ signatureGenerator.generate().encode(bOut);
+ }
+ armorOut.close();
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+ }
+
+ public static void generateSignature(Context context, InputData data, OutputStream outStream,
+ boolean armored, boolean binary, long signatureKeyId, String signaturePassPhrase,
+ int hashAlgorithm, boolean forceV3Signature, ProgressDialogUpdater progress)
+ throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
+ SignatureException {
+ Security.addProvider(new BouncyCastleProvider());
+
+ ArmoredOutputStream armorOut = null;
+ OutputStream out = null;
+ if (armored) {
+ armorOut = new ArmoredOutputStream(outStream);
+ armorOut.setHeader("Version", getFullVersion(context));
+ out = armorOut;
+ } else {
+ out = outStream;
+ }
+
+ PGPSecretKey signingKey = null;
+ PGPSecretKeyRing signingKeyRing = null;
+ PGPPrivateKey signaturePrivateKey = null;
+
+ if (signatureKeyId == 0) {
+ throw new GeneralException(context.getString(R.string.error_noSignatureKey));
+ }
+
+ signingKeyRing = getSecretKeyRing(signatureKeyId);
+ signingKey = getSigningKey(signatureKeyId);
+ if (signingKey == null) {
+ throw new GeneralException(context.getString(R.string.error_signatureFailed));
+ }
+
+ if (signaturePassPhrase == null) {
+ throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
+ }
+ signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
+ new BouncyCastleProvider());
+ if (signaturePrivateKey == null) {
+ throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey));
+ }
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingStreams, 0, 100);
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingSignature, 30, 100);
+
+ PGPSignatureGenerator signatureGenerator = null;
+ PGPV3SignatureGenerator signatureV3Generator = null;
+
+ int type = PGPSignature.CANONICAL_TEXT_DOCUMENT;
+ if (binary) {
+ type = PGPSignature.BINARY_DOCUMENT;
+ }
+
+ if (forceV3Signature) {
+ signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
+ .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
+ signatureV3Generator.initSign(type, signaturePrivateKey);
+ } else {
+ signatureGenerator = new PGPSignatureGenerator(
+ signingKey.getPublicKey().getAlgorithm(), hashAlgorithm,
+ new BouncyCastleProvider());
+ signatureGenerator.initSign(type, signaturePrivateKey);
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ String userId = getMainUserId(getMasterKey(signingKeyRing));
+ spGen.setSignerUserID(false, userId);
+ signatureGenerator.setHashedSubpackets(spGen.generate());
+ }
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_signing, 40, 100);
+
+ InputStream inStream = data.getInputStream();
+ if (binary) {
+ byte[] buffer = new byte[1 << 16];
+ int n = 0;
+ while ((n = inStream.read(buffer)) > 0) {
+ if (forceV3Signature) {
+ signatureV3Generator.update(buffer, 0, n);
+ } else {
+ signatureGenerator.update(buffer, 0, n);
+ }
+ }
+ } else {
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
+ final byte[] newline = "\r\n".getBytes("UTF-8");
+
+ while (true) {
+ final String line = reader.readLine();
+
+ if (line == null) {
+ break;
+ }
+
+ if (forceV3Signature) {
+ processLine(line, null, signatureV3Generator);
+ signatureV3Generator.update(newline);
+ } else {
+ processLine(line, null, signatureGenerator);
+ signatureGenerator.update(newline);
+ }
+ }
+ }
+
+ BCPGOutputStream bOut = new BCPGOutputStream(out);
+ if (forceV3Signature) {
+ signatureV3Generator.generate().encode(bOut);
+ } else {
+ signatureGenerator.generate().encode(bOut);
+ }
+ out.close();
+ outStream.close();
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+ }
+
+ public static long getDecryptionKeyId(Context context, InputData data) throws GeneralException,
+ NoAsymmetricEncryptionException, IOException {
+ InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ 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));
+ }
+
+ // TODO: currently we always only look at the first known key
+ // find the secret key
+ PGPSecretKey secretKey = null;
+ 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 = getSecretKey(pbe.getKeyID());
+ if (secretKey != null) {
+ break;
+ }
+ }
+ }
+
+ if (!gotAsymmetricEncryption) {
+ throw new NoAsymmetricEncryptionException();
+ }
+
+ if (secretKey == null) {
+ return Id.key.none;
+ }
+
+ return secretKey.getKeyID();
+ }
+
+ public static boolean hasSymmetricEncryption(Context context, InputData data)
+ throws GeneralException, IOException {
+ InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ 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, InputData data, OutputStream outStream,
+ String passPhrase, ProgressDialogUpdater progress, boolean assumeSymmetric)
+ throws IOException, GeneralException, PGPException, SignatureException {
+ if (passPhrase == null) {
+ passPhrase = "";
+ }
+ Bundle returnData = new Bundle();
+ InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ PGPEncryptedDataList enc;
+ Object o = pgpF.nextObject();
+ long signatureKeyId = 0;
+
+ int currentProgress = 0;
+ if (progress != null)
+ progress.setProgress(R.string.progress_readingData, currentProgress, 100);
+
+ if (o instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) o;
+ } else {
+ enc = (PGPEncryptedDataList) pgpF.nextObject();
+ }
+
+ if (enc == null) {
+ throw new GeneralException(context.getString(R.string.error_invalidData));
+ }
+
+ 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 (pbe == null) {
+ throw new GeneralException(
+ context.getString(R.string.error_noSymmetricEncryptionPacket));
+ }
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
+ clear = pbe.getDataStream(passPhrase.toCharArray(), new BouncyCastleProvider());
+ encryptedData = pbe;
+ currentProgress += 5;
+ } else {
+ if (progress != null)
+ 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 = getSecretKey(encData.getKeyID());
+ if (secretKey != null) {
+ pbe = encData;
+ break;
+ }
+ }
+ }
+
+ if (secretKey == null) {
+ throw new GeneralException(context.getString(R.string.error_noSecretKeyFound));
+ }
+
+ currentProgress += 5;
+ if (progress != null)
+ 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));
+ }
+ if (privateKey == null) {
+ throw new GeneralException(
+ context.getString(R.string.error_couldNotExtractPrivateKey));
+ }
+ currentProgress += 5;
+ if (progress != null)
+ progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
+ clear = pbe.getDataStream(privateKey, new BouncyCastleProvider());
+ encryptedData = pbe;
+ currentProgress += 5;
+ }
+
+ PGPObjectFactory plainFact = new PGPObjectFactory(clear);
+ Object dataChunk = plainFact.nextObject();
+ PGPOnePassSignature signature = null;
+ PGPPublicKey signatureKey = null;
+ int signatureIndex = -1;
+
+ if (dataChunk instanceof PGPCompressedData) {
+ if (progress != null)
+ 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) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
+ returnData.putBoolean(EXTRA_SIGNATURE, true);
+ PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
+ for (int i = 0; i < sigList.size(); ++i) {
+ signature = sigList.get(i);
+ signatureKey = getPublicKey(signature.getKeyID());
+ if (signatureKeyId == 0) {
+ signatureKeyId = signature.getKeyID();
+ }
+ if (signatureKey == null) {
+ signature = null;
+ } else {
+ signatureIndex = i;
+ signatureKeyId = signature.getKeyID();
+ String userId = null;
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
+ if (sigKeyRing != null) {
+ userId = getMainUserId(getMasterKey(sigKeyRing));
+ }
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
+ break;
+ }
+ }
+
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
+
+ if (signature != null) {
+ signature.initVerify(signatureKey, new BouncyCastleProvider());
+ } else {
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
+ }
+
+ dataChunk = plainFact.nextObject();
+ currentProgress += 10;
+ }
+
+ if (dataChunk instanceof PGPSignatureList) {
+ dataChunk = plainFact.nextObject();
+ }
+
+ if (dataChunk instanceof PGPLiteralData) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_decrypting, currentProgress, 100);
+ PGPLiteralData literalData = (PGPLiteralData) dataChunk;
+ OutputStream out = outStream;
+
+ byte[] buffer = new byte[1 << 16];
+ InputStream dataIn = literalData.getInputStream();
+
+ 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 = data.getStreamPosition();
+ while ((n = dataIn.read(buffer)) > 0) {
+ out.write(buffer, 0, n);
+ done += n;
+ if (signature != null) {
+ try {
+ signature.update(buffer, 0, n);
+ } catch (SignatureException e) {
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, 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 (data.getSize() - startPos == 0) {
+ currentProgress = endProgress;
+ } else {
+ currentProgress = (int) (startProgress + (endProgress - startProgress)
+ * (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
+ }
+ if (progress != null)
+ progress.setProgress(currentProgress, 100);
+ }
+
+ if (signature != null) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_verifyingSignature, 90, 100);
+ PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
+ PGPSignature messageSignature = signatureList.get(signatureIndex);
+ if (signature.verify(messageSignature)) {
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
+ } else {
+ returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
+ }
+ }
+ }
+
+ // TODO: add integrity somewhere
+ if (encryptedData.isIntegrityProtected()) {
+ if (progress != null)
+ progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100);
+ if (encryptedData.verify()) {
+ // passed
+ } else {
+ // failed
+ }
+ } else {
+ // no integrity check
+ }
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ public static Bundle verifyText(BaseActivity context, InputData data, OutputStream outStream,
+ ProgressDialogUpdater progress) throws IOException, GeneralException, PGPException,
+ SignatureException {
+ Bundle returnData = new Bundle();
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream());
+
+ if (progress != null)
+ 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(EXTRA_SIGNATURE, true);
+
+ if (progress != null)
+ 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 = getPublicKey(signature.getKeyID());
+ if (signatureKeyId == 0) {
+ signatureKeyId = signature.getKeyID();
+ }
+ if (signatureKey == null) {
+ Bundle pauseData = new Bundle();
+ pauseData.putInt(Constants.extras.STATUS, Id.message.unknown_signature_key);
+ pauseData.putLong(Constants.extras.KEY_ID, signatureKeyId);
+ Message msg = new Message();
+ msg.setData(pauseData);
+ context.sendMessage(msg);
+ // pause here
+ context.getRunningThread().pause();
+ // see whether the key was found in the meantime
+ signatureKey = getPublicKey(signature.getKeyID());
+ }
+
+ if (signatureKey == null) {
+ signature = null;
+ } else {
+ signatureKeyId = signature.getKeyID();
+ String userId = null;
+ PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
+ if (sigKeyRing != null) {
+ userId = getMainUserId(getMasterKey(sigKeyRing));
+ }
+ returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
+ break;
+ }
+ }
+
+ returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
+
+ if (signature == null) {
+ returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
+ if (progress != null)
+ 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(EXTRA_SIGNATURE_SUCCESS, signature.verify());
+
+ if (progress != null)
+ progress.setProgress(R.string.progress_done, 100, 100);
+ return returnData;
+ }
+
+ public static int getStreamContent(Context context, InputStream inStream) throws IOException {
+ InputStream in = PGPUtil.getDecoderStream(inStream);
+ PGPObjectFactory pgpF = new PGPObjectFactory(in);
+ Object object = pgpF.nextObject();
+ while (object != null) {
+ if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) {
+ return Id.content.keys;
+ } else if (object instanceof PGPEncryptedDataList) {
+ return Id.content.encrypted_data;
+ }
+ object = pgpF.nextObject();
+ }
+
+ return Id.content.unknown;
+ }
+
+ private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
+ final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException {
+
+ if (pLine == null) {
+ return;
+ }
+
+ final char[] chars = pLine.toCharArray();
+ int len = chars.length;
+
+ while (len > 0) {
+ if (!Character.isWhitespace(chars[len - 1])) {
+ break;
+ }
+ len--;
+ }
+
+ final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
+
+ if (pArmoredOutput != null) {
+ pArmoredOutput.write(data);
+ }
+ pSignatureGenerator.update(data);
+ }
+
+ private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
+ final PGPV3SignatureGenerator pSignatureGenerator) throws IOException,
+ SignatureException {
+
+ if (pLine == null) {
+ return;
+ }
+
+ final char[] chars = pLine.toCharArray();
+ int len = chars.length;
+
+ while (len > 0) {
+ if (!Character.isWhitespace(chars[len - 1])) {
+ break;
+ }
+ len--;
+ }
+
+ final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
+
+ if (pArmoredOutput != null) {
+ pArmoredOutput.write(data);
+ }
+ pSignatureGenerator.update(data);
+ }
+
+ // taken from ClearSignedFileProcessor in BC
+ 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 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 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;
+ }
+
+ public static boolean isReleaseVersion(Context context) {
+ try {
+ PackageInfo pi = context.getPackageManager().getPackageInfo(mApgPackageName, 0);
+ if (pi.versionCode % 100 == 99) {
+ return true;
+ } else {
+ return false;
+ }
+ } catch (NameNotFoundException e) {
+ // unpossible!
+ return false;
+ }
+ }
+
+ public static String getVersion(Context context) {
+ if (VERSION != null) {
+ return VERSION;
+ }
+ try {
+ PackageInfo pi = context.getPackageManager().getPackageInfo(mApgPackageName, 0);
+ VERSION = pi.versionName;
+ return VERSION;
+ } catch (NameNotFoundException e) {
+ // unpossible!
+ return "0.0.0";
+ }
+ }
+
+ public static String getFullVersion(Context context) {
+ return "APG v" + getVersion(context);
+ }
+
+ public static String generateRandomString(int length) {
+ SecureRandom random = new SecureRandom();
+ /*
+ * try { random = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider()); } catch
+ * (NoSuchAlgorithmException e) { // TODO: need to handle this case somehow return null; }
+ */
+ byte bytes[] = new byte[length];
+ random.nextBytes(bytes);
+ String result = "";
+ for (int i = 0; i < length; ++i) {
+ int v = (bytes[i] + 256) % 64;
+ if (v < 10) {
+ result += (char) ('0' + v);
+ } else if (v < 36) {
+ result += (char) ('A' + v - 10);
+ } else if (v < 62) {
+ result += (char) ('a' + v - 36);
+ } else if (v == 62) {
+ result += '_';
+ } else if (v == 63) {
+ result += '.';
+ }
+ }
+ return result;
+ }
+
+ static long getLengthOfStream(InputStream in) throws IOException {
+ long size = 0;
+ long n = 0;
+ byte dummy[] = new byte[0x10000];
+ while ((n = in.read(dummy)) > 0) {
+ size += n;
+ }
+ return size;
+ }
+
+ public static void deleteFileSecurely(Context context, File file, ProgressDialogUpdater progress)
+ throws FileNotFoundException, IOException {
+ long length = file.length();
+ SecureRandom random = new SecureRandom();
+ RandomAccessFile raf = new RandomAccessFile(file, "rws");
+ raf.seek(0);
+ raf.getFilePointer();
+ byte[] data = new byte[1 << 16];
+ int pos = 0;
+ String msg = context.getString(R.string.progress_deletingSecurely, file.getName());
+ while (pos < length) {
+ if (progress != null)
+ progress.setProgress(msg, (int) (100 * pos / length), 100);
+ random.nextBytes(data);
+ raf.write(data);
+ pos += data.length;
+ }
+ raf.close();
+ file.delete();
+ }
+}
diff --git a/org_apg/src/org/apg/ApgService.java b/org_apg/src/org/apg/ApgService.java
new file mode 100644
index 000000000..82e85371d
--- /dev/null
+++ b/org_apg/src/org/apg/ApgService.java
@@ -0,0 +1,643 @@
+package org.apg;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.apg.provider.KeyRings;
+import org.apg.provider.Keys;
+import org.apg.provider.UserIds;
+import org.apg.IApgService;
+
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+public class ApgService extends Service {
+ private final static String TAG = "ApgService";
+ public static final boolean LOCAL_LOGV = true;
+ public static final boolean LOCAL_LOGD = true;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (LOCAL_LOGD)
+ Log.d(TAG, "bound");
+ return mBinder;
+ }
+
+ /** error status */
+ private static enum error {
+ ARGUMENTS_MISSING, APG_FAILURE, NO_MATCHING_SECRET_KEY, PRIVATE_KEY_PASSPHRASE_WRONG, PRIVATE_KEY_PASSPHRASE_MISSING;
+
+ public int shiftedOrdinal() {
+ return ordinal() + 100;
+ }
+ }
+
+ private static enum call {
+ encrypt_with_passphrase, encrypt_with_public_key, decrypt, get_keys
+ }
+
+ /** all arguments that can be passed by calling application */
+ public static enum arg {
+ MESSAGE, // message to encrypt or to decrypt
+ SYMMETRIC_PASSPHRASE, // key for symmetric en/decryption
+ PUBLIC_KEYS, // public keys for encryption
+ ENCRYPTION_ALGORYTHM, // encryption algorithm
+ HASH_ALGORYTHM, // hash algorithm
+ ARMORED_OUTPUT, // whether to armor output
+ FORCE_V3_SIGNATURE, // whether to force v3 signature
+ COMPRESSION, // what compression to use for encrypted output
+ SIGNATURE_KEY, // key for signing
+ PRIVATE_KEY_PASSPHRASE, // passphrase for encrypted private key
+ KEY_TYPE, // type of key (private or public)
+ BLOB, // blob passed
+ }
+
+ /** all things that might be returned */
+ private static enum ret {
+ ERRORS, // string array list with errors
+ WARNINGS, // string array list with warnings
+ ERROR, // numeric error
+ RESULT, // en-/decrypted
+ FINGERPRINTS, // fingerprints of keys
+ USER_IDS, // user ids
+ }
+
+ /** required arguments for each AIDL function */
+ private static final HashMap> FUNCTIONS_REQUIRED_ARGS = new HashMap>();
+ static {
+ HashSet args = new HashSet();
+ args.add(arg.SYMMETRIC_PASSPHRASE);
+ FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_passphrase.name(), args);
+
+ args = new HashSet();
+ args.add(arg.PUBLIC_KEYS);
+ FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_public_key.name(), args);
+
+ args = new HashSet();
+ FUNCTIONS_REQUIRED_ARGS.put(call.decrypt.name(), args);
+
+ args = new HashSet();
+ args.add(arg.KEY_TYPE);
+ FUNCTIONS_REQUIRED_ARGS.put(call.get_keys.name(), args);
+ }
+
+ /** optional arguments for each AIDL function */
+ private static final HashMap> FUNCTIONS_OPTIONAL_ARGS = new HashMap>();
+ static {
+ HashSet args = new HashSet();
+ args.add(arg.ENCRYPTION_ALGORYTHM);
+ args.add(arg.HASH_ALGORYTHM);
+ args.add(arg.ARMORED_OUTPUT);
+ args.add(arg.FORCE_V3_SIGNATURE);
+ args.add(arg.COMPRESSION);
+ args.add(arg.PRIVATE_KEY_PASSPHRASE);
+ args.add(arg.SIGNATURE_KEY);
+ args.add(arg.BLOB);
+ args.add(arg.MESSAGE);
+ FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_passphrase.name(), args);
+ FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_public_key.name(), args);
+
+ args = new HashSet();
+ args.add(arg.SYMMETRIC_PASSPHRASE);
+ args.add(arg.PUBLIC_KEYS);
+ args.add(arg.PRIVATE_KEY_PASSPHRASE);
+ args.add(arg.MESSAGE);
+ args.add(arg.BLOB);
+ FUNCTIONS_OPTIONAL_ARGS.put(call.decrypt.name(), args);
+ }
+
+ /** a map from ApgService parameters to function calls to get the default */
+ private static final HashMap FUNCTIONS_DEFAULTS = new HashMap();
+ static {
+ FUNCTIONS_DEFAULTS.put(arg.ENCRYPTION_ALGORYTHM, "getDefaultEncryptionAlgorithm");
+ FUNCTIONS_DEFAULTS.put(arg.HASH_ALGORYTHM, "getDefaultHashAlgorithm");
+ FUNCTIONS_DEFAULTS.put(arg.ARMORED_OUTPUT, "getDefaultAsciiArmour");
+ FUNCTIONS_DEFAULTS.put(arg.FORCE_V3_SIGNATURE, "getForceV3Signatures");
+ FUNCTIONS_DEFAULTS.put(arg.COMPRESSION, "getDefaultMessageCompression");
+ }
+
+ /** a map of the default function names to their method */
+ private static final HashMap FUNCTIONS_DEFAULTS_METHODS = new HashMap();
+ static {
+ try {
+ FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm",
+ Preferences.class.getMethod("getDefaultEncryptionAlgorithm"));
+ FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm",
+ Preferences.class.getMethod("getDefaultHashAlgorithm"));
+ FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour",
+ Preferences.class.getMethod("getDefaultAsciiArmour"));
+ FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures",
+ Preferences.class.getMethod("getForceV3Signatures"));
+ FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression",
+ Preferences.class.getMethod("getDefaultMessageCompression"));
+ } catch (Exception e) {
+ Log.e(TAG, "Function method exception: " + e.getMessage());
+ }
+ }
+
+ private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException {
+ byte[] buffer = new byte[8];
+ int len = 0;
+ while ((len = is.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ }
+
+ private static Cursor getKeyEntries(HashMap pParams) {
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "("
+ + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
+ + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY
+ + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + "("
+ + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "."
+ + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
+
+ String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by")
+ : UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
+
+ String typeVal[] = null;
+ String typeWhere = null;
+ if (pParams.containsKey("key_type")) {
+ typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?";
+ typeVal = new String[] { "" + pParams.get("key_type") };
+ }
+ return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere,
+ typeVal, null, null, orderBy);
+ }
+
+ /**
+ * maps a fingerprint or user id of a key to a master key in database
+ *
+ * @param search_key
+ * fingerprint or user id to search for
+ * @return master key if found, or 0
+ */
+ private static long getMasterKey(String pSearchKey, Bundle pReturn) {
+ if (pSearchKey == null || pSearchKey.length() != 8) {
+ return 0;
+ }
+ ArrayList keyList = new ArrayList();
+ keyList.add(pSearchKey);
+ long[] keys = getMasterKey(keyList, pReturn);
+ if (keys.length > 0) {
+ return keys[0];
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * maps fingerprints or user ids of keys to master keys in database
+ *
+ * @param search_keys
+ * a list of keys (fingerprints or user ids) to look for in database
+ * @return an array of master keys
+ */
+ private static long[] getMasterKey(ArrayList pSearchKeys, Bundle pReturn) {
+
+ HashMap qParams = new HashMap();
+ qParams.put("columns", new String[] { KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1
+ });
+ qParams.put("key_type", Id.database.type_public);
+
+ Cursor mCursor = getKeyEntries(qParams);
+
+ if (LOCAL_LOGV)
+ Log.v(TAG, "going through installed user keys");
+ ArrayList masterKeys = new ArrayList();
+ while (mCursor.moveToNext()) {
+ long curMkey = mCursor.getLong(0);
+ String curUser = mCursor.getString(1);
+
+ String curFprint = Apg.getSmallFingerPrint(curMkey);
+ if (LOCAL_LOGV)
+ Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")");
+ if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) {
+ if (LOCAL_LOGV)
+ Log.v(TAG, "master key found for: " + curFprint);
+ masterKeys.add(curMkey);
+ pSearchKeys.remove(curFprint);
+ } else {
+ if (LOCAL_LOGV)
+ Log.v(TAG, "Installed key " + curFprint
+ + " is not in the list of public keys to encrypt with");
+ }
+ }
+ mCursor.close();
+
+ long[] masterKeyLongs = new long[masterKeys.size()];
+ int i = 0;
+ for (Long key : masterKeys) {
+ masterKeyLongs[i++] = key;
+ }
+
+ if (i == 0) {
+ Log.w(TAG, "Found not one public key");
+ pReturn.getStringArrayList(ret.WARNINGS.name()).add(
+ "Searched for public key(s) but found not one");
+ }
+
+ for (String key : pSearchKeys) {
+ Log.w(TAG, "Searched for key " + key + " but cannot find it in APG");
+ pReturn.getStringArrayList(ret.WARNINGS.name()).add(
+ "Searched for key " + key + " but cannot find it in APG");
+ }
+
+ return masterKeyLongs;
+ }
+
+ /**
+ * Add default arguments if missing
+ *
+ * @param args
+ * the bundle to add default parameters to if missing
+ */
+ private void addDefaultArguments(String pCall, Bundle pArgs) {
+ // check whether there are optional elements defined for that call
+ if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pCall)) {
+ Preferences preferences = Preferences.getPreferences(getBaseContext(), true);
+
+ Iterator iter = FUNCTIONS_DEFAULTS.keySet().iterator();
+ while (iter.hasNext()) {
+ arg currentArg = iter.next();
+ String currentKey = currentArg.name();
+ if (!pArgs.containsKey(currentKey)
+ && FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) {
+ String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg);
+ try {
+ Class> returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
+ .getReturnType();
+ if (returnType == String.class) {
+ pArgs.putString(currentKey,
+ (String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
+ .invoke(preferences));
+ } else if (returnType == boolean.class) {
+ pArgs.putBoolean(currentKey,
+ (Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
+ .invoke(preferences));
+ } else if (returnType == int.class) {
+ pArgs.putInt(currentKey,
+ (Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
+ .invoke(preferences));
+ } else {
+ Log.e(TAG, "Unknown return type " + returnType.toString()
+ + " for default option");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in add_default_arguments " + e.getMessage());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * updates a Bundle with default return values
+ *
+ * @param pReturn
+ * the Bundle to update
+ */
+ private void addDefaultReturns(Bundle pReturn) {
+ ArrayList errors = new ArrayList();
+ ArrayList warnings = new ArrayList();
+
+ pReturn.putStringArrayList(ret.ERRORS.name(), errors);
+ pReturn.putStringArrayList(ret.WARNINGS.name(), warnings);
+ }
+
+ /**
+ * checks for required arguments and adds them to the error if missing
+ *
+ * @param function
+ * the functions required arguments to check for
+ * @param pArgs
+ * the Bundle of arguments to check
+ * @param pReturn
+ * the bundle to write errors to
+ */
+ private void checkForRequiredArgs(String pFunction, Bundle pArgs, Bundle pReturn) {
+ if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) {
+ Iterator iter = FUNCTIONS_REQUIRED_ARGS.get(pFunction).iterator();
+ while (iter.hasNext()) {
+ String curArg = iter.next().name();
+ if (!pArgs.containsKey(curArg)) {
+ pReturn.getStringArrayList(ret.ERRORS.name())
+ .add("Argument missing: " + curArg);
+ }
+ }
+ }
+
+ if (pFunction.equals(call.encrypt_with_passphrase.name())
+ || pFunction.equals(call.encrypt_with_public_key.name())
+ || pFunction.equals(call.decrypt.name())) {
+ // check that either MESSAGE or BLOB are there
+ if (!pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add(
+ "Arguments missing: Neither MESSAGE nor BLOG found");
+ }
+
+ }
+ }
+
+ /**
+ * checks for unknown arguments and add them to warning if found
+ *
+ * @param function
+ * the functions name to check against
+ * @param pArgs
+ * the Bundle of arguments to check
+ * @param pReturn
+ * the bundle to write warnings to
+ */
+ private void checkForUnknownArgs(String pFunction, Bundle pArgs, Bundle pReturn) {
+
+ HashSet allArgs = new HashSet();
+ if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) {
+ allArgs.addAll(FUNCTIONS_REQUIRED_ARGS.get(pFunction));
+ }
+ if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pFunction)) {
+ allArgs.addAll(FUNCTIONS_OPTIONAL_ARGS.get(pFunction));
+ }
+
+ ArrayList unknownArgs = new ArrayList();
+ Iterator iter = pArgs.keySet().iterator();
+ while (iter.hasNext()) {
+ String curKey = iter.next();
+ try {
+ arg curArg = arg.valueOf(curKey);
+ if (!allArgs.contains(curArg)) {
+ pReturn.getStringArrayList(ret.WARNINGS.name()).add(
+ "Unknown argument: " + curKey);
+ unknownArgs.add(curKey);
+ }
+ } catch (Exception e) {
+ pReturn.getStringArrayList(ret.WARNINGS.name()).add("Unknown argument: " + curKey);
+ unknownArgs.add(curKey);
+ }
+ }
+
+ // remove unknown arguments so our bundle has just what we need
+ for (String arg : unknownArgs) {
+ pArgs.remove(arg);
+ }
+ }
+
+ private boolean prepareArgs(String pCall, Bundle pArgs, Bundle pReturn) {
+ Apg.initialize(getBaseContext());
+
+ /* add default return values for all functions */
+ addDefaultReturns(pReturn);
+
+ /* add default arguments if missing */
+ addDefaultArguments(pCall, pArgs);
+ if (LOCAL_LOGV)
+ Log.v(TAG, "add_default_arguments");
+
+ /* check for required arguments */
+ checkForRequiredArgs(pCall, pArgs, pReturn);
+ if (LOCAL_LOGV)
+ Log.v(TAG, "check_required_args");
+
+ /* check for unknown arguments and add to warning if found */
+ checkForUnknownArgs(pCall, pArgs, pReturn);
+ if (LOCAL_LOGV)
+ Log.v(TAG, "check_unknown_args");
+
+ /* return if errors happened */
+ if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) {
+ if (LOCAL_LOGV)
+ Log.v(TAG, "Errors after preparing, not executing " + pCall);
+ pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal());
+ return false;
+ }
+ if (LOCAL_LOGV)
+ Log.v(TAG, "error return");
+
+ return true;
+ }
+
+ private boolean encrypt(Bundle pArgs, Bundle pReturn) {
+ boolean isBlob = pArgs.containsKey(arg.BLOB.name());
+
+ long pubMasterKeys[] = {};
+ if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) {
+ ArrayList list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name());
+ ArrayList pubKeys = new ArrayList();
+ if (LOCAL_LOGV)
+ Log.v(TAG, "Long size: " + list.size());
+ Iterator iter = list.iterator();
+ while (iter.hasNext()) {
+ pubKeys.add(iter.next());
+ }
+ pubMasterKeys = getMasterKey(pubKeys, pReturn);
+ }
+
+ InputStream inStream = null;
+ if (isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on opening blob", e);
+ }
+ } else {
+ inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
+ }
+ InputData in = new InputData(inStream, 0); // XXX Size second param?
+
+ OutputStream out = new ByteArrayOutputStream();
+ if (LOCAL_LOGV)
+ Log.v(TAG, "About to encrypt");
+ try {
+ Apg.encrypt(getBaseContext(), // context
+ in, // input stream
+ out, // output stream
+ pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT
+ pubMasterKeys, // encryption keys
+ getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature
+ // key
+ pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase
+ null, // progress
+ pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption
+ pArgs.getInt(arg.HASH_ALGORYTHM.name()), // hash
+ pArgs.getInt(arg.COMPRESSION.name()), // compression
+ pArgs.getBoolean(arg.FORCE_V3_SIGNATURE.name()), // mPreferences.getForceV3Signatures(),
+ pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) // passPhrase
+ );
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in encrypt");
+ String msg = e.getMessage();
+ if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add(
+ "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): "
+ + msg);
+ pReturn.putInt(ret.ERROR.name(),
+ error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal());
+ } else if (msg.equals(getBaseContext().getString(
+ R.string.error_couldNotExtractPrivateKey))) {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add(
+ "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
+ + " probably wrong): " + msg);
+ pReturn.putInt(ret.ERROR.name(),
+ error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
+ } else {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add(
+ "Internal failure (" + e.getClass() + ") in APG when encrypting: "
+ + e.getMessage());
+ pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
+ }
+ return false;
+ }
+ if (LOCAL_LOGV)
+ Log.v(TAG, "Encrypted");
+ if (isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
+ .name())));
+ writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream);
+ outStream.close();
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on writing blob", e);
+ }
+ } else {
+ pReturn.putString(ret.RESULT.name(), out.toString());
+ }
+ return true;
+ }
+
+ private final IApgService.Stub mBinder = new IApgService.Stub() {
+
+ public boolean getKeys(Bundle pArgs, Bundle pReturn) {
+
+ prepareArgs("get_keys", pArgs, pReturn);
+
+ HashMap qParams = new HashMap();
+ qParams.put("columns", new String[] {
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1
+ });
+
+ qParams.put("key_type", pArgs.getInt(arg.KEY_TYPE.name()));
+
+ Cursor cursor = getKeyEntries(qParams);
+ ArrayList fPrints = new ArrayList();
+ ArrayList ids = new ArrayList();
+ while (cursor.moveToNext()) {
+ if (LOCAL_LOGV)
+ Log.v(TAG, "adding key " + Apg.getSmallFingerPrint(cursor.getLong(0)));
+ fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0)));
+ ids.add(cursor.getString(1));
+ }
+ cursor.close();
+
+ pReturn.putStringArrayList(ret.FINGERPRINTS.name(), fPrints);
+ pReturn.putStringArrayList(ret.USER_IDS.name(), ids);
+ return true;
+ }
+
+ public boolean encryptWithPublicKey(Bundle pArgs, Bundle pReturn) {
+ if (!prepareArgs("encrypt_with_public_key", pArgs, pReturn)) {
+ return false;
+ }
+
+ return encrypt(pArgs, pReturn);
+ }
+
+ public boolean encryptWithPassphrase(Bundle pArgs, Bundle pReturn) {
+ if (!prepareArgs("encrypt_with_passphrase", pArgs, pReturn)) {
+ return false;
+ }
+
+ return encrypt(pArgs, pReturn);
+
+ }
+
+ public boolean decrypt(Bundle pArgs, Bundle pReturn) {
+ if (!prepareArgs("decrypt", pArgs, pReturn)) {
+ return false;
+ }
+
+ boolean isBlob = pArgs.containsKey(arg.BLOB.name());
+
+ String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs
+ .getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs
+ .getString(arg.PRIVATE_KEY_PASSPHRASE.name());
+
+ InputStream inStream = null;
+ if (isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on opening blob", e);
+ }
+ } else {
+ inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
+ }
+
+ InputData in = new InputData(inStream, 0); // XXX what size in second parameter?
+ OutputStream out = new ByteArrayOutputStream();
+ if (LOCAL_LOGV)
+ Log.v(TAG, "About to decrypt");
+ try {
+ Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress
+ pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric
+ );
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in decrypt");
+ String msg = e.getMessage();
+ if (msg.equals(getBaseContext().getString(R.string.error_noSecretKeyFound))) {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg);
+ pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal());
+ } else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add(
+ "Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
+ + " wrong/missing): " + msg);
+ pReturn.putInt(ret.ERROR.name(),
+ error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
+ } else {
+ pReturn.getStringArrayList(ret.ERRORS.name()).add(
+ "Internal failure (" + e.getClass() + ") in APG when decrypting: "
+ + msg);
+ pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
+ }
+ return false;
+ }
+ if (LOCAL_LOGV)
+ Log.v(TAG, "... decrypted");
+
+ if (isBlob) {
+ ContentResolver cr = getContentResolver();
+ try {
+ OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
+ .name())));
+ writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()),
+ outStream);
+ outStream.close();
+ } catch (Exception e) {
+ Log.e(TAG, "... exception on writing blob", e);
+ }
+ } else {
+ pReturn.putString(ret.RESULT.name(), out.toString());
+ }
+ return true;
+ }
+
+ };
+}
diff --git a/org_apg/src/org/apg/AskForSecretKeyPassPhrase.java b/org_apg/src/org/apg/AskForSecretKeyPassPhrase.java
new file mode 100644
index 000000000..ebed75667
--- /dev/null
+++ b/org_apg/src/org/apg/AskForSecretKeyPassPhrase.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg;
+
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.apg.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+public class AskForSecretKeyPassPhrase {
+ public static interface PassPhraseCallbackInterface {
+ void passPhraseCallback(long keyId, String passPhrase);
+ }
+
+ public static Dialog createDialog(Activity context, long secretKeyId,
+ PassPhraseCallbackInterface callback) {
+ AlertDialog.Builder alert = new AlertDialog.Builder(context);
+
+ alert.setTitle(R.string.title_authentication);
+
+ final PGPSecretKey secretKey;
+ final Activity activity = context;
+
+ if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
+ secretKey = null;
+ alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
+ } else {
+ secretKey = Apg.getMasterKey(Apg.getSecretKeyRing(secretKeyId));
+ if (secretKey == null) {
+ alert.setTitle(R.string.title_keyNotFound);
+ alert.setMessage(context.getString(R.string.keyNotFound, secretKeyId));
+ alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ }
+ });
+ alert.setCancelable(false);
+ return alert.create();
+ }
+ String userId = Apg.getMainUserIdSafe(context, secretKey);
+ alert.setMessage(context.getString(R.string.passPhraseFor, userId));
+ }
+
+ 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;
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ String passPhrase = "" + input.getText();
+ long keyId;
+ if (secretKey != null) {
+ try {
+ PGPPrivateKey testKey = secretKey.extractPrivateKey(
+ passPhrase.toCharArray(), new BouncyCastleProvider());
+ if (testKey == null) {
+ Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ } catch (PGPException e) {
+ Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+ keyId = secretKey.getKeyID();
+ } else {
+ keyId = Id.key.symmetric;
+ }
+ cb.passPhraseCallback(keyId, passPhrase);
+ }
+ });
+
+ alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ activity.removeDialog(Id.dialog.pass_phrase);
+ }
+ });
+
+ return alert.create();
+ }
+}
diff --git a/org_apg/src/org/apg/CachedPassPhrase.java b/org_apg/src/org/apg/CachedPassPhrase.java
new file mode 100644
index 000000000..2d67a300d
--- /dev/null
+++ b/org_apg/src/org/apg/CachedPassPhrase.java
@@ -0,0 +1,48 @@
+package org.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;
+ }
+
+ @Override
+ public int hashCode() {
+ int hc1 = (int) (this.timestamp & 0xffffffff);
+ int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode());
+ return (hc1 + hc2) * hc2 + hc1;
+ }
+
+ @Override
+ 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;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + timestamp + ", *******)";
+ }
+}
diff --git a/org_apg/src/org/apg/Constants.java b/org_apg/src/org/apg/Constants.java
new file mode 100644
index 000000000..9669d4b0d
--- /dev/null
+++ b/org_apg/src/org/apg/Constants.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg;
+
+import android.os.Environment;
+
+public final class Constants {
+
+ public static final String TAG = "APG";
+
+ 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_HELP = "seenHelp";
+ public static final String HAS_SEEN_CHANGE_LOG = "seenChangeLogDialog";
+ 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";
+ public static final String LANGUAGE = "language";
+ public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";
+ public static final String KEY_SERVERS = "keyServers";
+ }
+
+ public static final class defaults {
+ public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu";
+ }
+
+ public static final class extras {
+ public static final String PROGRESS = "progress";
+ public static final String PROGRESS_MAX = "max";
+ public static final String STATUS = "status";
+ public static final String MESSAGE = "message";
+ public static final String KEY_ID = "keyId";
+ }
+}
diff --git a/org_apg/src/org/apg/DataDestination.java b/org_apg/src/org/apg/DataDestination.java
new file mode 100644
index 000000000..509670e69
--- /dev/null
+++ b/org_apg/src/org/apg/DataDestination.java
@@ -0,0 +1,81 @@
+package org.apg;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apg.Apg.GeneralException;
+import org.apg.R;
+
+import android.content.Context;
+import android.os.Environment;
+
+public class DataDestination {
+ private String mStreamFilename;
+ private String mFilename;
+ private int mMode = Id.mode.undefined;
+
+ public DataDestination() {
+
+ }
+
+ public void setMode(int mode) {
+ mMode = mode;
+ }
+
+ public void setFilename(String filename) {
+ mFilename = filename;
+ }
+
+ public String getStreamFilename() {
+ return mStreamFilename;
+ }
+
+ public OutputStream getOutputStream(Context context) throws Apg.GeneralException,
+ FileNotFoundException, IOException {
+ OutputStream out = null;
+ mStreamFilename = null;
+
+ switch (mMode) {
+ case Id.mode.stream: {
+ try {
+ while (true) {
+ mStreamFilename = Apg.generateRandomString(32);
+ if (mStreamFilename == null) {
+ throw new Apg.GeneralException("couldn't generate random file name");
+ }
+ context.openFileInput(mStreamFilename).close();
+ }
+ } catch (FileNotFoundException e) {
+ // found a name that isn't used yet
+ }
+ out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
+ break;
+ }
+
+ case Id.mode.byte_array: {
+ out = new ByteArrayOutputStream();
+ break;
+ }
+
+ case Id.mode.file: {
+ if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(
+ context.getString(R.string.error_externalStorageNotReady));
+ }
+ }
+ out = new FileOutputStream(mFilename);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return out;
+ }
+}
diff --git a/org_apg/src/org/apg/DataSource.java b/org_apg/src/org/apg/DataSource.java
new file mode 100644
index 000000000..340afa9f8
--- /dev/null
+++ b/org_apg/src/org/apg/DataSource.java
@@ -0,0 +1,104 @@
+package org.apg;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apg.Apg.GeneralException;
+import org.apg.R;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+
+public class DataSource {
+ private Uri mContentUri = null;
+ private String mText = null;
+ private byte[] mData = null;
+
+ public DataSource() {
+
+ }
+
+ public void setUri(Uri uri) {
+ mContentUri = uri;
+ mText = null;
+ mData = null;
+ }
+
+ public void setUri(String uri) {
+ if (uri.startsWith("/")) {
+ setUri(Uri.parse("file://" + uri));
+ } else {
+ setUri(Uri.parse(uri));
+ }
+ }
+
+ public void setText(String text) {
+ mText = text;
+ mData = null;
+ mContentUri = null;
+ }
+
+ public void setData(byte[] data) {
+ mData = data;
+ mText = null;
+ mContentUri = null;
+ }
+
+ public boolean isText() {
+ return mText != null;
+ }
+
+ public boolean isBinary() {
+ return mData != null || mContentUri != null;
+ }
+
+ public InputData getInputData(Context context, boolean withSize) throws GeneralException,
+ FileNotFoundException, IOException {
+ InputStream in = null;
+ long size = 0;
+
+ if (mContentUri != null) {
+ if (mContentUri.getScheme().equals("file")) {
+ // get the rest after "file://"
+ String path = Uri.decode(mContentUri.toString().substring(7));
+ if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new GeneralException(
+ context.getString(R.string.error_externalStorageNotReady));
+ }
+ }
+ in = new FileInputStream(path);
+ File file = new File(path);
+ if (withSize) {
+ size = file.length();
+ }
+ } else {
+ in = context.getContentResolver().openInputStream(mContentUri);
+ if (withSize) {
+ InputStream tmp = context.getContentResolver().openInputStream(mContentUri);
+ size = Apg.getLengthOfStream(tmp);
+ tmp.close();
+ }
+ }
+ } else if (mText != null || mData != null) {
+ byte[] bytes = null;
+ if (mData != null) {
+ bytes = mData;
+ } else {
+ bytes = mText.getBytes();
+ }
+ in = new ByteArrayInputStream(bytes);
+ if (withSize) {
+ size = bytes.length;
+ }
+ }
+
+ return new InputData(in, size);
+ }
+
+}
diff --git a/org_apg/src/org/apg/FileDialog.java b/org_apg/src/org/apg/FileDialog.java
new file mode 100644
index 000000000..6ad498b90
--- /dev/null
+++ b/org_apg/src/org/apg/FileDialog.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg;
+
+import org.apg.R;
+
+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.CheckBox;
+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 CheckBox mCheckBox;
+ 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, boolean checkbox);
+ }
+
+ public static AlertDialog build(Activity activity, String title, String message,
+ String defaultFile, OnClickListener onClickListener, String fileManagerTitle,
+ String fileManagerButton, String checkboxText, int requestCode) {
+ // TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now,
+ // but maybe the Intent now used will someday support them again, so leaving them in
+ LayoutInflater inflater = (LayoutInflater) activity
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+
+ alert.setTitle(title);
+ alert.setMessage(message);
+
+ 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() {
+ public void onClick(View v) {
+ openFile();
+ }
+ });
+ mFileManagerTitle = fileManagerTitle;
+ mFileManagerButton = fileManagerButton;
+ mRequestCode = requestCode;
+ mCheckBox = (CheckBox) view.findViewById(R.id.checkbox);
+ if (checkboxText == null) {
+ mCheckBox.setEnabled(false);
+ mCheckBox.setVisibility(View.GONE);
+ } else {
+ mCheckBox.setEnabled(true);
+ mCheckBox.setVisibility(View.VISIBLE);
+ mCheckBox.setText(checkboxText);
+ }
+
+ alert.setView(view);
+
+ final OnClickListener clickListener = onClickListener;
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ boolean checked = false;
+ if (mCheckBox.isEnabled()) {
+ checked = mCheckBox.isChecked();
+ }
+ clickListener.onOkClick(mFilename.getText().toString(), checked);
+ }
+ });
+
+ 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(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+ intent.setData(Uri.parse("file://" + filename));
+ intent.setType("*/*");
+
+ try {
+ mActivity.startActivityForResult(intent, mRequestCode);
+ } catch (ActivityNotFoundException e) {
+ // No compatible file manager was found.
+ Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/HkpKeyServer.java b/org_apg/src/org/apg/HkpKeyServer.java
new file mode 100644
index 000000000..294a60cb2
--- /dev/null
+++ b/org_apg/src/org/apg/HkpKeyServer.java
@@ -0,0 +1,242 @@
+package org.apg;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+
+import android.text.Html;
+
+public class HkpKeyServer extends KeyServer {
+ private static class HttpError extends Exception {
+ private static final long serialVersionUID = 1718783705229428893L;
+ private int mCode;
+ private String mData;
+
+ public HttpError(int code, String data) {
+ super("" + code + ": " + data);
+ mCode = code;
+ mData = data;
+ }
+
+ public int getCode() {
+ return mCode;
+ }
+
+ public String getData() {
+ return mData;
+ }
+ }
+
+ private String mHost;
+ private short mPort = 11371;
+
+ // example:
+ // pub 2048R/9F5C9090 2009-08-17 Jörg Runge
+ // <joerg@joergrunge.de>
+ public static Pattern PUB_KEY_LINE = Pattern
+ .compile(
+ "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
+ Pattern.CASE_INSENSITIVE);
+ public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE
+ | Pattern.CASE_INSENSITIVE);
+
+ public HkpKeyServer(String host) {
+ mHost = host;
+ }
+
+ public HkpKeyServer(String host, short port) {
+ mHost = host;
+ mPort = port;
+ }
+
+ static private String readAll(InputStream in, String encoding) throws IOException {
+ ByteArrayOutputStream raw = new ByteArrayOutputStream();
+
+ byte buffer[] = new byte[1 << 16];
+ int n = 0;
+ while ((n = in.read(buffer)) != -1) {
+ raw.write(buffer, 0, n);
+ }
+
+ if (encoding == null) {
+ encoding = "utf8";
+ }
+ return raw.toString(encoding);
+ }
+
+ // TODO: replace this with httpclient
+ private String query(String request) throws QueryException, HttpError {
+ InetAddress ips[];
+ try {
+ ips = InetAddress.getAllByName(mHost);
+ } catch (UnknownHostException e) {
+ throw new QueryException(e.toString());
+ }
+ for (int i = 0; i < ips.length; ++i) {
+ try {
+ String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request;
+ URL realUrl = new URL(url);
+ HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
+ conn.setConnectTimeout(5000);
+ conn.setReadTimeout(25000);
+ conn.connect();
+ int response = conn.getResponseCode();
+ if (response >= 200 && response < 300) {
+ return readAll(conn.getInputStream(), conn.getContentEncoding());
+ } else {
+ String data = readAll(conn.getErrorStream(), conn.getContentEncoding());
+ throw new HttpError(response, data);
+ }
+ } catch (MalformedURLException e) {
+ // nothing to do, try next IP
+ } catch (IOException e) {
+ // nothing to do, try next IP
+ }
+ }
+
+ throw new QueryException("querying server(s) for '" + mHost + "' failed");
+ }
+
+ // TODO: replace this with httpclient
+ @Override
+ public List search(String query) throws QueryException, TooManyResponses,
+ InsufficientQuery {
+ Vector results = new Vector();
+
+ if (query.length() < 3) {
+ throw new InsufficientQuery();
+ }
+
+ String encodedQuery;
+ try {
+ encodedQuery = URLEncoder.encode(query, "utf8");
+ } catch (UnsupportedEncodingException e) {
+ return null;
+ }
+ String request = "/pks/lookup?op=index&search=" + encodedQuery;
+
+ String data = null;
+ try {
+ data = query(request);
+ } catch (HttpError e) {
+ if (e.getCode() == 404) {
+ return results;
+ } else {
+ if (e.getData().toLowerCase().contains("no keys found")) {
+ return results;
+ } else if (e.getData().toLowerCase().contains("too many")) {
+ throw new TooManyResponses();
+ } else if (e.getData().toLowerCase().contains("insufficient")) {
+ throw new InsufficientQuery();
+ }
+ }
+ throw new QueryException("querying server(s) for '" + mHost + "' failed");
+ }
+
+ Matcher matcher = PUB_KEY_LINE.matcher(data);
+ while (matcher.find()) {
+ KeyInfo info = new KeyInfo();
+ info.size = Integer.parseInt(matcher.group(1));
+ info.algorithm = matcher.group(2);
+ info.keyId = Apg.keyFromHex(matcher.group(3));
+ info.fingerPrint = Apg.getSmallFingerPrint(info.keyId);
+ String chunks[] = matcher.group(4).split("-");
+ info.date = new GregorianCalendar(Integer.parseInt(chunks[0]),
+ Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime();
+ info.userIds = new Vector();
+ if (matcher.group(5).startsWith("*** KEY")) {
+ info.revoked = matcher.group(5);
+ } else {
+ String tmp = matcher.group(5).replaceAll("<.*?>", "");
+ tmp = Html.fromHtml(tmp).toString();
+ info.userIds.add(tmp);
+ }
+ if (matcher.group(6).length() > 0) {
+ Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6));
+ while (matcher2.find()) {
+ String tmp = matcher2.group(1).replaceAll("<.*?>", "");
+ tmp = Html.fromHtml(tmp).toString();
+ info.userIds.add(tmp);
+ }
+ }
+ results.add(info);
+ }
+
+ return results;
+ }
+
+ @Override
+ public String get(long keyId) throws QueryException {
+ HttpClient client = new DefaultHttpClient();
+ try {
+ HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
+ + "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId));
+
+ HttpResponse response = client.execute(get);
+ if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+ throw new QueryException("not found");
+ }
+
+ HttpEntity entity = response.getEntity();
+ InputStream is = entity.getContent();
+ String data = readAll(is, EntityUtils.getContentCharSet(entity));
+ Matcher matcher = Apg.PGP_PUBLIC_KEY.matcher(data);
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ } catch (IOException e) {
+ // nothing to do, better luck on the next keyserver
+ } finally {
+ client.getConnectionManager().shutdown();
+ }
+
+ return null;
+ }
+
+ @Override
+ void add(String armouredText) throws AddKeyException {
+ HttpClient client = new DefaultHttpClient();
+ try {
+ HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add");
+
+ List nameValuePairs = new ArrayList(2);
+ nameValuePairs.add(new BasicNameValuePair("keytext", armouredText));
+ post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
+
+ HttpResponse response = client.execute(post);
+ if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
+ throw new AddKeyException();
+ }
+ } catch (IOException e) {
+ // nothing to do, better luck on the next keyserver
+ } finally {
+ client.getConnectionManager().shutdown();
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/IApgService.aidl b/org_apg/src/org/apg/IApgService.aidl
new file mode 100644
index 000000000..8101bd2a4
--- /dev/null
+++ b/org_apg/src/org/apg/IApgService.aidl
@@ -0,0 +1,125 @@
+package org.apg;
+
+interface IApgService {
+
+ /* All functions fill the returnVals Bundle with the following keys:
+ *
+ * ArrayList "WARNINGS" = Warnings, if any
+ * ArrayList "ERRORS" = Human readable error descriptions, if any
+ * int "ERROR" = Numeric representation of error, if any
+ * starting with 100:
+ * 100: Required argument missing
+ * 101: Generic failure of APG
+ * 102: No matching private key found
+ * 103: Private key's passphrase wrong
+ * 104: Private key's passphrase missing
+ */
+
+ /* ********************************************************
+ * Encryption
+ * ********************************************************/
+
+ /* All encryption function's arguments
+ *
+ * Bundle params' keys:
+ * (optional/required)
+ * TYPE "STRING KEY" = EXPLANATION / VALUES
+ *
+ * (required)
+ * String "MESSAGE" = Message to encrypt
+ * OR
+ * String "BLOB" = ContentUri to a file handle
+ * with binary data to encrypt
+ * (Attention: file will be overwritten
+ * with encrypted content!)
+ *
+ * (optional)
+ * int "ENCRYPTION_ALGORYTHM" = Encryption Algorithm
+ * 7: AES-128, 8: AES-192, 9: AES-256,
+ * 4: Blowfish, 10: Twofish, 3: CAST5,
+ * 6: DES, 2: Triple DES, 1: IDEA
+ * (optional)
+ * int "HASH_ALGORYTHM" = Hash Algorithm
+ * 1: MD5, 3: RIPEMD-160, 2: SHA-1,
+ * 11: SHA-224, 8: SHA-256, 9: SHA-384,
+ * 10: SHA-512
+ * (optional)
+ * Boolean "ARMORED_OUTPUT" = Armor output
+ *
+ * (optional)
+ * Boolean "FORCE_V3_SIGNATURE" = Force V3 Signatures
+ *
+ * (optional)
+ * int "COMPRESSION" = Compression to use
+ * 0x21070001: none, 1: Zip, 2: Zlib,
+ * 3: BZip2
+ * (optional)
+ * String "SIGNATURE_KEY" = Key to sign with
+ *
+ * (optional)
+ * String "PRIVATE_KEY_PASSPHRASE" = Passphrase for signing key
+ *
+ * Bundle returnVals (in addition to the ERRORS/WARNINGS above):
+ * If "MESSAGE" was set:
+ * String "RESULT" = Encrypted message
+ */
+
+ /* Additional argument for function below:
+ * (required)
+ * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase to use
+ */
+ boolean encryptWithPassphrase(in Bundle params, out Bundle returnVals);
+
+ /* Additional argument:
+ * (required)
+ * ArrayList "PUBLIC_KEYS" = Public keys (8char fingerprint "123ABC12" OR
+ * complete id "Alice Meyer ")
+ */
+ boolean encryptWithPublicKey(in Bundle params, out Bundle returnVals);
+
+ /* ********************************************************
+ * Decryption
+ * ********************************************************/
+
+ /* Bundle params:
+ * (required)
+ * String "MESSAGE" = Message to dencrypt
+ * OR
+ * String "BLOB" = ContentUri to a file handle
+ * with binary data to dencrypt
+ * (Attention: file will be overwritten
+ * with dencrypted content!)
+ *
+ * (optional)
+ * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase for decryption
+ *
+ * (optional)
+ * String "PRIVATE_KEY_PASSPHRASE" = Private keys's passphrase on asymmetric encryption
+ *
+ * Bundle return_vals:
+ * If "MESSAGE" was set:
+ * String "RESULT" = Decrypted message
+ */
+ boolean decrypt(in Bundle params, out Bundle returnVals);
+
+ /* ********************************************************
+ * Get key information
+ * ********************************************************/
+
+ /* Get info about all available keys
+ *
+ * Bundle params:
+ * (required)
+ * int "KEY_TYPE" = info about what type of keys to return
+ * 0: public keys
+ * 1: private keys
+ *
+ * Returns:
+ * StringArrayList "FINGERPRINTS" = Short fingerprints of keys
+ *
+ * StringArrayList "USER_IDS" = User ids of corresponding fingerprints
+ * (order is the same as in FINGERPRINTS)
+ */
+ boolean getKeys(in Bundle params, out Bundle returnVals);
+
+}
\ No newline at end of file
diff --git a/org_apg/src/org/apg/Id.java b/org_apg/src/org/apg/Id.java
new file mode 100644
index 000000000..adcad0534
--- /dev/null
+++ b/org_apg/src/org/apg/Id.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg;
+
+import org.spongycastle.bcpg.CompressionAlgorithmTags;
+
+public final class Id {
+
+ public static final String TAG = "APG";
+
+ 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 int update = 0x21070004;
+ public static final int exportToServer = 0x21070005;
+ public static final int share = 0x21070006;
+ public static final int signKey = 0x21070007;
+
+ 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 int search = 0x21070009;
+ public static final int help = 0x21070010;
+ public static final int key_server = 0x21070011;
+ public static final int scanQRCode = 0x21070012;
+ }
+ }
+
+ 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 int delete_done = 0x21070009;
+ public static final int query_done = 0x21070010;
+ public static final int unknown_signature_key = 0x21070011;
+ }
+
+ 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 int key_server_preference = 0x21070005;
+ public static final int look_up_key_id = 0x21070006;
+ public static final int export_to_server = 0x21070007;
+ public static final int import_from_qr_code = 0x21070008;
+ public static final int sign_key = 0x21070009;
+ }
+
+ 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 int deleting = 0x21070013;
+ public static final int help = 0x21070014;
+ public static final int querying = 0x21070015;
+ public static final int lookup_unknown_key = 0x21070016;
+ public static final int signing = 0x21070017;
+ }
+
+ public static final class task {
+ public static final int import_keys = 0x21070001;
+ public static final int export_keys = 0x21070002;
+ }
+
+ public static final class database {
+ public static final int type_public = 0;
+ public static final int type_secret = 1;
+ }
+
+ public static final class type {
+ public static final int public_key = 0x21070001;
+ public static final int secret_key = 0x21070002;
+ 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 action {
+ public static final int encrypt = 0x21070001;
+ public static final int decrypt = 0x21070002;
+ public static final int import_public = 0x21070003;
+ public static final int import_secret = 0x21070004;
+ }
+ }
+
+ 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 int bad = -3;
+ }
+
+ 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 mode {
+ public static final int undefined = 0x21070001;
+ public static final int byte_array = 0x21070002;
+ public static final int file = 0x21070003;
+ public static final int stream = 0x21070004;
+ }
+
+ public static final class key {
+ public static final int none = 0;
+ public static final int symmetric = -1;
+ }
+
+ public static final class content {
+ public static final int unknown = 0;
+ public static final int encrypted_data = 1;
+ public static final int keys = 2;
+ }
+
+ public static final class keyserver {
+ public static final int search = 0x21070001;
+ public static final int get = 0x21070002;
+ public static final int add = 0x21070003;
+ }
+}
diff --git a/org_apg/src/org/apg/InputData.java b/org_apg/src/org/apg/InputData.java
new file mode 100644
index 000000000..ae5b51fe4
--- /dev/null
+++ b/org_apg/src/org/apg/InputData.java
@@ -0,0 +1,25 @@
+package org.apg;
+
+import java.io.InputStream;
+
+public class InputData {
+ private PositionAwareInputStream mInputStream;
+ private long mSize;
+
+ public InputData(InputStream inputStream, long size) {
+ mInputStream = new PositionAwareInputStream(inputStream);
+ mSize = size;
+ }
+
+ public InputStream getInputStream() {
+ return mInputStream;
+ }
+
+ public long getSize() {
+ return mSize;
+ }
+
+ public long getStreamPosition() {
+ return mInputStream.position();
+ }
+}
diff --git a/org_apg/src/org/apg/KeyServer.java b/org_apg/src/org/apg/KeyServer.java
new file mode 100644
index 000000000..32158454f
--- /dev/null
+++ b/org_apg/src/org/apg/KeyServer.java
@@ -0,0 +1,46 @@
+package org.apg;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+import java.util.Vector;
+
+public abstract class KeyServer {
+ static public class QueryException extends Exception {
+ private static final long serialVersionUID = 2703768928624654512L;
+
+ public QueryException(String message) {
+ super(message);
+ }
+ }
+
+ static public class TooManyResponses extends Exception {
+ private static final long serialVersionUID = 2703768928624654513L;
+ }
+
+ static public class InsufficientQuery extends Exception {
+ private static final long serialVersionUID = 2703768928624654514L;
+ }
+
+ static public class AddKeyException extends Exception {
+ private static final long serialVersionUID = -507574859137295530L;
+ }
+
+ static public class KeyInfo implements Serializable {
+ private static final long serialVersionUID = -7797972113284992662L;
+ public Vector userIds;
+ public String revoked;
+ public Date date;
+ public String fingerPrint;
+ public long keyId;
+ public int size;
+ public String algorithm;
+ }
+
+ abstract List search(String query) throws QueryException, TooManyResponses,
+ InsufficientQuery;
+
+ abstract String get(long keyId) throws QueryException;
+
+ abstract void add(String armouredText) throws AddKeyException;
+}
diff --git a/org_apg/src/org/apg/PausableThread.java b/org_apg/src/org/apg/PausableThread.java
new file mode 100644
index 000000000..87258d36c
--- /dev/null
+++ b/org_apg/src/org/apg/PausableThread.java
@@ -0,0 +1,35 @@
+package org.apg;
+
+public class PausableThread extends Thread {
+ private boolean mPaused = false;
+
+ public PausableThread(Runnable runnable) {
+ super(runnable);
+ }
+
+ public void pause() {
+ synchronized (this) {
+ mPaused = true;
+ while (mPaused) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ public void unpause() {
+ synchronized (this) {
+ mPaused = false;
+ notify();
+ }
+ }
+
+ public boolean isPaused() {
+ synchronized (this) {
+ return mPaused;
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/PositionAwareInputStream.java b/org_apg/src/org/apg/PositionAwareInputStream.java
new file mode 100644
index 000000000..c59459670
--- /dev/null
+++ b/org_apg/src/org/apg/PositionAwareInputStream.java
@@ -0,0 +1,67 @@
+package org.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/org_apg/src/org/apg/Preferences.java b/org_apg/src/org/apg/Preferences.java
new file mode 100644
index 000000000..f4800e064
--- /dev/null
+++ b/org_apg/src/org/apg/Preferences.java
@@ -0,0 +1,170 @@
+package org.apg;
+
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.openpgp.PGPEncryptedData;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.util.Vector;
+
+public class Preferences {
+ private static Preferences mPreferences;
+ private SharedPreferences mSharedPreferences;
+
+ public static synchronized Preferences getPreferences(Context context) {
+ return getPreferences(context, false);
+ }
+
+ public static synchronized Preferences getPreferences(Context context, boolean force_new) {
+ if (mPreferences == null || force_new) {
+ mPreferences = new Preferences(context);
+ }
+ return mPreferences;
+ }
+
+ private Preferences(Context context) {
+ mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE);
+ }
+
+ public String getLanguage() {
+ return mSharedPreferences.getString(Constants.pref.LANGUAGE, "");
+ }
+
+ public void setLanguage(String value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putString(Constants.pref.LANGUAGE, value);
+ editor.commit();
+ }
+
+ public int getPassPhraseCacheTtl() {
+ int ttl = mSharedPreferences.getInt(Constants.pref.PASS_PHRASE_CACHE_TTL, 180);
+ // fix the value if it was set to "never" in previous versions, which currently is not
+ // supported
+ if (ttl == 0) {
+ ttl = 180;
+ }
+ return ttl;
+ }
+
+ public void setPassPhraseCacheTtl(int value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putInt(Constants.pref.PASS_PHRASE_CACHE_TTL, value);
+ editor.commit();
+ }
+
+ public int getDefaultEncryptionAlgorithm() {
+ return mSharedPreferences.getInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM,
+ PGPEncryptedData.AES_256);
+ }
+
+ public void setDefaultEncryptionAlgorithm(int value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, value);
+ editor.commit();
+ }
+
+ public int getDefaultHashAlgorithm() {
+ return mSharedPreferences.getInt(Constants.pref.DEFAULT_HASH_ALGORITHM,
+ HashAlgorithmTags.SHA256);
+ }
+
+ public void setDefaultHashAlgorithm(int value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putInt(Constants.pref.DEFAULT_HASH_ALGORITHM, value);
+ editor.commit();
+ }
+
+ public int getDefaultMessageCompression() {
+ return mSharedPreferences.getInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION,
+ Id.choice.compression.zlib);
+ }
+
+ public void setDefaultMessageCompression(int value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, value);
+ editor.commit();
+ }
+
+ public int getDefaultFileCompression() {
+ return mSharedPreferences.getInt(Constants.pref.DEFAULT_FILE_COMPRESSION,
+ Id.choice.compression.none);
+ }
+
+ public void setDefaultFileCompression(int value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putInt(Constants.pref.DEFAULT_FILE_COMPRESSION, value);
+ editor.commit();
+ }
+
+ public boolean getDefaultAsciiArmour() {
+ return mSharedPreferences.getBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, false);
+ }
+
+ public void setDefaultAsciiArmour(boolean value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, value);
+ editor.commit();
+ }
+
+ public boolean getForceV3Signatures() {
+ return mSharedPreferences.getBoolean(Constants.pref.FORCE_V3_SIGNATURES, false);
+ }
+
+ public void setForceV3Signatures(boolean value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Constants.pref.FORCE_V3_SIGNATURES, value);
+ editor.commit();
+ }
+
+ public boolean hasSeenChangeLog(String version) {
+ return mSharedPreferences.getBoolean(Constants.pref.HAS_SEEN_CHANGE_LOG + version, false);
+ }
+
+ public void setHasSeenChangeLog(String version, boolean value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Constants.pref.HAS_SEEN_CHANGE_LOG + version, value);
+ editor.commit();
+ }
+
+ public boolean hasSeenHelp() {
+ return mSharedPreferences.getBoolean(Constants.pref.HAS_SEEN_HELP, false);
+ }
+
+ public void setHasSeenHelp(boolean value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Constants.pref.HAS_SEEN_HELP, value);
+ editor.commit();
+ }
+
+ public String[] getKeyServers() {
+ String rawData = mSharedPreferences.getString(Constants.pref.KEY_SERVERS,
+ Constants.defaults.KEY_SERVERS);
+ Vector servers = new Vector();
+ String chunks[] = rawData.split(",");
+ for (int i = 0; i < chunks.length; ++i) {
+ String tmp = chunks[i].trim();
+ if (tmp.length() > 0) {
+ servers.add(tmp);
+ }
+ }
+ return servers.toArray(chunks);
+ }
+
+ public void setKeyServers(String[] value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ String rawData = "";
+ for (int i = 0; i < value.length; ++i) {
+ String tmp = value[i].trim();
+ if (tmp.length() == 0) {
+ continue;
+ }
+ if (!"".equals(rawData)) {
+ rawData += ",";
+ }
+ rawData += tmp;
+ }
+ editor.putString(Constants.pref.KEY_SERVERS, rawData);
+ editor.commit();
+ }
+}
diff --git a/org_apg/src/org/apg/Primes.java b/org_apg/src/org/apg/Primes.java
new file mode 100644
index 000000000..1fd6482b2
--- /dev/null
+++ b/org_apg/src/org/apg/Primes.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.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/org_apg/src/org/apg/ProgressDialogUpdater.java b/org_apg/src/org/apg/ProgressDialogUpdater.java
new file mode 100644
index 000000000..043cca906
--- /dev/null
+++ b/org_apg/src/org/apg/ProgressDialogUpdater.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.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/org_apg/src/org/apg/Service.java b/org_apg/src/org/apg/Service.java
new file mode 100644
index 000000000..24c4daa11
--- /dev/null
+++ b/org_apg/src/org/apg/Service.java
@@ -0,0 +1,78 @@
+package org.apg;
+
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+
+public class Service extends android.app.Service {
+ private final IBinder mBinder = new LocalBinder();
+
+ public static final String EXTRA_TTL = "ttl";
+
+ private int mPassPhraseCacheTtl = 15;
+ private Handler mCacheHandler = new Handler();
+ private Runnable mCacheTask = new Runnable() {
+ public void run() {
+ // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
+ // and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl
+ int delay = mPassPhraseCacheTtl * 1000 / 2;
+ // also make sure the delay is not longer than one minute
+ if (delay > 60000) {
+ delay = 60000;
+ }
+
+ delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay);
+ // don't check too often, even if we were close
+ if (delay < 5000) {
+ delay = 5000;
+ }
+
+ mCacheHandler.postDelayed(this, delay);
+ }
+ };
+
+ static private boolean mIsRunning = false;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mIsRunning = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mIsRunning = false;
+ }
+
+ @Override
+ public void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+
+ if (intent != null) {
+ mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
+ }
+ if (mPassPhraseCacheTtl < 15) {
+ mPassPhraseCacheTtl = 15;
+ }
+ mCacheHandler.removeCallbacks(mCacheTask);
+ mCacheHandler.postDelayed(mCacheTask, 1000);
+ }
+
+ static public boolean isRunning() {
+ return mIsRunning;
+ }
+
+ public class LocalBinder extends Binder {
+ Service getService() {
+ return Service.this;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/org_apg/src/org/apg/provider/Accounts.java b/org_apg/src/org/apg/provider/Accounts.java
new file mode 100644
index 000000000..b95f079df
--- /dev/null
+++ b/org_apg/src/org/apg/provider/Accounts.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.provider;
+
+import android.provider.BaseColumns;
+
+public class Accounts implements BaseColumns {
+ public static final String TABLE_NAME = "accounts";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String NAME = "c_name";
+ public static final String NAME_type = "TEXT";
+}
diff --git a/org_apg/src/org/apg/provider/ApgServiceBlobDatabase.java b/org_apg/src/org/apg/provider/ApgServiceBlobDatabase.java
new file mode 100644
index 000000000..70b9cd64f
--- /dev/null
+++ b/org_apg/src/org/apg/provider/ApgServiceBlobDatabase.java
@@ -0,0 +1,54 @@
+package org.apg.provider;
+
+import org.apg.ApgService;
+
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.util.Log;
+
+public class ApgServiceBlobDatabase extends SQLiteOpenHelper {
+
+ private static final String TAG = "ApgServiceBlobDatabase";
+
+ private static final int VERSION = 1;
+ private static final String NAME = "apg_service_blob_data";
+ private static final String TABLE = "data";
+
+ public ApgServiceBlobDatabase(Context context) {
+ super(context, NAME, null, VERSION);
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "constructor called");
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "onCreate() called");
+ db.execSQL("create table " + TABLE + " ( _id integer primary key autoincrement," +
+ "key text not null)");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "onUpgrade() called");
+ // no upgrade necessary yet
+ }
+
+ public Uri insert(ContentValues vals) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "insert() called");
+ SQLiteDatabase db = this.getWritableDatabase();
+ long newId = db.insert(TABLE, null, vals);
+ return ContentUris.withAppendedId(ApgServiceBlobProvider.CONTENT_URI, newId);
+ }
+
+ public Cursor query(String id, String key) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "query() called");
+ SQLiteDatabase db = this.getReadableDatabase();
+ return db.query(TABLE, new String[] {"_id"},
+ "_id = ? and key = ?", new String[] {id, key},
+ null, null, null);
+ }
+}
diff --git a/org_apg/src/org/apg/provider/ApgServiceBlobProvider.java b/org_apg/src/org/apg/provider/ApgServiceBlobProvider.java
new file mode 100644
index 000000000..f2d5377a4
--- /dev/null
+++ b/org_apg/src/org/apg/provider/ApgServiceBlobProvider.java
@@ -0,0 +1,138 @@
+package org.apg.provider;
+
+import org.apg.ApgService;
+import org.apg.Constants;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.UUID;
+
+public class ApgServiceBlobProvider extends ContentProvider {
+
+ private static final String TAG = "ApgServiceBlobProvider";
+
+ public static final Uri CONTENT_URI = Uri.parse("content://org.thialfihar.android.apg.provider.apgserviceblobprovider");
+
+ private static final String COLUMN_KEY = "key";
+
+ private static final String STORE_PATH = Constants.path.APP_DIR+"/ApgServiceBlobs";
+
+ private ApgServiceBlobDatabase mDb = null;
+
+ public ApgServiceBlobProvider() {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "Constructor called");
+ File dir = new File(STORE_PATH);
+ dir.mkdirs();
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "Constructor finished");
+ }
+
+ @Override
+ public int delete(Uri arg0, String arg1, String[] arg2) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "delete() called");
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public String getType(Uri arg0) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "getType() called");
+ // not needed for now
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues ignored) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "insert() called");
+ // ContentValues are actually ignored, because we want to store a blob with no more information
+ // but have to create an record with the password generated here first
+
+ ContentValues vals = new ContentValues();
+
+ // Insert a random key in the database. This has to provided by the caller when updating or
+ // getting the blob
+ String password = UUID.randomUUID().toString();
+ vals.put(COLUMN_KEY, password);
+
+ Uri insertedUri = mDb.insert(vals);
+ return Uri.withAppendedPath(insertedUri, password);
+ }
+
+ @Override
+ public boolean onCreate() {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "onCreate() called");
+ mDb = new ApgServiceBlobDatabase(getContext());
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "query() called");
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "update() called");
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, FileNotFoundException {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "openFile() called");
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... with uri: "+uri.toString());
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... with mode: "+mode);
+
+ List segments = uri.getPathSegments();
+ if(segments.size() < 2) {
+ throw new SecurityException("Password not found in URI");
+ }
+ String id = segments.get(0);
+ String key = segments.get(1);
+
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... got id: "+id);
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... and key: "+key);
+
+ // get the data
+ Cursor result = mDb.query(id, key);
+
+ if(result.getCount() == 0) {
+ // either the key is wrong or no id exists
+ throw new FileNotFoundException("No file found with that ID and/or password");
+ }
+
+ File targetFile = new File(STORE_PATH, id);
+ if(mode.equals("w")) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... will try to open file w");
+ if( !targetFile.exists() ) {
+ try {
+ targetFile.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "... got IEOException on creating new file", e);
+ throw new FileNotFoundException("Could not create file to write to");
+ }
+ }
+ return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE );
+ } else if(mode.equals("r")) {
+ if(ApgService.LOCAL_LOGD) Log.d(TAG, "... will try to open file r");
+ if( !targetFile.exists() ) {
+ throw new FileNotFoundException("Error: Could not find the file requested");
+ }
+ return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+
+ return null;
+ }
+
+}
diff --git a/org_apg/src/org/apg/provider/DataProvider.java b/org_apg/src/org/apg/provider/DataProvider.java
new file mode 100644
index 000000000..a97851e58
--- /dev/null
+++ b/org_apg/src/org/apg/provider/DataProvider.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.provider;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+
+import org.apg.Id;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+
+public class DataProvider extends ContentProvider {
+ public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
+
+ private static final int PUBLIC_KEY_RING = 101;
+ private static final int PUBLIC_KEY_RING_ID = 102;
+ private static final int PUBLIC_KEY_RING_BY_KEY_ID = 103;
+ private static final int PUBLIC_KEY_RING_BY_EMAILS = 104;
+ private static final int PUBLIC_KEY_RING_KEY = 111;
+ private static final int PUBLIC_KEY_RING_KEY_RANK = 112;
+ private static final int PUBLIC_KEY_RING_USER_ID = 121;
+ private static final int PUBLIC_KEY_RING_USER_ID_RANK = 122;
+
+ private static final int SECRET_KEY_RING = 201;
+ private static final int SECRET_KEY_RING_ID = 202;
+ private static final int SECRET_KEY_RING_BY_KEY_ID = 203;
+ private static final int SECRET_KEY_RING_BY_EMAILS = 204;
+ private static final int SECRET_KEY_RING_KEY = 211;
+ private static final int SECRET_KEY_RING_KEY_RANK = 212;
+ private static final int SECRET_KEY_RING_USER_ID = 221;
+ private static final int SECRET_KEY_RING_USER_ID_RANK = 222;
+
+ private static final int DATA_STREAM = 301;
+
+ private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring";
+ private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.public.key_ring";
+
+ private static final String PUBLIC_KEY_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key";
+ private static final String PUBLIC_KEY_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.public.key";
+
+ private static final String SECRET_KEY_RING_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key_ring";
+ private static final String SECRET_KEY_RING_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key_ring";
+
+ private static final String SECRET_KEY_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key";
+ private static final String SECRET_KEY_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key";
+
+ private static final String USER_ID_CONTENT_DIR_TYPE =
+ "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
+ private static final String USER_ID_CONTENT_ITEM_TYPE =
+ "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
+
+ public static final String _ID = "_id";
+ public static final String MASTER_KEY_ID = "master_key_id";
+ public static final String KEY_ID = "key_id";
+ public static final String USER_ID = "user_id";
+
+ private static final UriMatcher mUriMatcher;
+
+ private Database mDb;
+
+ static {
+ mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/emails/*", PUBLIC_KEY_RING_BY_EMAILS);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEY);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_ID);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RING);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/emails/*", SECRET_KEY_RING_BY_EMAILS);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEY);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_ID);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK);
+
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RING);
+ mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID);
+
+ mUriMatcher.addURI(AUTHORITY, "data/*", DATA_STREAM);
+ }
+
+ @Override
+ public boolean onCreate() {
+ mDb = new Database(getContext());
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ // TODO: implement the others, then use them for the lists
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ HashMap projectionMap = new HashMap();
+
+ int match = mUriMatcher.match(uri);
+ int type;
+ switch (match) {
+ case PUBLIC_KEY_RING:
+ case PUBLIC_KEY_RING_ID:
+ case PUBLIC_KEY_RING_BY_KEY_ID:
+ case PUBLIC_KEY_RING_BY_EMAILS:
+ case PUBLIC_KEY_RING_KEY:
+ case PUBLIC_KEY_RING_KEY_RANK:
+ case PUBLIC_KEY_RING_USER_ID:
+ case PUBLIC_KEY_RING_USER_ID_RANK:
+ type = Id.database.type_public;
+ break;
+
+ case SECRET_KEY_RING:
+ case SECRET_KEY_RING_ID:
+ case SECRET_KEY_RING_BY_KEY_ID:
+ case SECRET_KEY_RING_BY_EMAILS:
+ case SECRET_KEY_RING_KEY:
+ case SECRET_KEY_RING_KEY_RANK:
+ case SECRET_KEY_RING_USER_ID:
+ case SECRET_KEY_RING_USER_ID_RANK:
+ type = Id.database.type_secret;
+ break;
+
+ default: {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type);
+
+ switch (match) {
+ case PUBLIC_KEY_RING_ID:
+ case SECRET_KEY_RING_ID: {
+ qb.appendWhere(" AND " +
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+
+ // break omitted intentionally
+ }
+
+ case PUBLIC_KEY_RING:
+ case SECRET_KEY_RING: {
+ qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
+
+ projectionMap.put(_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings._ID);
+ projectionMap.put(MASTER_KEY_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
+ projectionMap.put(USER_ID,
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID);
+
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
+ }
+
+ break;
+ }
+
+ case SECRET_KEY_RING_BY_KEY_ID:
+ case PUBLIC_KEY_RING_BY_KEY_ID: {
+ qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " +
+ KeyRings.TABLE_NAME + " ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ "tmp." + Keys.KEY_RING_ID + ")" +
+ " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
+
+ projectionMap.put(_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings._ID);
+ projectionMap.put(MASTER_KEY_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
+ projectionMap.put(USER_ID,
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID);
+
+ qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(3));
+
+ break;
+ }
+
+ case SECRET_KEY_RING_BY_EMAILS:
+ case PUBLIC_KEY_RING_BY_EMAILS: {
+ qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
+
+ projectionMap.put(_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings._ID);
+ projectionMap.put(MASTER_KEY_ID,
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
+ projectionMap.put(USER_ID,
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID);
+
+ String emails = uri.getPathSegments().get(3);
+ String chunks[] = emails.split(" *, *");
+ boolean gotCondition = false;
+ String emailWhere = "";
+ for (int i = 0; i < chunks.length; ++i) {
+ if (chunks[i].length() == 0) {
+ continue;
+ }
+ if (i != 0) {
+ emailWhere += " OR ";
+ }
+ emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
+ // match '*', so it has to be at the *end* of the user id
+ emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
+ gotCondition = true;
+ }
+
+ if (gotCondition) {
+ qb.appendWhere(" AND EXISTS (SELECT tmp." + UserIds._ID +
+ " FROM " + UserIds.TABLE_NAME +
+ " AS tmp WHERE tmp." + UserIds.KEY_ID + " = " +
+ Keys.TABLE_NAME + "." + Keys._ID +
+ " AND (" + emailWhere + "))");
+ }
+
+ break;
+ }
+
+ default: {
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ qb.setProjectionMap(projectionMap);
+
+ // If no sort order is specified use the default
+ String orderBy;
+ if (TextUtils.isEmpty(sortOrder)) {
+ orderBy = null;
+ } else {
+ orderBy = sortOrder;
+ }
+
+ //System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n"));
+ Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy);
+
+ // Tell the cursor what uri to watch, so it knows when its source data changes
+ c.setNotificationUri(getContext().getContentResolver(), uri);
+ return c;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ switch (mUriMatcher.match(uri)) {
+ case PUBLIC_KEY_RING:
+ case PUBLIC_KEY_RING_BY_EMAILS:
+ return PUBLIC_KEY_RING_CONTENT_DIR_TYPE;
+
+ case PUBLIC_KEY_RING_ID:
+ return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
+
+ case PUBLIC_KEY_RING_BY_KEY_ID:
+ return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
+
+ case PUBLIC_KEY_RING_KEY:
+ return PUBLIC_KEY_CONTENT_DIR_TYPE;
+
+ case PUBLIC_KEY_RING_KEY_RANK:
+ return PUBLIC_KEY_CONTENT_ITEM_TYPE;
+
+ case PUBLIC_KEY_RING_USER_ID:
+ return USER_ID_CONTENT_DIR_TYPE;
+
+ case PUBLIC_KEY_RING_USER_ID_RANK:
+ return USER_ID_CONTENT_ITEM_TYPE;
+
+ case SECRET_KEY_RING:
+ case SECRET_KEY_RING_BY_EMAILS:
+ return SECRET_KEY_RING_CONTENT_DIR_TYPE;
+
+ case SECRET_KEY_RING_ID:
+ return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
+
+ case SECRET_KEY_RING_BY_KEY_ID:
+ return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
+
+ case SECRET_KEY_RING_KEY:
+ return SECRET_KEY_CONTENT_DIR_TYPE;
+
+ case SECRET_KEY_RING_KEY_RANK:
+ return SECRET_KEY_CONTENT_ITEM_TYPE;
+
+ case SECRET_KEY_RING_USER_ID:
+ return USER_ID_CONTENT_DIR_TYPE;
+
+ case SECRET_KEY_RING_USER_ID_RANK:
+ return USER_ID_CONTENT_ITEM_TYPE;
+
+ default:
+ throw new IllegalArgumentException("Unknown URI " + uri);
+ }
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues initialValues) {
+ // not supported
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String where, String[] whereArgs) {
+ // not supported
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
+ // not supported
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ int match = mUriMatcher.match(uri);
+ if (match != DATA_STREAM) {
+ throw new FileNotFoundException();
+ }
+ String fileName = uri.getPathSegments().get(1);
+ File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName);
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+}
diff --git a/org_apg/src/org/apg/provider/Database.java b/org_apg/src/org/apg/provider/Database.java
new file mode 100644
index 000000000..8b1aaa9e7
--- /dev/null
+++ b/org_apg/src/org/apg/provider/Database.java
@@ -0,0 +1,605 @@
+package org.apg.provider;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.util.IterableIterator;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Vector;
+
+public class Database extends SQLiteOpenHelper {
+ public static class GeneralException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public GeneralException(String message) {
+ super(message);
+ }
+ }
+
+ private static final String DATABASE_NAME = "apg";
+ private static final int DATABASE_VERSION = 2;
+
+ public static final String AUTHORITY = "org.thialfihar.android.apg.database";
+
+ public static HashMap sKeyRingsProjection;
+ public static HashMap sKeysProjection;
+ public static HashMap sUserIdsProjection;
+
+ private SQLiteDatabase mDb = null;
+ private int mStatus = 0;
+
+ static {
+ sKeyRingsProjection = new HashMap();
+ sKeyRingsProjection.put(KeyRings._ID, KeyRings._ID);
+ sKeyRingsProjection.put(KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID);
+ sKeyRingsProjection.put(KeyRings.TYPE, KeyRings.TYPE);
+ sKeyRingsProjection.put(KeyRings.WHO_ID, KeyRings.WHO_ID);
+ sKeyRingsProjection.put(KeyRings.KEY_RING_DATA, KeyRings.KEY_RING_DATA);
+
+ sKeysProjection = new HashMap();
+ sKeysProjection.put(Keys._ID, Keys._ID);
+ sKeysProjection.put(Keys.KEY_ID, Keys.KEY_ID);
+ sKeysProjection.put(Keys.TYPE, Keys.TYPE);
+ sKeysProjection.put(Keys.IS_MASTER_KEY, Keys.IS_MASTER_KEY);
+ sKeysProjection.put(Keys.ALGORITHM, Keys.ALGORITHM);
+ sKeysProjection.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
+ sKeysProjection.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
+ sKeysProjection.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
+ sKeysProjection.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
+ sKeysProjection.put(Keys.CREATION, Keys.CREATION);
+ sKeysProjection.put(Keys.EXPIRY, Keys.EXPIRY);
+ sKeysProjection.put(Keys.KEY_DATA, Keys.KEY_DATA);
+ sKeysProjection.put(Keys.RANK, Keys.RANK);
+
+ sUserIdsProjection = new HashMap();
+ sUserIdsProjection.put(UserIds._ID, UserIds._ID);
+ sUserIdsProjection.put(UserIds.KEY_ID, UserIds.KEY_ID);
+ sUserIdsProjection.put(UserIds.USER_ID, UserIds.USER_ID);
+ sUserIdsProjection.put(UserIds.RANK, UserIds.RANK);
+ }
+
+ public Database(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ // force upgrade to test things
+ //onUpgrade(getWritableDatabase(), 1, 2);
+ mDb = getWritableDatabase();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ mDb.close();
+ super.finalize();
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
+ KeyRings._ID + " " + KeyRings._ID_type + "," +
+ KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
+ KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
+ KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
+ KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
+
+ db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
+ Keys._ID + " " + Keys._ID_type + "," +
+ Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
+ Keys.TYPE + " " + Keys.TYPE_type + ", " +
+ Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
+ Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
+ Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
+ Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
+ Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
+ Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
+ Keys.CREATION + " " + Keys.CREATION_type + ", " +
+ Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
+ Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
+ Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
+ Keys.RANK + " " + Keys.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
+ UserIds._ID + " " + UserIds._ID_type + "," +
+ UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
+ UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
+ UserIds.RANK + " " + UserIds.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
+ Accounts._ID + " " + Accounts._ID_type + "," +
+ Accounts.NAME + " " + Accounts.NAME_type + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ mDb = db;
+ for (int version = oldVersion; version < newVersion; ++version) {
+ switch (version) {
+ case 1: { // upgrade 1 to 2
+ db.execSQL("DROP TABLE IF EXISTS " + KeyRings.TABLE_NAME + ";");
+ db.execSQL("DROP TABLE IF EXISTS " + Keys.TABLE_NAME + ";");
+ db.execSQL("DROP TABLE IF EXISTS " + UserIds.TABLE_NAME + ";");
+
+ db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
+ KeyRings._ID + " " + KeyRings._ID_type + "," +
+ KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
+ KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
+ KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
+ KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
+
+ db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
+ Keys._ID + " " + Keys._ID_type + "," +
+ Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
+ Keys.TYPE + " " + Keys.TYPE_type + ", " +
+ Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
+ Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
+ Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
+ Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
+ Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
+ Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
+ Keys.CREATION + " " + Keys.CREATION_type + ", " +
+ Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
+ Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
+ Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
+ Keys.RANK + " " + Keys.RANK_type + ");");
+
+ db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
+ UserIds._ID + " " + UserIds._ID_type + "," +
+ UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
+ UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
+ UserIds.RANK + " " + UserIds.RANK_type + ");");
+
+ Cursor cursor = db.query("public_keys", new String[] { "c_key_data" },
+ null, null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ byte[] data = cursor.getBlob(0);
+ try {
+ PGPPublicKeyRing keyRing = new PGPPublicKeyRing(data);
+ saveKeyRing(keyRing);
+ } catch (IOException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (GeneralException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ }
+ } while (cursor.moveToNext());
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ cursor = db.query("secret_keys", new String[]{ "c_key_data" },
+ null, null, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ byte[] data = cursor.getBlob(0);
+ try {
+ PGPSecretKeyRing keyRing = new PGPSecretKeyRing(data);
+ saveKeyRing(keyRing);
+ } catch (IOException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (PGPException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ } catch (GeneralException e) {
+ Log.e("apg.db.upgrade", "key import failed: " + e);
+ }
+ } while (cursor.moveToNext());
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ db.execSQL("DROP TABLE IF EXISTS public_keys;");
+ db.execSQL("DROP TABLE IF EXISTS secret_keys;");
+
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+ mDb = null;
+ }
+
+ public int saveKeyRing(PGPPublicKeyRing keyRing) throws IOException, GeneralException {
+ mDb.beginTransaction();
+ ContentValues values = new ContentValues();
+ PGPPublicKey masterKey = keyRing.getPublicKey();
+ long masterKeyId = masterKey.getKeyID();
+
+ values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRings.TYPE, Id.database.type_public);
+ values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+
+ long rowId = insertOrUpdateKeyRing(values);
+ int returnValue = mStatus;
+
+ if (rowId == -1) {
+ throw new GeneralException("saving public key ring " + masterKeyId + " failed");
+ }
+
+ Vector seenIds = new Vector();
+ int rank = 0;
+ for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) {
+ seenIds.add(saveKey(rowId, key, rank));
+ ++rank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(Keys.TABLE_NAME,
+ Keys.KEY_RING_ID + " = ? AND " +
+ Keys._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ return returnValue;
+ }
+
+ public int saveKeyRing(PGPSecretKeyRing keyRing) throws IOException, GeneralException {
+ mDb.beginTransaction();
+ ContentValues values = new ContentValues();
+ PGPSecretKey masterKey = keyRing.getSecretKey();
+ long masterKeyId = masterKey.getKeyID();
+
+ values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRings.TYPE, Id.database.type_secret);
+ values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+
+ long rowId = insertOrUpdateKeyRing(values);
+ int returnValue = mStatus;
+
+ if (rowId == -1) {
+ throw new GeneralException("saving secret key ring " + masterKeyId + " failed");
+ }
+
+ Vector seenIds = new Vector();
+ int rank = 0;
+ for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) {
+ seenIds.add(saveKey(rowId, key, rank));
+ ++rank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(Keys.TABLE_NAME,
+ Keys.KEY_RING_ID + " = ? AND " +
+ Keys._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ return returnValue;
+ }
+
+ private int saveKey(long keyRingId, PGPPublicKey key, int rank)
+ throws IOException, GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(Keys.KEY_ID, key.getKeyID());
+ values.put(Keys.TYPE, Id.database.type_public);
+ values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
+ values.put(Keys.ALGORITHM, key.getAlgorithm());
+ values.put(Keys.KEY_SIZE, key.getBitStrength());
+ values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
+ values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
+ values.put(Keys.IS_REVOKED, key.isRevoked());
+ values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
+ Date expiryDate = Apg.getExpiryDate(key);
+ if (expiryDate != null) {
+ values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
+ }
+ values.put(Keys.KEY_RING_ID, keyRingId);
+ values.put(Keys.KEY_DATA, key.getEncoded());
+ values.put(Keys.RANK, rank);
+
+ long rowId = insertOrUpdateKey(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving public key " + key.getKeyID() + " failed");
+ }
+
+ Vector seenIds = new Vector();
+ int userIdRank = 0;
+ for (String userId : new IterableIterator(key.getUserIDs())) {
+ seenIds.add(saveUserId(rowId, userId, userIdRank));
+ ++userIdRank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ? AND " +
+ UserIds._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ return (int)rowId;
+ }
+
+ private int saveKey(long keyRingId, PGPSecretKey key, int rank)
+ throws IOException, GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(Keys.KEY_ID, key.getPublicKey().getKeyID());
+ values.put(Keys.TYPE, Id.database.type_secret);
+ values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
+ values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
+ values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
+ values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
+ values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
+ values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
+ values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
+ Date expiryDate = Apg.getExpiryDate(key);
+ if (expiryDate != null) {
+ values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
+ }
+ values.put(Keys.KEY_RING_ID, keyRingId);
+ values.put(Keys.KEY_DATA, key.getEncoded());
+ values.put(Keys.RANK, rank);
+
+ long rowId = insertOrUpdateKey(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving secret key " + key.getPublicKey().getKeyID() + " failed");
+ }
+
+ Vector seenIds = new Vector();
+ int userIdRank = 0;
+ for (String userId : new IterableIterator(key.getUserIDs())) {
+ seenIds.add(saveUserId(rowId, userId, userIdRank));
+ ++userIdRank;
+ }
+
+ String seenIdsStr = "";
+ for (Integer id : seenIds) {
+ if (seenIdsStr.length() > 0) {
+ seenIdsStr += ",";
+ }
+ seenIdsStr += id;
+ }
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ? AND " +
+ UserIds._ID + " NOT IN (" + seenIdsStr + ")",
+ new String[] { "" + rowId });
+
+ return (int)rowId;
+ }
+
+ private int saveUserId(long keyId, String userId, int rank) throws GeneralException {
+ ContentValues values = new ContentValues();
+
+ values.put(UserIds.KEY_ID, keyId);
+ values.put(UserIds.USER_ID, userId);
+ values.put(UserIds.RANK, rank);
+
+ long rowId = insertOrUpdateUserId(values);
+
+ if (rowId == -1) {
+ throw new GeneralException("saving user id " + userId + " failed");
+ }
+
+ return (int)rowId;
+ }
+
+ private long insertOrUpdateKeyRing(ContentValues values) {
+ Cursor c = mDb.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID },
+ KeyRings.MASTER_KEY_ID + " = ? AND " + KeyRings.TYPE + " = ?",
+ new String[] {
+ values.getAsString(KeyRings.MASTER_KEY_ID),
+ values.getAsString(KeyRings.TYPE),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(KeyRings.TABLE_NAME, values,
+ KeyRings._ID + " = ?", new String[] { "" + rowId });
+ mStatus = Id.return_value.updated;
+ } else {
+ rowId = mDb.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values);
+ mStatus = Id.return_value.ok;
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ private long insertOrUpdateKey(ContentValues values) {
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
+ Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
+ new String[] {
+ values.getAsString(Keys.KEY_ID),
+ values.getAsString(Keys.TYPE),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(Keys.TABLE_NAME, values,
+ Keys._ID + " = ?", new String[] { "" + rowId });
+ } else {
+ rowId = mDb.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ private long insertOrUpdateUserId(ContentValues values) {
+ Cursor c = mDb.query(UserIds.TABLE_NAME, new String[] { UserIds._ID },
+ UserIds.KEY_ID + " = ? AND " + UserIds.USER_ID + " = ?",
+ new String[] {
+ values.getAsString(UserIds.KEY_ID),
+ values.getAsString(UserIds.USER_ID),
+ },
+ null, null, null);
+ long rowId = -1;
+ if (c != null && c.moveToFirst()) {
+ rowId = c.getLong(0);
+ mDb.update(UserIds.TABLE_NAME, values,
+ UserIds._ID + " = ?", new String[] { "" + rowId });
+ } else {
+ rowId = mDb.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return rowId;
+ }
+
+ public Object getKeyRing(int keyRingId) {
+ Cursor c = mDb.query(KeyRings.TABLE_NAME,
+ new String[] { KeyRings.KEY_RING_DATA, KeyRings.TYPE },
+ KeyRings._ID + " = ?",
+ new String[] {
+ "" + keyRingId,
+ },
+ null, null, null);
+ byte[] data = null;
+ Object keyRing = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ if (data != null) {
+ try {
+ if (c.getInt(1) == Id.database.type_public) {
+ keyRing = new PGPPublicKeyRing(data);
+ } else {
+ keyRing = new PGPSecretKeyRing(data);
+ }
+ } catch (IOException e) {
+ // can't load it, then
+ } catch (PGPException e) {
+ // can't load it, then
+ }
+ }
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return keyRing;
+ }
+
+ public byte[] getKeyRingDataFromKeyId(int type, long keyId) {
+ Cursor c = mDb.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ")",
+ new String[] { KeyRings.TABLE_NAME + "." + KeyRings.KEY_RING_DATA },
+ Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+
+ byte[] data = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return data;
+ }
+
+ public byte[] getKeyDataFromKeyId(int type, long keyId) {
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys.KEY_DATA },
+ Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
+ new String[] {
+ "" + keyId,
+ "" + type,
+ },
+ null, null, null);
+ byte[] data = null;
+ if (c != null && c.moveToFirst()) {
+ data = c.getBlob(0);
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ return data;
+ }
+
+ public void deleteKeyRing(int keyRingId) {
+ mDb.beginTransaction();
+ mDb.delete(KeyRings.TABLE_NAME,
+ KeyRings._ID + " = ?", new String[] { "" + keyRingId });
+
+ Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
+ Keys.KEY_RING_ID + " = ?",
+ new String[] {
+ "" + keyRingId,
+ },
+ null, null, null);
+ if (c != null && c.moveToFirst()) {
+ do {
+ int keyId = c.getInt(0);
+ deleteKey(keyId);
+ } while (c.moveToNext());
+ }
+
+ if (c != null) {
+ c.close();
+ }
+
+ mDb.setTransactionSuccessful();
+ mDb.endTransaction();
+ }
+
+ private void deleteKey(int keyId) {
+ mDb.delete(Keys.TABLE_NAME,
+ Keys._ID + " = ?", new String[] { "" + keyId });
+
+ mDb.delete(UserIds.TABLE_NAME,
+ UserIds.KEY_ID + " = ?", new String[] { "" + keyId });
+ }
+
+ public SQLiteDatabase db() {
+ return mDb;
+ }
+}
diff --git a/org_apg/src/org/apg/provider/KeyRings.java b/org_apg/src/org/apg/provider/KeyRings.java
new file mode 100644
index 000000000..304afd24f
--- /dev/null
+++ b/org_apg/src/org/apg/provider/KeyRings.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.provider;
+
+import android.provider.BaseColumns;
+
+public class KeyRings implements BaseColumns {
+ public static final String TABLE_NAME = "key_rings";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String MASTER_KEY_ID = "c_master_key_id";
+ public static final String MASTER_KEY_ID_type = "INT64";
+ public static final String TYPE = "c_type";
+ public static final String TYPE_type = "INTEGER";
+ public static final String WHO_ID = "c_who_id";
+ public static final String WHO_ID_type = "INTEGER";
+ public static final String KEY_RING_DATA = "c_key_ring_data";
+ public static final String KEY_RING_DATA_type = "BLOB";
+}
diff --git a/org_apg/src/org/apg/provider/Keys.java b/org_apg/src/org/apg/provider/Keys.java
new file mode 100644
index 000000000..63eaee54f
--- /dev/null
+++ b/org_apg/src/org/apg/provider/Keys.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.provider;
+
+import android.provider.BaseColumns;
+
+public class Keys implements BaseColumns {
+ public static final String TABLE_NAME = "keys";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String KEY_ID = "c_key_id";
+ public static final String KEY_ID_type = "INT64";
+ public static final String TYPE = "c_type";
+ public static final String TYPE_type = "INTEGER";
+ public static final String IS_MASTER_KEY = "c_is_master_key";
+ public static final String IS_MASTER_KEY_type = "INTEGER";
+ public static final String ALGORITHM = "c_algorithm";
+ public static final String ALGORITHM_type = "INTEGER";
+ public static final String KEY_SIZE = "c_key_size";
+ public static final String KEY_SIZE_type = "INTEGER";
+ public static final String CAN_SIGN = "c_can_sign";
+ public static final String CAN_SIGN_type = "INTEGER";
+ public static final String CAN_ENCRYPT = "c_can_encrypt";
+ public static final String CAN_ENCRYPT_type = "INTEGER";
+ public static final String IS_REVOKED = "c_is_revoked";
+ public static final String IS_REVOKED_type = "INTEGER";
+ public static final String CREATION = "c_creation";
+ public static final String CREATION_type = "INTEGER";
+ public static final String EXPIRY = "c_expiry";
+ public static final String EXPIRY_type = "INTEGER";
+ public static final String KEY_RING_ID = "c_key_ring_id";
+ public static final String KEY_RING_ID_type = "INTEGER";
+ public static final String KEY_DATA = "c_key_data";
+ public static final String KEY_DATA_type = "BLOB";
+ public static final String RANK = "c_key_data";
+ public static final String RANK_type = "INTEGER";
+}
diff --git a/org_apg/src/org/apg/provider/UserIds.java b/org_apg/src/org/apg/provider/UserIds.java
new file mode 100644
index 000000000..e8ddc677d
--- /dev/null
+++ b/org_apg/src/org/apg/provider/UserIds.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.provider;
+
+import android.provider.BaseColumns;
+
+public class UserIds implements BaseColumns {
+ public static final String TABLE_NAME = "user_ids";
+
+ public static final String _ID_type = "INTEGER PRIMARY KEY";
+ public static final String KEY_ID = "c_key_id";
+ public static final String KEY_ID_type = "INTEGER";
+ public static final String USER_ID = "c_user_id";
+ public static final String USER_ID_type = "TEXT";
+ public static final String RANK = "c_rank";
+ public static final String RANK_type = "INTEGER";
+}
diff --git a/org_apg/src/org/apg/ui/AboutActivity.java b/org_apg/src/org/apg/ui/AboutActivity.java
new file mode 100644
index 000000000..308a1e06e
--- /dev/null
+++ b/org_apg/src/org/apg/ui/AboutActivity.java
@@ -0,0 +1,51 @@
+package org.apg.ui;
+
+import org.apg.Constants;
+import org.apg.R;
+
+import android.app.Activity;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+public class AboutActivity extends Activity {
+ Activity mActivity;
+
+ /**
+ * Instantiate View for this Activity
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.about_activity);
+
+ mActivity = this;
+
+ TextView versionText = (TextView) findViewById(R.id.about_version);
+ versionText.setText(getString(R.string.about_version) + " " + getVersion());
+ }
+
+ /**
+ * Get the current package version.
+ *
+ * @return The current version.
+ */
+ private String getVersion() {
+ String result = "";
+ try {
+ PackageManager manager = mActivity.getPackageManager();
+ PackageInfo info = manager.getPackageInfo(mActivity.getPackageName(), 0);
+
+ result = String.format("%s (%s)", info.versionName, info.versionCode);
+ } catch (NameNotFoundException e) {
+ Log.w(Constants.TAG, "Unable to get application version: " + e.getMessage());
+ result = "Unable to get application version.";
+ }
+
+ return result;
+ }
+}
diff --git a/org_apg/src/org/apg/ui/BaseActivity.java b/org_apg/src/org/apg/ui/BaseActivity.java
new file mode 100644
index 000000000..9b5039a5d
--- /dev/null
+++ b/org_apg/src/org/apg/ui/BaseActivity.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Locale;
+
+import org.apg.Apg;
+import org.apg.AskForSecretKeyPassPhrase;
+import org.apg.Constants;
+import org.apg.Id;
+import org.apg.PausableThread;
+import org.apg.Preferences;
+import org.apg.ProgressDialogUpdater;
+import org.apg.Service;
+import org.apg.R;
+
+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.res.Configuration;
+import android.os.Bundle;
+import android.os.Environment;
+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 PausableThread mRunningThread = null;
+ private Thread mDeletingThread = null;
+
+ private long mSecretKeyId = 0;
+ private String mDeleteFile = null;
+
+ protected Preferences mPreferences;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ handlerCallback(msg);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mPreferences = Preferences.getPreferences(this);
+ setLanguage(this, mPreferences.getLanguage());
+
+ Apg.initialize(this);
+
+ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ File dir = new File(Constants.path.APP_DIR);
+ if (!dir.exists() && !dir.mkdirs()) {
+ // ignore this for now, it's not crucial
+ // that the directory doesn't exist at this point
+ }
+ }
+
+ startCacheService(this, mPreferences);
+ }
+
+ public static void startCacheService(Activity activity, Preferences preferences) {
+ Intent intent = new Intent(activity, Service.class);
+ intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
+ activity.startService(intent);
+ }
+
+ @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: {
+ startActivity(new Intent(this, AboutActivity.class));
+ return true;
+ }
+
+ case Id.menu.option.preferences: {
+ startActivity(new Intent(this, PreferencesActivity.class));
+ return true;
+ }
+
+ case Id.menu.option.search: {
+ startSearch("", false, null, false);
+ 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;
+ }
+
+ case Id.dialog.deleting: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
+ return mProgressDialog;
+ }
+
+ case Id.dialog.querying: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_querying));
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialog.setCancelable(false);
+ return mProgressDialog;
+ }
+
+ case Id.dialog.signing: {
+ mProgressDialog.setMessage(this.getString(R.string.progress_signing));
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialog.setCancelable(false);
+ return mProgressDialog;
+ }
+
+ default: {
+ break;
+ }
+ }
+ mProgressDialog = null;
+
+ switch (id) {
+
+ 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);
+ final File file = new File(getDeleteFile());
+ showDialog(Id.dialog.deleting);
+ mDeletingThread = new Thread(new Runnable() {
+ public void run() {
+ Bundle data = new Bundle();
+ data.putInt(Constants.extras.STATUS, Id.message.delete_done);
+ try {
+ Apg.deleteFileSecurely(BaseActivity.this, file, BaseActivity.this);
+ } catch (FileNotFoundException e) {
+ data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
+ R.string.error_fileNotFound, file));
+ } catch (IOException e) {
+ data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
+ R.string.error_fileDeleteFailed, file));
+ }
+ Message msg = new Message();
+ msg.setData(data);
+ sendMessage(msg);
+ }
+ });
+ mDeletingThread.start();
+ }
+ });
+ 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(Apg.EXTRA_KEY_ID));
+ } 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(Constants.extras.STATUS, Id.message.progress_update);
+ data.putInt(Constants.extras.PROGRESS, progress);
+ data.putInt(Constants.extras.PROGRESS_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(Constants.extras.STATUS, Id.message.progress_update);
+ data.putString(Constants.extras.MESSAGE, message);
+ data.putInt(Constants.extras.PROGRESS, progress);
+ data.putInt(Constants.extras.PROGRESS_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(Constants.extras.STATUS);
+ switch (type) {
+ case Id.message.progress_update: {
+ String message = data.getString(Constants.extras.MESSAGE);
+ if (mProgressDialog != null) {
+ if (message != null) {
+ mProgressDialog.setMessage(message);
+ }
+ mProgressDialog.setMax(data.getInt(Constants.extras.PROGRESS_MAX));
+ mProgressDialog.setProgress(data.getInt(Constants.extras.PROGRESS));
+ }
+ break;
+ }
+
+ case Id.message.delete_done: {
+ mProgressDialog = null;
+ deleteDoneCallback(msg);
+ break;
+ }
+
+ case Id.message.import_done: // intentionally no break
+ case Id.message.export_done: // intentionally no break
+ case Id.message.query_done: // intentionally no break
+ case Id.message.done: {
+ mProgressDialog = null;
+ doneCallback(msg);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+
+ public void doneCallback(Message msg) {
+
+ }
+
+ public void deleteDoneCallback(Message msg) {
+ removeDialog(Id.dialog.deleting);
+ mDeletingThread = null;
+
+ Bundle data = msg.getData();
+ String error = data.getString(Apg.EXTRA_ERROR);
+ String message;
+ if (error != null) {
+ message = getString(R.string.errorMessage, error);
+ } else {
+ message = getString(R.string.fileDeleteSuccessful);
+ }
+
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+ }
+
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ Apg.setCachedPassPhrase(keyId, passPhrase);
+ }
+
+ public void sendMessage(Message msg) {
+ mHandler.sendMessage(msg);
+ }
+
+ public PausableThread getRunningThread() {
+ return mRunningThread;
+ }
+
+ public void startThread() {
+ mRunningThread = new PausableThread(this);
+ mRunningThread.start();
+ }
+
+ public void run() {
+
+ }
+
+ public void setSecretKeyId(long id) {
+ mSecretKeyId = id;
+ }
+
+ public long getSecretKeyId() {
+ return mSecretKeyId;
+ }
+
+ protected void setDeleteFile(String deleteFile) {
+ mDeleteFile = deleteFile;
+ }
+
+ protected String getDeleteFile() {
+ return mDeleteFile;
+ }
+
+ public static void setLanguage(Context context, String language) {
+ Locale locale;
+ if (language == null || language.equals("")) {
+ locale = Locale.getDefault();
+ } else {
+ locale = new Locale(language);
+ }
+ Configuration config = new Configuration();
+ config.locale = locale;
+ context.getResources().updateConfiguration(config,
+ context.getResources().getDisplayMetrics());
+ }
+}
diff --git a/org_apg/src/org/apg/ui/DecryptActivity.java b/org_apg/src/org/apg/ui/DecryptActivity.java
new file mode 100644
index 000000000..cb58dfb09
--- /dev/null
+++ b/org_apg/src/org/apg/ui/DecryptActivity.java
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.DataDestination;
+import org.apg.DataSource;
+import org.apg.FileDialog;
+import org.apg.Id;
+import org.apg.InputData;
+import org.apg.PausableThread;
+import org.apg.provider.DataProvider;
+import org.apg.util.Compatibility;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.apg.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+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;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+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;
+
+public class DecryptActivity extends BaseActivity {
+ private long mSignatureKeyId = 0;
+
+ private Intent mIntent;
+
+ private boolean mReturnResult = false;
+ private String mReplyTo = null;
+ private String mSubject = null;
+ private boolean mSignedOnly = false;
+ 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;
+
+ private Uri mContentUri = null;
+ private byte[] mData = null;
+ private boolean mReturnBinary = false;
+
+ private DataSource mDataSource = null;
+ private DataDestination mDataDestination = null;
+
+ private long mUnknownSignatureKeyId = 0;
+
+ @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() {
+ 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() {
+ 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() {
+ 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();
+ }
+
+ mIntent = getIntent();
+ if (Intent.ACTION_VIEW.equals(mIntent.getAction())) {
+ Uri uri = mIntent.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 = new String(byteOut.toByteArray());
+ mMessage.setText(data);
+ } catch (FileNotFoundException e) {
+ // ignore, then
+ } catch (IOException e) {
+ // ignore, then
+ }
+ } else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) {
+ Log.d(Constants.TAG, "Apg Intent DECRYPT startet");
+ Bundle extras = mIntent.getExtras();
+ if (extras == null) {
+ Log.d(Constants.TAG, "extra bundle was null");
+ extras = new Bundle();
+ } else {
+ Log.d(Constants.TAG, "got extras");
+ }
+
+ mData = extras.getByteArray(Apg.EXTRA_DATA);
+ String textData = null;
+ if (mData == null) {
+ Log.d(Constants.TAG, "EXTRA_DATA was null");
+ textData = extras.getString(Apg.EXTRA_TEXT);
+ } else {
+ Log.d(Constants.TAG, "Got data from EXTRA_DATA");
+ }
+ if (textData != null) {
+ Log.d(Constants.TAG, "textData null, matching text ...");
+ Matcher matcher = Apg.PGP_MESSAGE.matcher(textData);
+ if (matcher.matches()) {
+ Log.d(Constants.TAG, "PGP_MESSAGE matched");
+ textData = matcher.group(1);
+ // replace non breakable spaces
+ textData = textData.replaceAll("\\xa0", " ");
+ mMessage.setText(textData);
+ } else {
+ matcher = Apg.PGP_SIGNED_MESSAGE.matcher(textData);
+ if (matcher.matches()) {
+ Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
+ textData = matcher.group(1);
+ // replace non breakable spaces
+ textData = textData.replaceAll("\\xa0", " ");
+ mMessage.setText(textData);
+ mDecryptButton.setText(R.string.btn_verify);
+ } else {
+ Log.d(Constants.TAG, "Nothing matched!");
+ }
+ }
+ }
+ mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO);
+ mSubject = extras.getString(Apg.EXTRA_SUBJECT);
+ } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) {
+ mInputFilename = mIntent.getDataString();
+ if ("file".equals(mIntent.getScheme())) {
+ mInputFilename = Uri.decode(mInputFilename.substring(7));
+ }
+ mFilename.setText(mInputFilename);
+ guessOutputFilename();
+ mSource.setInAnimation(null);
+ mSource.setOutAnimation(null);
+ while (mSource.getCurrentView().getId() != R.id.sourceFile) {
+ mSource.showNext();
+ }
+ } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) {
+ mContentUri = mIntent.getData();
+ Bundle extras = mIntent.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+
+ mReturnBinary = extras.getBoolean(Apg.EXTRA_BINARY, false);
+
+ if (mContentUri == null) {
+ mData = extras.getByteArray(Apg.EXTRA_DATA);
+ String data = extras.getString(Apg.EXTRA_TEXT);
+ if (data != null) {
+ Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ // replace non breakable spaces
+ data = data.replaceAll("\\xa0", " ");
+ mMessage.setText(data);
+ } else {
+ matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data);
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ // replace non breakable spaces
+ data = data.replaceAll("\\xa0", " ");
+ mMessage.setText(data);
+ mDecryptButton.setText(R.string.btn_verify);
+ }
+ }
+ }
+ }
+ mReturnResult = true;
+ }
+
+ if (mSource.getCurrentView().getId() == R.id.sourceMessage
+ && mMessage.getText().length() == 0) {
+
+ CharSequence clipboardText = Compatibility.getClipboardText(this);
+
+ String data = "";
+ if (clipboardText != null) {
+ Matcher matcher = Apg.PGP_MESSAGE.matcher(clipboardText);
+ if (!matcher.matches()) {
+ matcher = Apg.PGP_SIGNED_MESSAGE.matcher(clipboardText);
+ }
+ if (matcher.matches()) {
+ data = matcher.group(1);
+ mMessage.setText(data);
+ Toast.makeText(this, R.string.usingClipboardContent, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ mSignatureLayout.setVisibility(View.GONE);
+ mSignatureLayout.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ if (mSignatureKeyId == 0) {
+ return;
+ }
+ PGPPublicKeyRing key = Apg.getPublicKeyRing(mSignatureKeyId);
+ if (key != null) {
+ Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class);
+ intent.setAction(Apg.Intent.LOOK_UP_KEY_ID);
+ intent.putExtra(Apg.EXTRA_KEY_ID, mSignatureKeyId);
+ startActivity(intent);
+ }
+ }
+ });
+
+ mDecryptButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ decryptClicked();
+ }
+ });
+
+ mReplyButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ replyClicked();
+ }
+ });
+ mReplyButton.setVisibility(View.INVISIBLE);
+
+ if (mReturnResult) {
+ mSourcePrevious.setClickable(false);
+ mSourcePrevious.setEnabled(false);
+ mSourcePrevious.setVisibility(View.INVISIBLE);
+
+ mSourceNext.setClickable(false);
+ mSourceNext.setEnabled(false);
+ mSourceNext.setVisibility(View.INVISIBLE);
+
+ mSourceLabel.setClickable(false);
+ mSourceLabel.setEnabled(false);
+ }
+
+ updateSource();
+
+ if (mSource.getCurrentView().getId() == R.id.sourceMessage
+ && (mMessage.getText().length() > 0 || mData != null || mContentUri != null)) {
+ mDecryptButton.performClick();
+ }
+ }
+
+ private void openFile() {
+ String filename = mFilename.getText().toString();
+
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+ intent.setData(Uri.parse("file://" + filename));
+ intent.setType("*/*");
+
+ try {
+ startActivityForResult(intent, Id.request.filename);
+ } catch (ActivityNotFoundException e) {
+ // No compatible file manager was found.
+ Toast.makeText(this, R.string.noFilemanagerInstalled, 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;
+ }
+
+ if (mInputFilename.startsWith("file")) {
+ 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;
+ fillDataSource();
+ try {
+ InputData in = mDataSource.getInputData(this, false);
+ 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);
+ in = mDataSource.getInputData(this, false);
+ 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;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ }
+ 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(Apg.EXTRA_TEXT, data);
+ intent.putExtra(Apg.EXTRA_SUBJECT, "Re: " + mSubject);
+ intent.putExtra(Apg.EXTRA_SEND_TO, mReplyTo);
+ intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, getSecretKeyId());
+ intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId });
+ startActivity(intent);
+ }
+
+ 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();
+ fillDataSource();
+ fillDataDestination();
+ try {
+ InputData in = mDataSource.getInputData(this, true);
+ OutputStream out = mDataDestination.getOutputStream(this);
+
+ if (mSignedOnly) {
+ data = Apg.verifyText(this, in, out, this);
+ } else {
+ data = Apg.decrypt(this, in, out, Apg.getCachedPassPhrase(getSecretKeyId()), this,
+ mAssumeSymmetricEncryption);
+ }
+
+ out.close();
+
+ if (mDataDestination.getStreamFilename() != null) {
+ data.putString(Apg.EXTRA_RESULT_URI, "content://" + DataProvider.AUTHORITY
+ + "/data/" + mDataDestination.getStreamFilename());
+ } else if (mDecryptTarget == Id.target.message) {
+ if (mReturnBinary) {
+ data.putByteArray(Apg.EXTRA_DECRYPTED_DATA,
+ ((ByteArrayOutputStream) out).toByteArray());
+ } else {
+ data.putString(Apg.EXTRA_DECRYPTED_MESSAGE, new String(
+ ((ByteArrayOutputStream) out).toByteArray()));
+ }
+ }
+ } catch (PGPException e) {
+ error = "" + e;
+ } catch (IOException e) {
+ error = "" + e;
+ } catch (SignatureException e) {
+ error = "" + e;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ }
+
+ data.putInt(Constants.extras.STATUS, Id.message.done);
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ @Override
+ public void handlerCallback(Message msg) {
+ Bundle data = msg.getData();
+ if (data == null) {
+ return;
+ }
+
+ if (data.getInt(Constants.extras.STATUS) == Id.message.unknown_signature_key) {
+ mUnknownSignatureKeyId = data.getLong(Constants.extras.KEY_ID);
+ showDialog(Id.dialog.lookup_unknown_key);
+ return;
+ }
+
+ super.handlerCallback(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(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show();
+ if (mReturnResult) {
+ Intent intent = new Intent();
+ intent.putExtras(data);
+ setResult(RESULT_OK, intent);
+ finish();
+ return;
+ }
+
+ switch (mDecryptTarget) {
+ case Id.target.message: {
+ String decryptedMessage = data.getString(Apg.EXTRA_DECRYPTED_MESSAGE);
+ mMessage.setText(decryptedMessage);
+ mMessage.setHorizontallyScrolling(false);
+ 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(Apg.EXTRA_SIGNATURE)) {
+ String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
+ mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
+ mUserIdRest.setText("id: " + Apg.getSmallFingerPrint(mSignatureKeyId));
+ 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(Apg.EXTRA_SIGNATURE_SUCCESS)) {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
+ } else if (data.getBoolean(Apg.EXTRA_SIGNATURE_UNKNOWN)) {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ Toast.makeText(this, R.string.unknownSignatureKeyTouchToLookUp, Toast.LENGTH_LONG)
+ .show();
+ } 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;
+ }
+
+ case Id.request.look_up_key_id: {
+ PausableThread thread = getRunningThread();
+ if (thread != null && thread.isPaused()) {
+ thread.unpause();
+ }
+ 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() {
+ public void onOkClick(String filename, boolean checked) {
+ removeDialog(Id.dialog.output_filename);
+ mOutputFilename = filename;
+ decryptStart();
+ }
+
+ public void onCancelClick() {
+ removeDialog(Id.dialog.output_filename);
+ }
+ }, getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave), null, Id.request.output_filename);
+ }
+
+ case Id.dialog.lookup_unknown_key: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.title_unknownSignatureKey);
+ alert.setMessage(getString(R.string.lookupUnknownKey,
+ Apg.getSmallFingerPrint(mUnknownSignatureKeyId)));
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.lookup_unknown_key);
+ Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class);
+ intent.setAction(Apg.Intent.LOOK_UP_KEY_ID);
+ intent.putExtra(Apg.EXTRA_KEY_ID, mUnknownSignatureKeyId);
+ startActivityForResult(intent, Id.request.look_up_key_id);
+ }
+ });
+ alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.lookup_unknown_key);
+ PausableThread thread = getRunningThread();
+ if (thread != null && thread.isPaused()) {
+ thread.unpause();
+ }
+ }
+ });
+ alert.setCancelable(true);
+
+ return alert.create();
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return super.onCreateDialog(id);
+ }
+
+ protected void fillDataSource() {
+ mDataSource = new DataSource();
+ if (mContentUri != null) {
+ mDataSource.setUri(mContentUri);
+ } else if (mDecryptTarget == Id.target.file) {
+ mDataSource.setUri(mInputFilename);
+ } else {
+ if (mData != null) {
+ mDataSource.setData(mData);
+ } else {
+ mDataSource.setText(mMessage.getText().toString());
+ }
+ }
+ }
+
+ protected void fillDataDestination() {
+ mDataDestination = new DataDestination();
+ if (mContentUri != null) {
+ mDataDestination.setMode(Id.mode.stream);
+ } else if (mDecryptTarget == Id.target.file) {
+ mDataDestination.setFilename(mOutputFilename);
+ mDataDestination.setMode(Id.mode.file);
+ } else {
+ mDataDestination.setMode(Id.mode.byte_array);
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/ui/EditKeyActivity.java b/org_apg/src/org/apg/ui/EditKeyActivity.java
new file mode 100644
index 000000000..c3945d4ed
--- /dev/null
+++ b/org_apg/src/org/apg/ui/EditKeyActivity.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.Id;
+import org.apg.provider.Database;
+import org.apg.ui.widget.KeyEditor;
+import org.apg.ui.widget.SectionView;
+import org.apg.util.IterableIterator;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.apg.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Toast;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
+import java.util.Vector;
+
+public class EditKeyActivity extends BaseActivity implements OnClickListener {
+
+ private PGPSecretKeyRing mKeyRing = null;
+
+ private SectionView mUserIds;
+ private SectionView mKeys;
+
+ private Button mSaveButton;
+ private Button mDiscardButton;
+
+ private String mCurrentPassPhrase = null;
+ private String mNewPassPhrase = null;
+
+ private Button mChangePassPhrase;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.edit_key);
+
+ Vector userIds = new Vector();
+ Vector keys = new Vector();
+
+ Intent intent = getIntent();
+ long keyId = 0;
+ if (intent.getExtras() != null) {
+ keyId = intent.getExtras().getLong(Apg.EXTRA_KEY_ID);
+ }
+
+ if (keyId != 0) {
+ PGPSecretKey masterKey = null;
+ mKeyRing = Apg.getSecretKeyRing(keyId);
+ if (mKeyRing != null) {
+ masterKey = Apg.getMasterKey(mKeyRing);
+ for (PGPSecretKey key : new IterableIterator(mKeyRing.getSecretKeys())) {
+ keys.add(key);
+ }
+ }
+ if (masterKey != null) {
+ for (String userId : new IterableIterator(masterKey.getUserIDs())) {
+ userIds.add(userId);
+ }
+ }
+ }
+
+ mChangePassPhrase = (Button) findViewById(R.id.btn_change_pass_phrase);
+ mChangePassPhrase.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ showDialog(Id.dialog.new_pass_phrase);
+ }
+ });
+
+ mSaveButton = (Button) findViewById(R.id.btn_save);
+ mDiscardButton = (Button) findViewById(R.id.btn_discard);
+
+ mSaveButton.setOnClickListener(this);
+ mDiscardButton.setOnClickListener(this);
+
+ LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ LinearLayout container = (LinearLayout) findViewById(R.id.container);
+ mUserIds = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
+ 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(Id.type.key);
+ mKeys.setKeys(keys);
+ container.addView(mKeys);
+
+ mCurrentPassPhrase = Apg.getEditPassPhrase();
+ if (mCurrentPassPhrase == null) {
+ mCurrentPassPhrase = "";
+ }
+
+ updatePassPhraseButtonText();
+
+ Toast.makeText(this,
+ getString(R.string.warningMessage, getString(R.string.keyEditingIsBeta)),
+ Toast.LENGTH_LONG).show();
+ }
+
+ private long getMasterKeyId() {
+ if (mKeys.getEditors().getChildCount() == 0) {
+ return 0;
+ }
+ return ((KeyEditor) mKeys.getEditors().getChildAt(0)).getValue().getKeyID();
+ }
+
+ public boolean havePassPhrase() {
+ return (!mCurrentPassPhrase.equals(""))
+ || (mNewPassPhrase != null && !mNewPassPhrase.equals(""));
+ }
+
+ @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
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case Id.dialog.new_pass_phrase: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ if (havePassPhrase()) {
+ alert.setTitle(R.string.title_changePassPhrase);
+ } else {
+ alert.setTitle(R.string.title_setPassPhrase);
+ }
+ 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(Id.dialog.new_pass_phrase);
+
+ String passPhrase1 = "" + input1.getText();
+ String passPhrase2 = "" + input2.getText();
+ if (!passPhrase1.equals(passPhrase2)) {
+ showDialog(Id.dialog.pass_phrases_do_not_match);
+ return;
+ }
+
+ if (passPhrase1.equals("")) {
+ showDialog(Id.dialog.no_pass_phrase);
+ return;
+ }
+
+ mNewPassPhrase = passPhrase1;
+ updatePassPhraseButtonText();
+ }
+ });
+
+ alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.new_pass_phrase);
+ }
+ });
+
+ return alert.create();
+ }
+
+ default: {
+ return super.onCreateDialog(id);
+ }
+ }
+ }
+
+ public void onClick(View v) {
+ if (v == mSaveButton) {
+ // TODO: some warning
+ saveClicked();
+ } else if (v == mDiscardButton) {
+ finish();
+ }
+ }
+
+ private void saveClicked() {
+ if (!havePassPhrase()) {
+ Toast.makeText(this, R.string.setAPassPhrase, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ showDialog(Id.dialog.saving);
+ startThread();
+ }
+
+ @Override
+ public void run() {
+ String error = null;
+ Bundle data = new Bundle();
+ Message msg = new Message();
+
+ try {
+ String oldPassPhrase = mCurrentPassPhrase;
+ String newPassPhrase = mNewPassPhrase;
+ if (newPassPhrase == null) {
+ newPassPhrase = oldPassPhrase;
+ }
+ Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this);
+ Apg.setCachedPassPhrase(getMasterKeyId(), newPassPhrase);
+ } catch (NoSuchProviderException e) {
+ error = "" + e;
+ } catch (NoSuchAlgorithmException e) {
+ error = "" + e;
+ } catch (PGPException e) {
+ error = "" + e;
+ } catch (SignatureException e) {
+ error = "" + e;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ } catch (Database.GeneralException e) {
+ error = "" + e;
+ } catch (IOException e) {
+ error = "" + e;
+ }
+
+ data.putInt(Constants.extras.STATUS, Id.message.done);
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ Bundle data = msg.getData();
+ removeDialog(Id.dialog.saving);
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(EditKeyActivity.this, getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
+ setResult(RESULT_OK);
+ finish();
+ }
+ }
+
+ private void updatePassPhraseButtonText() {
+ mChangePassPhrase.setText(havePassPhrase() ? R.string.btn_changePassPhrase
+ : R.string.btn_setPassPhrase);
+ }
+}
diff --git a/org_apg/src/org/apg/ui/EncryptActivity.java b/org_apg/src/org/apg/ui/EncryptActivity.java
new file mode 100644
index 000000000..e5892a4d5
--- /dev/null
+++ b/org_apg/src/org/apg/ui/EncryptActivity.java
@@ -0,0 +1,998 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.DataDestination;
+import org.apg.DataSource;
+import org.apg.FileDialog;
+import org.apg.Id;
+import org.apg.InputData;
+import org.apg.provider.DataProvider;
+import org.apg.util.Choice;
+import org.apg.util.Compatibility;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.apg.R;
+
+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.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;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
+import java.util.Vector;
+
+public class EncryptActivity extends BaseActivity {
+ private Intent mIntent = null;
+ private String mSubject = null;
+ private String mSendTo = null;
+
+ private long mEncryptionKeyIds[] = null;
+
+ private boolean mReturnResult = false;
+ private EditText mMessage = null;
+ private Button mSelectKeysButton = null;
+ private Button mEncryptButton = null;
+ 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;
+
+ private boolean mAsciiArmourDemand = false;
+ private boolean mOverrideAsciiArmour = false;
+ private Uri mContentUri = null;
+ private byte[] mData = null;
+
+ private DataSource mDataSource = null;
+ private DataDestination mDataDestination = null;
+
+ private boolean mGenerateSignature = false;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.encrypt);
+
+ mGenerateSignature = false;
+
+ 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() {
+ 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() {
+ 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() {
+ 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() {
+ 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() {
+ 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) + " ("
+ + getString(R.string.fast) + ")"),
+ new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"),
+ new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow)
+ + ")"), };
+ ArrayAdapter adapter = new ArrayAdapter(this,
+ android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mFileCompression.setAdapter(adapter);
+
+ int defaultFileCompression = mPreferences.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(mPreferences.getDefaultAsciiArmour());
+ mAsciiArmour.setOnClickListener(new OnClickListener() {
+ public void onClick(View view) {
+ guessOutputFilename();
+ }
+ });
+
+ mEncryptToClipboardButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ encryptToClipboardClicked();
+ }
+ });
+
+ mEncryptButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ encryptClicked();
+ }
+ });
+
+ mSelectKeysButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ selectPublicKeys();
+ }
+ });
+
+ mSign.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ CheckBox checkBox = (CheckBox) v;
+ if (checkBox.isChecked()) {
+ selectSecretKey();
+ } else {
+ setSecretKeyId(Id.key.none);
+ updateView();
+ }
+ }
+ });
+
+ mIntent = getIntent();
+ if (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
+ || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())
+ || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
+ || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
+ mContentUri = mIntent.getData();
+ Bundle extras = mIntent.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+
+ if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
+ || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
+ mReturnResult = true;
+ }
+
+ if (Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
+ mGenerateSignature = true;
+ mOverrideAsciiArmour = true;
+ mAsciiArmourDemand = false;
+ }
+
+ if (extras.containsKey(Apg.EXTRA_ASCII_ARMOUR)) {
+ mAsciiArmourDemand = extras.getBoolean(Apg.EXTRA_ASCII_ARMOUR, true);
+ mOverrideAsciiArmour = true;
+ mAsciiArmour.setChecked(mAsciiArmourDemand);
+ }
+
+ mData = extras.getByteArray(Apg.EXTRA_DATA);
+ String textData = null;
+ if (mData == null) {
+ textData = extras.getString(Apg.EXTRA_TEXT);
+ }
+ mSendTo = extras.getString(Apg.EXTRA_SEND_TO);
+ mSubject = extras.getString(Apg.EXTRA_SUBJECT);
+ long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
+ long encryptionKeyIds[] = extras.getLongArray(Apg.EXTRA_ENCRYPTION_KEY_IDS);
+ if (signatureKeyId != 0) {
+ PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId);
+ PGPSecretKey masterKey = null;
+ if (keyRing != null) {
+ masterKey = Apg.getMasterKey(keyRing);
+ if (masterKey != null) {
+ Vector signKeys = Apg.getUsableSigningKeys(keyRing);
+ if (signKeys.size() > 0) {
+ setSecretKeyId(masterKey.getKeyID());
+ }
+ }
+ }
+ }
+
+ if (encryptionKeyIds != null) {
+ Vector goodIds = new Vector();
+ for (int i = 0; i < encryptionKeyIds.length; ++i) {
+ PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]);
+ PGPPublicKey masterKey = null;
+ if (keyRing == null) {
+ continue;
+ }
+ masterKey = Apg.getMasterKey(keyRing);
+ if (masterKey == null) {
+ continue;
+ }
+ Vector 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 (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
+ || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
+ || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
+ if (textData != null) {
+ mMessage.setText(textData);
+ }
+ mSource.setInAnimation(null);
+ mSource.setOutAnimation(null);
+ while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
+ mSource.showNext();
+ }
+ } else if (Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())) {
+ if ("file".equals(mIntent.getScheme())) {
+ mInputFilename = Uri.decode(mIntent.getDataString().replace("file://", ""));
+ mFilename.setText(mInputFilename);
+ guessOutputFilename();
+ }
+ mSource.setInAnimation(null);
+ mSource.setOutAnimation(null);
+ while (mSource.getCurrentView().getId() != R.id.sourceFile) {
+ mSource.showNext();
+ }
+ }
+ }
+
+ updateView();
+ updateSource();
+ updateMode();
+
+ if (mReturnResult) {
+ mSourcePrevious.setClickable(false);
+ mSourcePrevious.setEnabled(false);
+ mSourcePrevious.setVisibility(View.INVISIBLE);
+
+ mSourceNext.setClickable(false);
+ mSourceNext.setEnabled(false);
+ mSourceNext.setVisibility(View.INVISIBLE);
+
+ mSourceLabel.setClickable(false);
+ mSourceLabel.setEnabled(false);
+ }
+
+ updateButtons();
+
+ if (mReturnResult
+ && (mMessage.getText().length() > 0 || mData != null || mContentUri != null)
+ && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || getSecretKeyId() != 0)) {
+ encryptClicked();
+ }
+ }
+
+ private void openFile() {
+ String filename = mFilename.getText().toString();
+
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+
+ intent.setData(Uri.parse("file://" + filename));
+ intent.setType("*/*");
+
+ try {
+ startActivityForResult(intent, Id.request.filename);
+ } catch (ActivityNotFoundException e) {
+ // No compatible file manager was found.
+ Toast.makeText(this, R.string.noFilemanagerInstalled, 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);
+ break;
+ }
+
+ case R.id.sourceMessage: {
+ mSourceLabel.setText(R.string.label_message);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ updateButtons();
+ }
+
+ private void updateButtons() {
+ switch (mSource.getCurrentView().getId()) {
+ case R.id.sourceFile: {
+ mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
+ mEncryptButton.setText(R.string.btn_encrypt);
+ break;
+ }
+
+ case R.id.sourceMessage: {
+ mSourceLabel.setText(R.string.label_message);
+ if (mReturnResult) {
+ mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
+ } else {
+ mEncryptToClipboardButton.setVisibility(View.VISIBLE);
+ }
+ if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
+ if (mReturnResult) {
+ mEncryptButton.setText(R.string.btn_encrypt);
+ } else {
+ mEncryptButton.setText(R.string.btn_encryptAndEmail);
+ }
+ mEncryptButton.setEnabled(true);
+ mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
+ mEncryptToClipboardButton.setEnabled(true);
+ } else {
+ if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
+ if (getSecretKeyId() == 0) {
+ if (mReturnResult) {
+ mEncryptButton.setText(R.string.btn_encrypt);
+ } else {
+ mEncryptButton.setText(R.string.btn_encryptAndEmail);
+ }
+ mEncryptButton.setEnabled(false);
+ mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
+ mEncryptToClipboardButton.setEnabled(false);
+ } else {
+ if (mReturnResult) {
+ mEncryptButton.setText(R.string.btn_sign);
+ } else {
+ mEncryptButton.setText(R.string.btn_signAndEmail);
+ }
+ mEncryptButton.setEnabled(true);
+ mEncryptToClipboardButton.setText(R.string.btn_signToClipboard);
+ mEncryptToClipboardButton.setEnabled(true);
+ }
+ } else {
+ if (mReturnResult) {
+ mEncryptButton.setText(R.string.btn_encrypt);
+ } else {
+ mEncryptButton.setText(R.string.btn_encryptAndEmail);
+ }
+ mEncryptButton.setEnabled(true);
+ mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
+ 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;
+ }
+ }
+ updateButtons();
+ }
+
+ 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;
+ }
+
+ if (!mInputFilename.startsWith("content")) {
+ 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 {
+ InputData in;
+ OutputStream out;
+ boolean useAsciiArmour = true;
+ long encryptionKeyIds[] = null;
+ long signatureKeyId = 0;
+ int compressionId = 0;
+ boolean signOnly = false;
+
+ 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);
+ }
+
+ fillDataSource(signOnly && !mReturnResult);
+ fillDataDestination();
+
+ // streams
+ in = mDataSource.getInputData(this, true);
+ out = mDataDestination.getOutputStream(this);
+
+ if (mEncryptTarget == Id.target.file) {
+ useAsciiArmour = mAsciiArmour.isChecked();
+ compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
+ } else {
+ useAsciiArmour = true;
+ compressionId = mPreferences.getDefaultMessageCompression();
+ }
+
+ if (mOverrideAsciiArmour) {
+ useAsciiArmour = mAsciiArmourDemand;
+ }
+
+ if (mGenerateSignature) {
+ Apg.generateSignature(this, in, out, useAsciiArmour, mDataSource.isBinary(),
+ getSecretKeyId(), Apg.getCachedPassPhrase(getSecretKeyId()),
+ mPreferences.getDefaultHashAlgorithm(),
+ mPreferences.getForceV3Signatures(), this);
+ } else if (signOnly) {
+ Apg.signText(this, in, out, getSecretKeyId(),
+ Apg.getCachedPassPhrase(getSecretKeyId()),
+ mPreferences.getDefaultHashAlgorithm(),
+ mPreferences.getForceV3Signatures(), this);
+ } else {
+ Apg.encrypt(this, in, out, useAsciiArmour, encryptionKeyIds, signatureKeyId,
+ Apg.getCachedPassPhrase(signatureKeyId), this,
+ mPreferences.getDefaultEncryptionAlgorithm(),
+ mPreferences.getDefaultHashAlgorithm(), compressionId,
+ mPreferences.getForceV3Signatures(), passPhrase);
+ }
+
+ out.close();
+ if (mEncryptTarget != Id.target.file) {
+
+ if (out instanceof ByteArrayOutputStream) {
+ if (useAsciiArmour) {
+ String extraData = new String(((ByteArrayOutputStream) out).toByteArray());
+ if (mGenerateSignature) {
+ data.putString(Apg.EXTRA_SIGNATURE_TEXT, extraData);
+ } else {
+ data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, extraData);
+ }
+ } else {
+ byte extraData[] = ((ByteArrayOutputStream) out).toByteArray();
+ if (mGenerateSignature) {
+ data.putByteArray(Apg.EXTRA_SIGNATURE_DATA, extraData);
+ } else {
+ data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA, extraData);
+ }
+ }
+ } else if (out instanceof FileOutputStream) {
+ String fileName = mDataDestination.getStreamFilename();
+ String uri = "content://" + DataProvider.AUTHORITY + "/data/" + fileName;
+ data.putString(Apg.EXTRA_RESULT_URI, uri);
+ } else {
+ throw new Apg.GeneralException("No output-data found.");
+ }
+ }
+ } catch (IOException e) {
+ error = "" + e;
+ } catch (PGPException e) {
+ error = "" + e;
+ } catch (NoSuchProviderException e) {
+ error = "" + e;
+ } catch (NoSuchAlgorithmException e) {
+ error = "" + e;
+ } catch (SignatureException e) {
+ error = "" + e;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ }
+
+ data.putInt(Constants.extras.STATUS, Id.message.done);
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_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);
+ }
+
+ updateButtons();
+ }
+
+ private void selectPublicKeys() {
+ Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
+ Vector keyIds = new Vector();
+ if (getSecretKeyId() != 0) {
+ keyIds.add(getSecretKeyId());
+ }
+ if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
+ for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
+ keyIds.add(mEncryptionKeyIds[i]);
+ }
+ }
+ long[] initialKeyIds = null;
+ if (keyIds.size() > 0) {
+ initialKeyIds = new long[keyIds.size()];
+ for (int i = 0; i < keyIds.size(); ++i) {
+ initialKeyIds[i] = keyIds.get(i);
+ }
+ }
+ intent.putExtra(Apg.EXTRA_SELECTION, initialKeyIds);
+ 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(Apg.EXTRA_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(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+ switch (mEncryptTarget) {
+ case Id.target.clipboard: {
+ String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
+ Compatibility.copyToClipboard(this, message);
+ Toast.makeText(this, R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT)
+ .show();
+ break;
+ }
+
+ case Id.target.email: {
+ if (mReturnResult) {
+ Intent intent = new Intent();
+ intent.putExtras(data);
+ setResult(RESULT_OK, intent);
+ finish();
+ return;
+ }
+
+ String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
+ Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
+ emailIntent.setType("text/plain; charset=utf-8");
+ emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
+ if (mSubject != null) {
+ emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, mSubject);
+ }
+ if (mSendTo != null) {
+ emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { mSendTo });
+ }
+ EncryptActivity.this.startActivity(Intent.createChooser(emailIntent,
+ getString(R.string.title_sendEmail)));
+ break;
+ }
+
+ 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() {
+ public void onOkClick(String filename, boolean checked) {
+ removeDialog(Id.dialog.output_filename);
+ mOutputFilename = filename;
+ encryptStart();
+ }
+
+ public void onCancelClick() {
+ removeDialog(Id.dialog.output_filename);
+ }
+ }, getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave), null, Id.request.output_filename);
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ return super.onCreateDialog(id);
+ }
+
+ protected void fillDataSource(boolean fixContent) {
+ mDataSource = new DataSource();
+ if (mContentUri != null) {
+ mDataSource.setUri(mContentUri);
+ } else if (mEncryptTarget == Id.target.file) {
+ mDataSource.setUri(mInputFilename);
+ } else {
+ if (mData != null) {
+ mDataSource.setData(mData);
+ } else {
+ String message = mMessage.getText().toString();
+ if (fixContent) {
+ // 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");
+ }
+ mDataSource.setText(message);
+ }
+ }
+ }
+
+ protected void fillDataDestination() {
+ mDataDestination = new DataDestination();
+ if (mContentUri != null) {
+ mDataDestination.setMode(Id.mode.stream);
+ } else if (mEncryptTarget == Id.target.file) {
+ mDataDestination.setFilename(mOutputFilename);
+ mDataDestination.setMode(Id.mode.file);
+ } else {
+ mDataDestination.setMode(Id.mode.byte_array);
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/ui/GeneralActivity.java b/org_apg/src/org/apg/ui/GeneralActivity.java
new file mode 100644
index 000000000..d70694630
--- /dev/null
+++ b/org_apg/src/org/apg/ui/GeneralActivity.java
@@ -0,0 +1,177 @@
+package org.apg.ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.util.Choice;
+import org.apg.R;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+public class GeneralActivity extends BaseActivity {
+ private Intent mIntent;
+ private ArrayAdapter mAdapter;
+ private ListView mList;
+ private Button mCancelButton;
+ private String mDataString;
+ private Uri mDataUri;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.general);
+
+ mIntent = getIntent();
+
+ InputStream inStream = null;
+ {
+ String data = mIntent.getStringExtra(Intent.EXTRA_TEXT);
+ if (data != null) {
+ mDataString = data;
+ inStream = new ByteArrayInputStream(data.getBytes());
+ }
+ }
+
+ if (inStream == null) {
+ Uri data = mIntent.getData();
+ if (data != null) {
+ mDataUri = data;
+ try {
+ inStream = getContentResolver().openInputStream(data);
+ } catch (FileNotFoundException e) {
+ // didn't work
+ Toast.makeText(this, "failed to open stream", Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ if (inStream == null) {
+ Toast.makeText(this, "no data found", Toast.LENGTH_SHORT).show();
+ finish();
+ return;
+ }
+
+ int contentType = Id.content.unknown;
+ try {
+ contentType = Apg.getStreamContent(this, inStream);
+ inStream.close();
+ } catch (IOException e) {
+ // just means that there's no PGP data in there
+ }
+
+ mList = (ListView) findViewById(R.id.options);
+ Vector choices = new Vector();
+
+ if (contentType == Id.content.keys) {
+ choices.add(new Choice(Id.choice.action.import_public,
+ getString(R.string.action_importPublic)));
+ choices.add(new Choice(Id.choice.action.import_secret,
+ getString(R.string.action_importSecret)));
+ }
+
+ if (contentType == Id.content.encrypted_data) {
+ choices.add(new Choice(Id.choice.action.decrypt, getString(R.string.action_decrypt)));
+ }
+
+ if (contentType == Id.content.unknown) {
+ choices.add(new Choice(Id.choice.action.encrypt, getString(R.string.action_encrypt)));
+ }
+
+ mAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, choices);
+ mList.setAdapter(mAdapter);
+
+ mList.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView> arg0, View arg1, int arg2, long arg3) {
+ clicked(mAdapter.getItem(arg2).getId());
+ }
+ });
+
+ mCancelButton = (Button) findViewById(R.id.btn_cancel);
+ mCancelButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ GeneralActivity.this.finish();
+ }
+ });
+
+ if (choices.size() == 1) {
+ clicked(choices.get(0).getId());
+ }
+ }
+
+ private void clicked(int id) {
+ Intent intent = new Intent();
+ switch (id) {
+ case Id.choice.action.encrypt: {
+ intent.setClass(this, EncryptActivity.class);
+ if (mDataString != null) {
+ intent.setAction(Apg.Intent.ENCRYPT);
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setAction(Apg.Intent.ENCRYPT_FILE);
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+
+ break;
+ }
+
+ case Id.choice.action.decrypt: {
+ intent.setClass(this, DecryptActivity.class);
+ if (mDataString != null) {
+ intent.setAction(Apg.Intent.DECRYPT);
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setAction(Apg.Intent.DECRYPT_FILE);
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+
+ break;
+ }
+
+ case Id.choice.action.import_public: {
+ intent.setClass(this, PublicKeyListActivity.class);
+ intent.setAction(Apg.Intent.IMPORT);
+ if (mDataString != null) {
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+ break;
+ }
+
+ case Id.choice.action.import_secret: {
+ intent.setClass(this, SecretKeyListActivity.class);
+ intent.setAction(Apg.Intent.IMPORT);
+ if (mDataString != null) {
+ intent.putExtra(Apg.EXTRA_TEXT, mDataString);
+ } else if (mDataUri != null) {
+ intent.setDataAndType(mDataUri, mIntent.getType());
+ }
+ break;
+ }
+
+ default: {
+ // shouldn't happen
+ return;
+ }
+ }
+
+ startActivity(intent);
+ finish();
+ }
+}
diff --git a/org_apg/src/org/apg/ui/ImportFromQRCodeActivity.java b/org_apg/src/org/apg/ui/ImportFromQRCodeActivity.java
new file mode 100644
index 000000000..593c841df
--- /dev/null
+++ b/org_apg/src/org/apg/ui/ImportFromQRCodeActivity.java
@@ -0,0 +1,138 @@
+package org.apg.ui;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.HkpKeyServer;
+import org.apg.Id;
+import org.apg.KeyServer.QueryException;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.apg.R;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.google.zxing.integration.android.IntentResult;
+
+public class ImportFromQRCodeActivity extends BaseActivity {
+ private static final String TAG = "ImportFromQRCodeActivity";
+
+ private final Bundle status = new Bundle();
+ private final Message msg = new Message();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ new IntentIntegrator(this).initiateScan();
+ }
+
+ private void importAndSign(final long keyId, final String expectedFingerprint) {
+ if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
+
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ try {
+ // TODO: display some sort of spinner here while the user waits
+
+ HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]); // TODO: there should be only 1
+ String encodedKey = server.get(keyId);
+
+ PGPKeyRing keyring = Apg.decodeKeyRing(new ByteArrayInputStream(encodedKey.getBytes()));
+ if (keyring != null && keyring instanceof PGPPublicKeyRing) {
+ PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
+
+ // make sure the fingerprints match before we cache this thing
+ String actualFingerprint = Apg.convertToHex(publicKeyRing.getPublicKey().getFingerprint());
+ if (expectedFingerprint.equals(actualFingerprint)) {
+ // store the signed key in our local cache
+ int retval = Apg.storeKeyRingInCache(publicKeyRing);
+ if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
+ status.putString(Apg.EXTRA_ERROR, "Failed to store signed key in local cache");
+ } else {
+ Intent intent = new Intent(ImportFromQRCodeActivity.this, SignKeyActivity.class);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
+ startActivityForResult(intent, Id.request.sign_key);
+ }
+ } else {
+ status.putString(Apg.EXTRA_ERROR, "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
+ }
+ }
+ } catch (QueryException e) {
+ Log.e(TAG, "Failed to query KeyServer", e);
+ status.putString(Apg.EXTRA_ERROR, "Failed to query KeyServer");
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to query KeyServer", e);
+ status.putString(Apg.EXTRA_ERROR, "Failed to query KeyServer");
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+ }
+ }
+ };
+
+ t.setName("KeyExchange Download Thread");
+ t.setDaemon(true);
+ t.start();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case IntentIntegrator.REQUEST_CODE: {
+ boolean debug = true; // TODO: remove this!!!
+ IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
+ if (debug || (scanResult != null && scanResult.getFormatName() != null)) {
+ String[] bits = debug ? new String[] { "5993515643896327656", "0816 F68A 6816 68FB 01BF 2CA5 532D 3EB9 1E2F EDE8" } : scanResult.getContents().split(",");
+ if (bits.length != 2) {
+ return; // dont know how to handle this. Not a valid code
+ }
+
+ long keyId = Long.parseLong(bits[0]);
+ String expectedFingerprint = bits[1];
+
+ importAndSign(keyId, expectedFingerprint);
+
+ break;
+ }
+ }
+
+ case Id.request.sign_key: {
+ // signals the end of processing. Signature was either applied, or it wasnt
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+
+ msg.setData(status);
+ sendMessage(msg);
+
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ Bundle data = msg.getData();
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ Toast.makeText(this, R.string.keySignSuccess, Toast.LENGTH_SHORT).show(); // TODO
+ finish();
+ }
+}
diff --git a/org_apg/src/org/apg/ui/KeyListActivity.java b/org_apg/src/org/apg/ui/KeyListActivity.java
new file mode 100644
index 000000000..6c76f02bc
--- /dev/null
+++ b/org_apg/src/org/apg/ui/KeyListActivity.java
@@ -0,0 +1,768 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.FileDialog;
+import org.apg.Id;
+import org.apg.InputData;
+import org.apg.provider.KeyRings;
+import org.apg.provider.Keys;
+import org.apg.provider.UserIds;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.apg.R;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.SearchManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseExpandableListAdapter;
+import android.widget.Button;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.io.ByteArrayInputStream;
+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.util.Vector;
+
+public class KeyListActivity extends BaseActivity {
+ protected ExpandableListView mList;
+ protected KeyListAdapter mListAdapter;
+ protected View mFilterLayout;
+ protected Button mClearFilterButton;
+ protected TextView mFilterInfo;
+
+ protected int mSelectedItem = -1;
+ protected int mTask = 0;
+
+ protected String mImportFilename = Constants.path.APP_DIR + "/";
+ protected String mExportFilename = Constants.path.APP_DIR + "/";
+
+ protected String mImportData;
+ protected boolean mDeleteAfterImport = false;
+
+ protected int mKeyType = Id.type.public_key;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.key_list);
+
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+
+ mList = (ExpandableListView) findViewById(R.id.list);
+ registerForContextMenu(mList);
+
+ mFilterLayout = findViewById(R.id.layout_filter);
+ mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
+ mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
+
+ mClearFilterButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ handleIntent(new Intent());
+ }
+ });
+
+ handleIntent(getIntent());
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleIntent(intent);
+ }
+
+ protected void handleIntent(Intent intent) {
+ String searchString = null;
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ searchString = intent.getStringExtra(SearchManager.QUERY);
+ if (searchString != null && searchString.trim().length() == 0) {
+ searchString = null;
+ }
+ }
+
+ if (searchString == null) {
+ mFilterLayout.setVisibility(View.GONE);
+ } else {
+ mFilterLayout.setVisibility(View.VISIBLE);
+ mFilterInfo.setText(getString(R.string.filterInfo, searchString));
+ }
+
+ if (mListAdapter != null) {
+ mListAdapter.cleanup();
+ }
+ mListAdapter = new KeyListAdapter(this, searchString);
+ mList.setAdapter(mListAdapter);
+
+ if (Apg.Intent.IMPORT.equals(intent.getAction())) {
+ if ("file".equals(intent.getScheme()) && intent.getDataString() != null) {
+ mImportFilename = Uri.decode(intent.getDataString().replace("file://", ""));
+ } else {
+ mImportData = intent.getStringExtra(Apg.EXTRA_TEXT);
+ }
+ importKeys();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Id.menu.option.import_keys: {
+ showDialog(Id.dialog.import_keys);
+ return true;
+ }
+
+ case Id.menu.option.export_keys: {
+ showDialog(Id.dialog.export_keys);
+ return true;
+ }
+
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
+ int type = ExpandableListView.getPackedPositionType(info.packedPosition);
+ int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+
+ if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ return super.onContextItemSelected(menuItem);
+ }
+
+ switch (menuItem.getItemId()) {
+ case Id.menu.export: {
+ mSelectedItem = groupPosition;
+ showDialog(Id.dialog.export_key);
+ return true;
+ }
+
+ case Id.menu.delete: {
+ mSelectedItem = groupPosition;
+ showDialog(Id.dialog.delete_key);
+ return true;
+ }
+
+ default: {
+ return super.onContextItemSelected(menuItem);
+ }
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ boolean singleKeyExport = false;
+
+ switch (id) {
+ case Id.dialog.delete_key: {
+ final int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
+ mSelectedItem = -1;
+ // TODO: better way to do this?
+ String userId = "";
+ Object keyRing = Apg.getKeyRing(keyRingId);
+ if (keyRing != null) {
+ if (keyRing instanceof PGPPublicKeyRing) {
+ userId = Apg.getMainUserIdSafe(this,
+ Apg.getMasterKey((PGPPublicKeyRing) keyRing));
+ } else {
+ userId = Apg.getMainUserIdSafe(this,
+ Apg.getMasterKey((PGPSecretKeyRing) keyRing));
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.warning);
+ builder.setMessage(getString(
+ mKeyType == Id.type.public_key ? R.string.keyDeletionConfirmation
+ : R.string.secretKeyDeletionConfirmation, userId));
+ builder.setIcon(android.R.drawable.ic_dialog_alert);
+ builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ deleteKey(keyRingId);
+ removeDialog(Id.dialog.delete_key);
+ }
+ });
+ builder.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ removeDialog(Id.dialog.delete_key);
+ }
+ });
+ return builder.create();
+ }
+
+ case Id.dialog.import_keys: {
+ return FileDialog.build(this, getString(R.string.title_importKeys),
+ getString(R.string.specifyFileToImportFrom), mImportFilename,
+ new FileDialog.OnClickListener() {
+ public void onOkClick(String filename, boolean checked) {
+ removeDialog(Id.dialog.import_keys);
+ mDeleteAfterImport = checked;
+ mImportFilename = filename;
+ importKeys();
+ }
+
+ public void onCancelClick() {
+ removeDialog(Id.dialog.import_keys);
+ }
+ }, getString(R.string.filemanager_titleOpen),
+ getString(R.string.filemanager_btnOpen),
+ getString(R.string.label_deleteAfterImport), Id.request.filename);
+ }
+
+ case Id.dialog.export_key: {
+ singleKeyExport = true;
+ // break intentionally omitted, to use the Id.dialog.export_keys dialog
+ }
+
+ case Id.dialog.export_keys: {
+ String title = (singleKeyExport ? getString(R.string.title_exportKey)
+ : getString(R.string.title_exportKeys));
+
+ final int thisDialogId = (singleKeyExport ? Id.dialog.export_key
+ : Id.dialog.export_keys);
+
+ return FileDialog.build(this, title,
+ getString(mKeyType == Id.type.public_key ? R.string.specifyFileToExportTo
+ : R.string.specifyFileToExportSecretKeysTo), mExportFilename,
+ new FileDialog.OnClickListener() {
+ public void onOkClick(String filename, boolean checked) {
+ removeDialog(thisDialogId);
+ mExportFilename = filename;
+ exportKeys();
+ }
+
+ public void onCancelClick() {
+ removeDialog(thisDialogId);
+ }
+ }, getString(R.string.filemanager_titleSave),
+ getString(R.string.filemanager_btnSave), null, Id.request.filename);
+ }
+
+ default: {
+ return super.onCreateDialog(id);
+ }
+ }
+ }
+
+ public void importKeys() {
+ showDialog(Id.dialog.importing);
+ mTask = Id.task.import_keys;
+ startThread();
+ }
+
+ public void exportKeys() {
+ showDialog(Id.dialog.exporting);
+ mTask = Id.task.export_keys;
+ startThread();
+ }
+
+ @Override
+ public void run() {
+ String error = null;
+ Bundle data = new Bundle();
+ Message msg = new Message();
+
+ try {
+ InputStream importInputStream = null;
+ OutputStream exportOutputStream = null;
+ long size = 0;
+ if (mTask == Id.task.import_keys) {
+ if (mImportData != null) {
+ byte[] bytes = mImportData.getBytes();
+ size = bytes.length;
+ importInputStream = new ByteArrayInputStream(bytes);
+ } else {
+ File file = new File(mImportFilename);
+ size = file.length();
+ importInputStream = new FileInputStream(file);
+ }
+ } else {
+ exportOutputStream = new FileOutputStream(mExportFilename);
+ }
+
+ if (mTask == Id.task.import_keys) {
+ data = Apg.importKeyRings(this, mKeyType, new InputData(importInputStream, size),
+ this);
+ } else {
+ Vector keyRingIds = new Vector();
+ if (mSelectedItem == -1) {
+ keyRingIds = Apg
+ .getKeyRingIds(mKeyType == Id.type.public_key ? Id.database.type_public
+ : Id.database.type_secret);
+ } else {
+ int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
+ keyRingIds.add(keyRingId);
+ mSelectedItem = -1;
+ }
+ data = Apg.exportKeyRings(this, keyRingIds, exportOutputStream, this);
+ }
+ } catch (FileNotFoundException e) {
+ error = getString(R.string.error_fileNotFound);
+ } catch (IOException e) {
+ error = "" + e;
+ } catch (PGPException e) {
+ error = "" + e;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ }
+
+ mImportData = null;
+
+ if (mTask == Id.task.import_keys) {
+ data.putInt(Constants.extras.STATUS, Id.message.import_done);
+ } else {
+ data.putInt(Constants.extras.STATUS, Id.message.export_done);
+ }
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ protected void deleteKey(int keyRingId) {
+ Apg.deleteKey(keyRingId);
+ refreshList();
+ }
+
+ protected void refreshList() {
+ mListAdapter.rebuild(true);
+ mListAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ Bundle data = msg.getData();
+ if (data != null) {
+ int type = data.getInt(Constants.extras.STATUS);
+ switch (type) {
+ case Id.message.import_done: {
+ removeDialog(Id.dialog.importing);
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(KeyListActivity.this, getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ int added = data.getInt("added");
+ int updated = data.getInt("updated");
+ int bad = data.getInt("bad");
+ String message;
+ if (added > 0 && updated > 0) {
+ message = getString(R.string.keysAddedAndUpdated, added, updated);
+ } else if (added > 0) {
+ message = getString(R.string.keysAdded, added);
+ } else if (updated > 0) {
+ message = getString(R.string.keysUpdated, updated);
+ } else {
+ message = getString(R.string.noKeysAddedOrUpdated);
+ }
+ Toast.makeText(KeyListActivity.this, message, Toast.LENGTH_SHORT).show();
+ if (bad > 0) {
+ 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.badKeysEncountered, bad));
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ alert.setCancelable(true);
+ alert.create().show();
+ } else if (mDeleteAfterImport) {
+ // everything went well, so now delete, if that was turned on
+ setDeleteFile(mImportFilename);
+ showDialog(Id.dialog.delete_file);
+ }
+ }
+ refreshList();
+ break;
+ }
+
+ case Id.message.export_done: {
+ removeDialog(Id.dialog.exporting);
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(KeyListActivity.this, getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ } else {
+ int exported = data.getInt("exported");
+ String message;
+ if (exported == 1) {
+ message = getString(R.string.keyExported);
+ } else if (exported > 0) {
+ message = getString(R.string.keysExported, exported);
+ } else {
+ message = getString(R.string.noKeysExported);
+ }
+ Toast.makeText(KeyListActivity.this, message, Toast.LENGTH_SHORT).show();
+ }
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ protected class KeyListAdapter extends BaseExpandableListAdapter {
+ private LayoutInflater mInflater;
+ private Vector> mChildren;
+ private SQLiteDatabase mDatabase;
+ private Cursor mCursor;
+ private String mSearchString;
+
+ private class KeyChild {
+ public static final int KEY = 0;
+ public static final int USER_ID = 1;
+ public static final int FINGER_PRINT = 2;
+
+ public int type;
+ public String userId;
+ public long keyId;
+ public boolean isMasterKey;
+ public int algorithm;
+ public int keySize;
+ public boolean canSign;
+ public boolean canEncrypt;
+ public String fingerPrint;
+
+ public KeyChild(long keyId, boolean isMasterKey, int algorithm, int keySize,
+ boolean canSign, boolean canEncrypt) {
+ this.type = KEY;
+ this.keyId = keyId;
+ this.isMasterKey = isMasterKey;
+ this.algorithm = algorithm;
+ this.keySize = keySize;
+ this.canSign = canSign;
+ this.canEncrypt = canEncrypt;
+ }
+
+ public KeyChild(String userId) {
+ type = USER_ID;
+ this.userId = userId;
+ }
+
+ public KeyChild(String fingerPrint, boolean isFingerPrint) {
+ type = FINGER_PRINT;
+ this.fingerPrint = fingerPrint;
+ }
+ }
+
+ public KeyListAdapter(Context context, String searchString) {
+ mSearchString = searchString;
+
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mDatabase = Apg.getDatabase().db();
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "("
+ + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
+ + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY
+ + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + "("
+ + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "."
+ + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.RANK
+ + " = '0')");
+
+ if (searchString != null && searchString.trim().length() > 0) {
+ String[] chunks = searchString.trim().split(" +");
+ qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME
+ + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME
+ + "." + Keys._ID);
+ for (int i = 0; i < chunks.length; ++i) {
+ qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
+ qb.appendWhereEscapeString("%" + chunks[i] + "%");
+ }
+ qb.appendWhere(")");
+ }
+
+ mCursor = qb.query(mDatabase, new String[] { KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
+ }, KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?", new String[] { ""
+ + (mKeyType == Id.type.public_key ? Id.database.type_public
+ : Id.database.type_secret) }, null, null, UserIds.TABLE_NAME + "."
+ + UserIds.USER_ID + " ASC");
+
+ // content provider way for reference, might have to go back to it sometime:
+ /*
+ * Uri contentUri = null; if (mKeyType == Id.type.secret_key) { contentUri =
+ * Apg.CONTENT_URI_SECRET_KEY_RINGS; } else { contentUri =
+ * Apg.CONTENT_URI_PUBLIC_KEY_RINGS; } mCursor = getContentResolver().query( contentUri,
+ * new String[] { DataProvider._ID, // 0 DataProvider.MASTER_KEY_ID, // 1
+ * DataProvider.USER_ID, // 2 }, null, null, null);
+ */
+
+ startManagingCursor(mCursor);
+ rebuild(false);
+ }
+
+ public void cleanup() {
+ if (mCursor != null) {
+ stopManagingCursor(mCursor);
+ mCursor.close();
+ }
+ }
+
+ public void rebuild(boolean requery) {
+ if (requery) {
+ mCursor.requery();
+ }
+ mChildren = new Vector>();
+ for (int i = 0; i < mCursor.getCount(); ++i) {
+ mChildren.add(null);
+ }
+ }
+
+ protected Vector getChildrenOfGroup(int groupPosition) {
+ Vector children = mChildren.get(groupPosition);
+ if (children != null) {
+ return children;
+ }
+
+ mCursor.moveToPosition(groupPosition);
+ children = new Vector();
+ Cursor c = mDatabase.query(Keys.TABLE_NAME, new String[] { Keys._ID, // 0
+ Keys.KEY_ID, // 1
+ Keys.IS_MASTER_KEY, // 2
+ Keys.ALGORITHM, // 3
+ Keys.KEY_SIZE, // 4
+ Keys.CAN_SIGN, // 5
+ Keys.CAN_ENCRYPT, // 6
+ }, Keys.KEY_RING_ID + " = ?", new String[] { mCursor.getString(0) }, null, null,
+ Keys.RANK + " ASC");
+
+ int masterKeyId = -1;
+ long fingerPrintId = -1;
+ for (int i = 0; i < c.getCount(); ++i) {
+ c.moveToPosition(i);
+ children.add(new KeyChild(c.getLong(1), c.getInt(2) == 1, c.getInt(3), c.getInt(4),
+ c.getInt(5) == 1, c.getInt(6) == 1));
+ if (i == 0) {
+ masterKeyId = c.getInt(0);
+ fingerPrintId = c.getLong(1);
+ }
+ }
+ c.close();
+
+ if (masterKeyId != -1) {
+ children.insertElementAt(new KeyChild(Apg.getFingerPrint(fingerPrintId), true), 0);
+ c = mDatabase.query(UserIds.TABLE_NAME, new String[] { UserIds.USER_ID, // 0
+ }, UserIds.KEY_ID + " = ? AND " + UserIds.RANK + " > 0", new String[] { ""
+ + masterKeyId }, null, null, UserIds.RANK + " ASC");
+
+ for (int i = 0; i < c.getCount(); ++i) {
+ c.moveToPosition(i);
+ children.add(new KeyChild(c.getString(0)));
+ }
+ c.close();
+ }
+
+ mChildren.set(groupPosition, children);
+ return children;
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public boolean isChildSelectable(int groupPosition, int childPosition) {
+ return true;
+ }
+
+ public int getGroupCount() {
+ return mCursor.getCount();
+ }
+
+ public Object getChild(int groupPosition, int childPosition) {
+ return null;
+ }
+
+ public long getChildId(int groupPosition, int childPosition) {
+ return childPosition;
+ }
+
+ public int getChildrenCount(int groupPosition) {
+ return getChildrenOfGroup(groupPosition).size();
+ }
+
+ public Object getGroup(int position) {
+ return position;
+ }
+
+ public long getGroupId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
+ }
+
+ public int getKeyRingId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(0); // _ID
+ }
+
+ public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
+ ViewGroup parent) {
+ mCursor.moveToPosition(groupPosition);
+
+ View view = mInflater.inflate(R.layout.key_list_group_item, null);
+ view.setBackgroundResource(android.R.drawable.list_selector_background);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText("");
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = mCursor.getString(2); // USER_ID
+ if (userId != null) {
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mainUserIdRest.setText("<" + chunks[1]);
+ }
+ mainUserId.setText(userId);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknownUserId);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+ return view;
+ }
+
+ public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
+ View convertView, ViewGroup parent) {
+ mCursor.moveToPosition(groupPosition);
+
+ Vector children = getChildrenOfGroup(groupPosition);
+
+ KeyChild child = children.get(childPosition);
+ View view = null;
+ switch (child.type) {
+ case KeyChild.KEY: {
+ if (child.isMasterKey) {
+ view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
+ } else {
+ view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
+ }
+
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ String keyIdStr = Apg.getSmallFingerPrint(child.keyId);
+ keyId.setText(keyIdStr);
+ TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
+ keyDetails.setText("(" + algorithmStr + ")");
+
+ ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
+ if (!child.canEncrypt) {
+ encryptIcon.setVisibility(View.GONE);
+ }
+
+ ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ if (!child.canSign) {
+ signIcon.setVisibility(View.GONE);
+ }
+ break;
+ }
+
+ case KeyChild.USER_ID: {
+ view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
+ TextView userId = (TextView) view.findViewById(R.id.userId);
+ userId.setText(child.userId);
+ break;
+ }
+
+ case KeyChild.FINGER_PRINT: {
+ view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
+ TextView userId = (TextView) view.findViewById(R.id.userId);
+ userId.setText(getString(R.string.fingerprint) + ":\n"
+ + child.fingerPrint.replace(" ", "\n"));
+ break;
+ }
+ }
+ return view;
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.filename: {
+ if (resultCode == RESULT_OK && data != null) {
+ String filename = data.getDataString();
+ if (filename != null) {
+ // Get rid of URI prefix:
+ if (filename.startsWith("file://")) {
+ filename = filename.substring(7);
+ }
+ // replace %20 and so on
+ filename = Uri.decode(filename);
+
+ FileDialog.setFilename(filename);
+ }
+ }
+ return;
+ }
+
+ default: {
+ break;
+ }
+ }
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/org_apg/src/org/apg/ui/KeyServerPreferenceActivity.java b/org_apg/src/org/apg/ui/KeyServerPreferenceActivity.java
new file mode 100644
index 000000000..85d31779a
--- /dev/null
+++ b/org_apg/src/org/apg/ui/KeyServerPreferenceActivity.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import java.util.Vector;
+
+import org.apg.Apg;
+import org.apg.ui.widget.Editor;
+import org.apg.ui.widget.KeyServerEditor;
+import org.apg.ui.widget.Editor.EditorListener;
+import org.apg.R;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class KeyServerPreferenceActivity extends BaseActivity
+ implements OnClickListener, EditorListener {
+ private LayoutInflater mInflater;
+ private ViewGroup mEditors;
+ private View mAdd;
+ private TextView mTitle;
+ private TextView mSummary;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.key_server_preference);
+
+ mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ mTitle = (TextView) findViewById(R.id.title);
+ mSummary = (TextView) findViewById(R.id.summary);
+
+ mTitle.setText(R.string.label_keyServers);
+
+ mEditors = (ViewGroup) findViewById(R.id.editors);
+ mAdd = findViewById(R.id.add);
+ mAdd.setOnClickListener(this);
+
+ Intent intent = getIntent();
+ String servers[] = intent.getStringArrayExtra(Apg.EXTRA_KEY_SERVERS);
+ if (servers != null) {
+ for (int i = 0; i < servers.length; ++i) {
+ KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, mEditors, false);
+ view.setEditorListener(this);
+ view.setValue(servers[i]);
+ mEditors.addView(view);
+ }
+ }
+
+ Button okButton = (Button) findViewById(R.id.btn_ok);
+ okButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ okClicked();
+ }
+ });
+
+ Button cancelButton = (Button) findViewById(R.id.btn_cancel);
+ cancelButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ cancelClicked();
+ }
+ });
+ }
+
+ public void onDeleted(Editor editor) {
+ // nothing to do
+ }
+
+ public void onClick(View v) {
+ KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, mEditors, false);
+ view.setEditorListener(this);
+ mEditors.addView(view);
+ }
+
+ private void cancelClicked() {
+ setResult(RESULT_CANCELED, null);
+ finish();
+ }
+
+ private void okClicked() {
+ Intent data = new Intent();
+ Vector servers = new Vector();
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
+ String tmp = editor.getValue();
+ if (tmp.length() > 0) {
+ servers.add(tmp);
+ }
+ }
+ String[] dummy = new String[0];
+ data.putExtra(Apg.EXTRA_KEY_SERVERS, servers.toArray(dummy));
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // override this, so no option menu is added (as would be in BaseActivity), since
+ // we're still in preferences
+ return true;
+ }
+}
diff --git a/org_apg/src/org/apg/ui/KeyServerQueryActivity.java b/org_apg/src/org/apg/ui/KeyServerQueryActivity.java
new file mode 100644
index 000000000..606acb575
--- /dev/null
+++ b/org_apg/src/org/apg/ui/KeyServerQueryActivity.java
@@ -0,0 +1,297 @@
+package org.apg.ui;
+
+import java.util.List;
+import java.util.Vector;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.HkpKeyServer;
+import org.apg.Id;
+import org.apg.KeyServer.InsufficientQuery;
+import org.apg.KeyServer.KeyInfo;
+import org.apg.KeyServer.QueryException;
+import org.apg.KeyServer.TooManyResponses;
+import org.apg.R;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class KeyServerQueryActivity extends BaseActivity {
+ private ListView mList;
+ private EditText mQuery;
+ private Button mSearch;
+ private KeyInfoListAdapter mAdapter;
+ private Spinner mKeyServer;
+
+ private int mQueryType;
+ private String mQueryString;
+ private long mQueryId;
+ private volatile List mSearchResult;
+ private volatile String mKeyData;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.key_server_query_layout);
+
+ mQuery = (EditText) findViewById(R.id.query);
+ mSearch = (Button) findViewById(R.id.btn_search);
+ mList = (ListView) findViewById(R.id.list);
+ mAdapter = new KeyInfoListAdapter(this);
+ mList.setAdapter(mAdapter);
+
+ mKeyServer = (Spinner) findViewById(R.id.keyServer);
+ ArrayAdapter adapter =
+ new ArrayAdapter(this,
+ android.R.layout.simple_spinner_item,
+ mPreferences.getKeyServers());
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mKeyServer.setAdapter(adapter);
+ if (adapter.getCount() > 0) {
+ mKeyServer.setSelection(0);
+ } else {
+ mSearch.setEnabled(false);
+ }
+
+ mList.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView> adapter, View view, int position, long keyId) {
+ get(keyId);
+ }
+ });
+
+ mSearch.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ String query = mQuery.getText().toString();
+ search(query);
+ }
+ });
+
+ Intent intent = getIntent();
+ if (Apg.Intent.LOOK_UP_KEY_ID.equals(intent.getAction()) ||
+ Apg.Intent.LOOK_UP_KEY_ID_AND_RETURN.equals(intent.getAction())) {
+ long keyId = intent.getLongExtra(Apg.EXTRA_KEY_ID, 0);
+ if (keyId != 0) {
+ String query = "0x" + Apg.keyToHex(keyId);
+ mQuery.setText(query);
+ search(query);
+ }
+ }
+ }
+
+ private void search(String query) {
+ showDialog(Id.dialog.querying);
+ mQueryType = Id.keyserver.search;
+ mQueryString = query;
+ mAdapter.setKeys(new Vector());
+ startThread();
+ }
+
+ private void get(long keyId) {
+ showDialog(Id.dialog.querying);
+ mQueryType = Id.keyserver.get;
+ mQueryId = keyId;
+ startThread();
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ ProgressDialog progress = (ProgressDialog) super.onCreateDialog(id);
+ progress.setMessage(this.getString(R.string.progress_queryingServer,
+ (String)mKeyServer.getSelectedItem()));
+ return progress;
+ }
+
+ @Override
+ public void run() {
+ String error = null;
+ Bundle data = new Bundle();
+ Message msg = new Message();
+
+ try {
+ HkpKeyServer server = new HkpKeyServer((String)mKeyServer.getSelectedItem());
+ if (mQueryType == Id.keyserver.search) {
+ mSearchResult = server.search(mQueryString);
+ } else if (mQueryType == Id.keyserver.get) {
+ mKeyData = server.get(mQueryId);
+ }
+ } catch (QueryException e) {
+ error = "" + e;
+ } catch (InsufficientQuery e) {
+ error = "Insufficient query.";
+ } catch (TooManyResponses e) {
+ error = "Too many responses.";
+ }
+
+ data.putInt(Constants.extras.STATUS, Id.message.done);
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ removeDialog(Id.dialog.querying);
+
+ Bundle data = msg.getData();
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ if (mQueryType == Id.keyserver.search) {
+ if (mSearchResult != null) {
+ Toast.makeText(this, getString(R.string.keysFound, mSearchResult.size()), Toast.LENGTH_SHORT).show();
+ mAdapter.setKeys(mSearchResult);
+ }
+ } else if (mQueryType == Id.keyserver.get) {
+ Intent orgIntent = getIntent();
+ if (Apg.Intent.LOOK_UP_KEY_ID_AND_RETURN.equals(orgIntent.getAction())) {
+ if (mKeyData != null) {
+ Intent intent = new Intent();
+ intent.putExtra(Apg.EXTRA_TEXT, mKeyData);
+ setResult(RESULT_OK, intent);
+ } else {
+ setResult(RESULT_CANCELED);
+ }
+ finish();
+ } else {
+ if (mKeyData != null) {
+ Intent intent = new Intent(this, PublicKeyListActivity.class);
+ intent.setAction(Apg.Intent.IMPORT);
+ intent.putExtra(Apg.EXTRA_TEXT, mKeyData);
+ startActivity(intent);
+ }
+ }
+ }
+ }
+
+ public class KeyInfoListAdapter extends BaseAdapter {
+ protected LayoutInflater mInflater;
+ protected Activity mActivity;
+ protected List mKeys;
+
+ public KeyInfoListAdapter(Activity activity) {
+ mActivity = activity;
+ mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mKeys = new Vector();
+ }
+
+ public void setKeys(List keys) {
+ mKeys = keys;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mKeys.size();
+ }
+
+ public Object getItem(int position) {
+ return mKeys.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mKeys.get(position).keyId;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ KeyInfo keyInfo = mKeys.get(position);
+
+ View view = mInflater.inflate(R.layout.key_server_query_result_item, null);
+
+ 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 algorithm = (TextView) view.findViewById(R.id.algorithm);
+ algorithm.setText("");
+ TextView status = (TextView) view.findViewById(R.id.status);
+ status.setText("");
+
+ String userId = keyInfo.userIds.get(0);
+ if (userId != null) {
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mainUserIdRest.setText("<" + chunks[1]);
+ }
+ mainUserId.setText(userId);
+ }
+
+ keyId.setText(Apg.getSmallFingerPrint(keyInfo.keyId));
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+
+ algorithm.setText("" + keyInfo.size + "/" + keyInfo.algorithm);
+
+ if (keyInfo.revoked != null) {
+ status.setText("revoked");
+ } else {
+ status.setVisibility(View.GONE);
+ }
+
+ LinearLayout ll = (LinearLayout) view.findViewById(R.id.list);
+ if (keyInfo.userIds.size() == 1) {
+ ll.setVisibility(View.GONE);
+ } else {
+ boolean first = true;
+ boolean second = true;
+ for (String uid : keyInfo.userIds) {
+ if (first) {
+ first = false;
+ continue;
+ }
+ if (!second) {
+ View sep = new View(mActivity);
+ sep.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 1));
+ sep.setBackgroundResource(android.R.drawable.divider_horizontal_dark);
+ ll.addView(sep);
+ }
+ TextView uidView = (TextView) mInflater.inflate(R.layout.key_server_query_result_user_id, null);
+ uidView.setText(uid);
+ ll.addView(uidView);
+ second = false;
+ }
+ }
+
+ return view;
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/ui/MailListActivity.java b/org_apg/src/org/apg/ui/MailListActivity.java
new file mode 100644
index 000000000..ad1d08068
--- /dev/null
+++ b/org_apg/src/org/apg/ui/MailListActivity.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import java.util.Vector;
+import java.util.regex.Matcher;
+
+import org.apg.Apg;
+import org.apg.Preferences;
+import org.apg.R;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Html;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+public class MailListActivity extends ListActivity {
+ LayoutInflater mInflater = null;
+
+ public static final String EXTRA_ACCOUNT = "account";
+
+ private static class Conversation {
+ public long id;
+ public String subject;
+ public Vector messages;
+
+ public Conversation(long id, String subject) {
+ this.id = id;
+ this.subject = subject;
+ }
+ }
+
+ 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, boolean signedOnly) {
+ this.parent = parent;
+ this.id = id;
+ this.subject = subject;
+ this.fromAddress = fromAddress;
+ this.replyTo = replyTo;
+ this.data = data;
+ if (this.replyTo == null || this.replyTo.equals("")) {
+ this.replyTo = this.fromAddress;
+ }
+ this.signedOnly = signedOnly;
+ }
+ }
+
+ private Vector mConversations;
+ private Vector mMessages;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Preferences prefs = Preferences.getPreferences(this);
+ BaseActivity.setLanguage(this, prefs.getLanguage());
+
+ super.onCreate(savedInstanceState);
+
+ mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ mConversations = new Vector();
+ mMessages = new Vector();
+
+ String account = getIntent().getExtras().getString(EXTRA_ACCOUNT);
+ // TODO: what if account is null?
+ Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
+ Cursor cursor =
+ managedQuery(uri, new String[] { "conversation_id", "subject" }, null, null, null);
+ for (int i = 0; i < cursor.getCount(); ++i) {
+ cursor.moveToPosition(i);
+
+ int idIndex = cursor.getColumnIndex("conversation_id");
+ int subjectIndex = cursor.getColumnIndex("subject");
+ long conversationId = cursor.getLong(idIndex);
+ Conversation conversation =
+ new Conversation(conversationId, cursor.getString(subjectIndex));
+ Uri messageUri = Uri.withAppendedPath(uri, "" + conversationId + "/messages");
+ Cursor messageCursor =
+ managedQuery(messageUri, new String[] {
+ "messageId",
+ "subject",
+ "fromAddress",
+ "replyToAddresses",
+ "body" }, null, null, null);
+ Vector messages = new Vector();
+ for (int j = 0; j < messageCursor.getCount(); ++j) {
+ messageCursor.moveToPosition(j);
+ idIndex = messageCursor.getColumnIndex("messageId");
+ subjectIndex = messageCursor.getColumnIndex("subject");
+ int fromAddressIndex = messageCursor.getColumnIndex("fromAddress");
+ int replyToIndex = messageCursor.getColumnIndex("replyToAddresses");
+ 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 {
+ 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, signedOnly);
+
+ messages.add(message);
+ mMessages.add(message);
+ }
+ conversation.messages = messages;
+ mConversations.add(conversation);
+ }
+
+ setListAdapter(new MailboxAdapter());
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView> arg0, View v, int position, long id) {
+ Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
+ intent.setAction(Apg.Intent.DECRYPT);
+ Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
+ intent.putExtra(Apg.EXTRA_TEXT, message.data);
+ intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
+ intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
+ startActivity(intent);
+ }
+ });
+ }
+
+ private class MailboxAdapter extends BaseAdapter implements ListAdapter {
+
+ @Override
+ public boolean isEnabled(int position) {
+ Message message = (Message) getItem(position);
+ return message.data != null;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mMessages.size();
+ }
+
+ public Object getItem(int position) {
+ return mMessages.get(position);
+ }
+
+ public long getItemId(int position) {
+ return mMessages.get(position).id;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ 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.emailAddress);
+ ImageView status = (ImageView) view.findViewById(R.id.ic_status);
+
+ subject.setText(message.subject);
+ email.setText(message.fromAddress);
+ if (message.data != null) {
+ if (message.signedOnly) {
+ status.setImageResource(R.drawable.signed);
+ } else {
+ status.setImageResource(R.drawable.encrypted);
+ }
+ status.setVisibility(View.VISIBLE);
+ } else {
+ status.setVisibility(View.INVISIBLE);
+ }
+
+ return view;
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/ui/MainActivity.java b/org_apg/src/org/apg/ui/MainActivity.java
new file mode 100644
index 000000000..8c985c2ac
--- /dev/null
+++ b/org_apg/src/org/apg/ui/MainActivity.java
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import java.security.Security;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.Id.dialog;
+import org.apg.Id.menu;
+import org.apg.Id.menu.option;
+import org.apg.provider.Accounts;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.apg.R;
+
+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.database.Cursor;
+import android.database.SQLException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.util.Linkify;
+import android.text.util.Linkify.TransformFilter;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+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;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.Button;
+import android.widget.CursorAdapter;
+import android.widget.EditText;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class MainActivity extends BaseActivity {
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ private ListView mAccounts = null;
+ private AccountListAdapter mListAdapter = null;
+ private Cursor mAccountCursor;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ Button encryptMessageButton = (Button) findViewById(R.id.btn_encryptMessage);
+ Button decryptMessageButton = (Button) findViewById(R.id.btn_decryptMessage);
+ 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() {
+ public void onClick(View v) {
+ Intent intent = new Intent(MainActivity.this, EncryptActivity.class);
+ intent.setAction(Apg.Intent.ENCRYPT);
+ startActivity(intent);
+ }
+ });
+
+ decryptMessageButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(MainActivity.this, DecryptActivity.class);
+ intent.setAction(Apg.Intent.DECRYPT);
+ startActivity(intent);
+ }
+ });
+
+ encryptFileButton.setOnClickListener(new OnClickListener() {
+ 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() {
+ public void onClick(View v) {
+ Intent intent = new Intent(MainActivity.this, DecryptActivity.class);
+ intent.setAction(Apg.Intent.DECRYPT_FILE);
+ startActivity(intent);
+ }
+ });
+
+ mAccountCursor =
+ Apg.getDatabase().db().query(Accounts.TABLE_NAME,
+ new String[] {
+ Accounts._ID,
+ Accounts.NAME,
+ }, null, null, null, null, Accounts.NAME + " ASC");
+ startManagingCursor(mAccountCursor);
+
+ mListAdapter = new AccountListAdapter(this, mAccountCursor);
+ mAccounts.setAdapter(mListAdapter);
+ mAccounts.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView> arg0, View view, int index, long id) {
+ String accountName = (String) mAccounts.getItemAtPosition(index);
+ startActivity(new Intent(MainActivity.this, MailListActivity.class)
+ .putExtra(MailListActivity.EXTRA_ACCOUNT, accountName));
+ }
+ });
+ registerForContextMenu(mAccounts);
+
+ if (!mPreferences.hasSeenHelp()) {
+ showDialog(Id.dialog.help);
+ }
+
+ if (Apg.isReleaseVersion(this) && !mPreferences.hasSeenChangeLog(Apg.getVersion(this))) {
+ showDialog(Id.dialog.change_log);
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case Id.dialog.new_account: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setTitle(R.string.title_addAccount);
+ alert.setMessage(R.string.specifyGoogleMailAccount);
+
+ LayoutInflater inflater =
+ (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.add_account_dialog, null);
+
+ final EditText input = (EditText) view.findViewById(R.id.input);
+ alert.setView(view);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ MainActivity.this.removeDialog(Id.dialog.new_account);
+ String accountName = "" + input.getText();
+
+ try {
+ 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;
+ }
+ } catch (SecurityException e) {
+ Toast.makeText(MainActivity.this,
+ getString(R.string.errorMessage,
+ getString(R.string.error_accountReadingNotAllowed)),
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Accounts.NAME, accountName);
+ try {
+ Apg.getDatabase().db().insert(Accounts.TABLE_NAME,
+ Accounts.NAME, values);
+ mAccountCursor.requery();
+ mListAdapter.notifyDataSetChanged();
+ } catch (SQLException e) {
+ Toast.makeText(MainActivity.this,
+ getString(R.string.errorMessage,
+ 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(Id.dialog.new_account);
+ }
+ });
+
+ return alert.create();
+ }
+
+ case Id.dialog.change_log: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setTitle("Changes " + Apg.getFullVersion(this));
+ 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("Changes:\n" +
+ "* \n" +
+ "\n" +
+ "WARNING: be careful editing your existing keys, as they " +
+ "WILL be stripped of certificates right now.\n" +
+ "\n" +
+ "Also: 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(Id.dialog.change_log);
+ mPreferences.setHasSeenChangeLog(
+ Apg.getVersion(MainActivity.this), true);
+ }
+ });
+
+ return alert.create();
+ }
+
+ case Id.dialog.help: {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+
+ alert.setTitle(R.string.title_help);
+
+ 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(R.string.text_help);
+
+ TransformFilter packageNames = new TransformFilter() {
+ public final String transformUrl(final Matcher match, String url) {
+ String name = match.group(1).toLowerCase();
+ if (name.equals("astro")) {
+ return "com.metago.astro";
+ } else if (name.equals("k-9 mail")) {
+ return "com.fsck.k9";
+ } else {
+ return "org.openintents.filemanager";
+ }
+ }
+ };
+
+ Pattern pattern = Pattern.compile("(OI File Manager|ASTRO|K-9 Mail)");
+ String scheme = "market://search?q=pname:";
+ message.setAutoLinkMask(0);
+ Linkify.addLinks(message, pattern, scheme, null, packageNames);
+
+ alert.setView(layout);
+
+ alert.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ MainActivity.this.removeDialog(Id.dialog.help);
+ mPreferences.setHasSeenHelp(true);
+ }
+ });
+
+ return alert.create();
+ }
+
+ default: {
+ return super.onCreateDialog(id);
+ }
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.manage_public_keys, 0, R.string.menu_managePublicKeys)
+ .setIcon(android.R.drawable.ic_menu_manage);
+ menu.add(0, Id.menu.option.manage_secret_keys, 1, R.string.menu_manageSecretKeys)
+ .setIcon(android.R.drawable.ic_menu_manage);
+ menu.add(1, Id.menu.option.create, 2, R.string.menu_addAccount)
+ .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.key_server, 4, R.string.menu_keyServer)
+ .setIcon(android.R.drawable.ic_menu_search);
+ menu.add(3, Id.menu.option.about, 5, R.string.menu_about)
+ .setIcon(android.R.drawable.ic_menu_info_details);
+ menu.add(3, Id.menu.option.help, 6, R.string.menu_help)
+ .setIcon(android.R.drawable.ic_menu_help);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Id.menu.option.create: {
+ showDialog(Id.dialog.new_account);
+ return true;
+ }
+
+ case Id.menu.option.manage_public_keys: {
+ startActivity(new Intent(this, PublicKeyListActivity.class));
+ return true;
+ }
+
+ case Id.menu.option.manage_secret_keys: {
+ startActivity(new Intent(this, SecretKeyListActivity.class));
+ return true;
+ }
+
+ case Id.menu.option.help: {
+ showDialog(Id.dialog.help);
+ return true;
+ }
+
+ case Id.menu.option.key_server: {
+ startActivity(new Intent(this, KeyServerQueryActivity.class));
+ return true;
+ }
+
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+
+ TextView nameTextView = (TextView) v.findViewById(R.id.accountName);
+ if (nameTextView != null) {
+ menu.setHeaderTitle(nameTextView.getText());
+ menu.add(0, Id.menu.delete, 0, R.string.menu_deleteAccount);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ AdapterView.AdapterContextMenuInfo info =
+ (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
+
+ switch (menuItem.getItemId()) {
+ case Id.menu.delete: {
+ Apg.getDatabase().db().delete(Accounts.TABLE_NAME,
+ Accounts._ID + " = ?",
+ new String[] { "" + info.id });
+ mAccountCursor.requery();
+ mListAdapter.notifyDataSetChanged();
+ return true;
+ }
+
+ default: {
+ return super.onContextItemSelected(menuItem);
+ }
+ }
+ }
+
+
+ private static class AccountListAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public AccountListAdapter(Context context, Cursor cursor) {
+ super(context, cursor);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public Object getItem(int position) {
+ Cursor c = getCursor();
+ c.moveToPosition(position);
+ return c.getString(c.getColumnIndex(Accounts.NAME));
+ }
+
+ @Override
+ public int getCount() {
+ return super.getCount();
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.account_item, null);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView nameTextView = (TextView) view.findViewById(R.id.accountName);
+ int nameIndex = cursor.getColumnIndex(Accounts.NAME);
+ final String account = cursor.getString(nameIndex);
+ nameTextView.setText(account);
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/org_apg/src/org/apg/ui/PreferencesActivity.java b/org_apg/src/org/apg/ui/PreferencesActivity.java
new file mode 100644
index 000000000..421c9cc39
--- /dev/null
+++ b/org_apg/src/org/apg/ui/PreferencesActivity.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.Id;
+import org.apg.Preferences;
+import org.apg.Constants.pref;
+import org.apg.Id.choice;
+import org.apg.Id.request;
+import org.apg.Id.choice.compression;
+import org.apg.ui.widget.IntegerListPreference;
+import org.spongycastle.bcpg.HashAlgorithmTags;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.apg.R;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceScreen;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Vector;
+
+public class PreferencesActivity extends PreferenceActivity {
+ private ListPreference mLanguage = null;
+ private IntegerListPreference mPassPhraseCacheTtl = null;
+ private IntegerListPreference mEncryptionAlgorithm = null;
+ private IntegerListPreference mHashAlgorithm = null;
+ private IntegerListPreference mMessageCompression = null;
+ private IntegerListPreference mFileCompression = null;
+ private CheckBoxPreference mAsciiArmour = null;
+ private CheckBoxPreference mForceV3Signatures = null;
+ private PreferenceScreen mKeyServerPreference = null;
+ private Preferences mPreferences;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ mPreferences = Preferences.getPreferences(this);
+ BaseActivity.setLanguage(this, mPreferences.getLanguage());
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.apg_preferences);
+
+ mLanguage = (ListPreference) findPreference(Constants.pref.LANGUAGE);
+ Vector entryVector = new Vector(Arrays.asList(mLanguage.getEntries()));
+ Vector entryValueVector = new Vector(Arrays.asList(mLanguage.getEntryValues()));
+ String supportedLanguages[] = getResources().getStringArray(R.array.supported_languages);
+ HashSet supportedLanguageSet = new HashSet(Arrays.asList(supportedLanguages));
+ for (int i = entryVector.size() - 1; i > -1; --i)
+ {
+ if (!supportedLanguageSet.contains(entryValueVector.get(i)))
+ {
+ entryVector.remove(i);
+ entryValueVector.remove(i);
+ }
+ }
+ CharSequence dummy[] = new CharSequence[0];
+ mLanguage.setEntries(entryVector.toArray(dummy));
+ mLanguage.setEntryValues(entryValueVector.toArray(dummy));
+ mLanguage.setValue(mPreferences.getLanguage());
+ mLanguage.setSummary(mLanguage.getEntry());
+ mLanguage.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mLanguage.setValue(newValue.toString());
+ mLanguage.setSummary(mLanguage.getEntry());
+ mPreferences.setLanguage(newValue.toString());
+ return false;
+ }
+ });
+
+ mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL);
+ mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
+ mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
+ mPassPhraseCacheTtl.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mPassPhraseCacheTtl.setValue(newValue.toString());
+ mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
+ mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
+ BaseActivity.startCacheService(PreferencesActivity.this, mPreferences);
+ return false;
+ }
+ });
+
+ mEncryptionAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM);
+ int valueIds[] = {
+ PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, PGPEncryptedData.AES_256,
+ PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, PGPEncryptedData.CAST5,
+ PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, PGPEncryptedData.IDEA,
+ };
+ String entries[] = {
+ "AES-128", "AES-192", "AES-256",
+ "Blowfish", "Twofish", "CAST5",
+ "DES", "Triple DES", "IDEA",
+ };
+ String values[] = new String[valueIds.length];
+ for (int i = 0; i < values.length; ++i) {
+ values[i] = "" + valueIds[i];
+ }
+ mEncryptionAlgorithm.setEntries(entries);
+ mEncryptionAlgorithm.setEntryValues(values);
+ mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm());
+ mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
+ mEncryptionAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mEncryptionAlgorithm.setValue(newValue.toString());
+ mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
+ mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mHashAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM);
+ valueIds = new int[] {
+ HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, HashAlgorithmTags.SHA1,
+ HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA384,
+ HashAlgorithmTags.SHA512,
+ };
+ entries = new String[] {
+ "MD5", "RIPEMD-160", "SHA-1",
+ "SHA-224", "SHA-256", "SHA-384",
+ "SHA-512",
+ };
+ values = new String[valueIds.length];
+ for (int i = 0; i < values.length; ++i) {
+ values[i] = "" + valueIds[i];
+ }
+ mHashAlgorithm.setEntries(entries);
+ mHashAlgorithm.setEntryValues(values);
+ mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm());
+ mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
+ mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mHashAlgorithm.setValue(newValue.toString());
+ mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
+ mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION);
+ valueIds = new int[] {
+ Id.choice.compression.none,
+ Id.choice.compression.zip,
+ Id.choice.compression.zlib,
+ Id.choice.compression.bzip2,
+ };
+ entries = new String[] {
+ getString(R.string.choice_none) + " (" + getString(R.string.fast) + ")",
+ "ZIP (" + getString(R.string.fast) + ")",
+ "ZLIB (" + getString(R.string.fast) + ")",
+ "BZIP2 (" + getString(R.string.very_slow) + ")",
+ };
+ values = new String[valueIds.length];
+ for (int i = 0; i < values.length; ++i) {
+ values[i] = "" + valueIds[i];
+ }
+ mMessageCompression.setEntries(entries);
+ mMessageCompression.setEntryValues(values);
+ mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
+ mMessageCompression.setSummary(mMessageCompression.getEntry());
+ mMessageCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mMessageCompression.setValue(newValue.toString());
+ mMessageCompression.setSummary(mMessageCompression.getEntry());
+ mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mFileCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION);
+ mFileCompression.setEntries(entries);
+ mFileCompression.setEntryValues(values);
+ mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
+ mFileCompression.setSummary(mFileCompression.getEntry());
+ mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mFileCompression.setValue(newValue.toString());
+ mFileCompression.setSummary(mFileCompression.getEntry());
+ mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
+ return false;
+ }
+ });
+
+ mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR);
+ mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
+ mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mAsciiArmour.setChecked((Boolean)newValue);
+ mPreferences.setDefaultAsciiArmour((Boolean)newValue);
+ return false;
+ }
+ });
+
+ mForceV3Signatures = (CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES);
+ mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
+ mForceV3Signatures.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
+ {
+ public boolean onPreferenceChange(Preference preference, Object newValue)
+ {
+ mForceV3Signatures.setChecked((Boolean)newValue);
+ mPreferences.setForceV3Signatures((Boolean)newValue);
+ return false;
+ }
+ });
+
+ mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS);
+ String servers[] = mPreferences.getKeyServers();
+ mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, servers.length));
+ mKeyServerPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(PreferencesActivity.this,
+ KeyServerPreferenceActivity.class);
+ intent.putExtra(Apg.EXTRA_KEY_SERVERS, mPreferences.getKeyServers());
+ startActivityForResult(intent, Id.request.key_server_preference);
+ return false;
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.key_server_preference: {
+ if (resultCode == RESULT_CANCELED || data == null) {
+ return;
+ }
+ String servers[] = data.getStringArrayExtra(Apg.EXTRA_KEY_SERVERS);
+ mPreferences.setKeyServers(servers);
+ mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, servers.length));
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+ break;
+ }
+ }
+ }
+}
+
diff --git a/org_apg/src/org/apg/ui/PublicKeyListActivity.java b/org_apg/src/org/apg/ui/PublicKeyListActivity.java
new file mode 100644
index 000000000..81a79ce33
--- /dev/null
+++ b/org_apg/src/org/apg/ui/PublicKeyListActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.Id;
+import org.apg.Constants.path;
+import org.apg.Id.menu;
+import org.apg.Id.request;
+import org.apg.Id.type;
+import org.apg.Id.menu.option;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.apg.R;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+
+public class PublicKeyListActivity extends KeyListActivity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mExportFilename = Constants.path.APP_DIR + "/pubexport.asc";
+ mKeyType = Id.type.public_key;
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys).setIcon(
+ android.R.drawable.ic_menu_add);
+ 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.search, 2, R.string.menu_search).setIcon(
+ android.R.drawable.ic_menu_search);
+ menu.add(1, Id.menu.option.preferences, 3, R.string.menu_preferences).setIcon(
+ android.R.drawable.ic_menu_preferences);
+ menu.add(1, Id.menu.option.about, 4, R.string.menu_about).setIcon(
+ android.R.drawable.ic_menu_info_details);
+ menu.add(1, Id.menu.option.scanQRCode, 5, R.string.menu_scanQRCode).setIcon(
+ android.R.drawable.ic_menu_add);
+ return true;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
+ int type = ExpandableListView.getPackedPositionType(info.packedPosition);
+
+ if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ // TODO: user id? menu.setHeaderTitle("Key");
+ menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
+ menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
+ menu.add(0, Id.menu.update, 1, R.string.menu_updateKey);
+ menu.add(0, Id.menu.exportToServer, 1, R.string.menu_exportKeyToServer);
+ menu.add(0, Id.menu.signKey, 1, R.string.menu_signKey);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
+ int type = ExpandableListView.getPackedPositionType(info.packedPosition);
+ int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+
+ if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ return super.onContextItemSelected(menuItem);
+ }
+
+ switch (menuItem.getItemId()) {
+ case Id.menu.update: {
+ mSelectedItem = groupPosition;
+ final int keyRingId = mListAdapter.getKeyRingId(groupPosition);
+ long keyId = 0;
+ Object keyRing = Apg.getKeyRing(keyRingId);
+ if (keyRing != null && keyRing instanceof PGPPublicKeyRing) {
+ keyId = Apg.getMasterKey((PGPPublicKeyRing) keyRing).getKeyID();
+ }
+ if (keyId == 0) {
+ // this shouldn't happen
+ return true;
+ }
+
+ Intent intent = new Intent(this, KeyServerQueryActivity.class);
+ intent.setAction(Apg.Intent.LOOK_UP_KEY_ID_AND_RETURN);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
+ startActivityForResult(intent, Id.request.look_up_key_id);
+
+ return true;
+ }
+
+ case Id.menu.exportToServer: {
+ mSelectedItem = groupPosition;
+ final int keyRingId = mListAdapter.getKeyRingId(groupPosition);
+
+ Intent intent = new Intent(this, SendKeyActivity.class);
+ intent.setAction(Apg.Intent.EXPORT_KEY_TO_SERVER);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyRingId);
+ startActivityForResult(intent, Id.request.export_to_server);
+
+ return true;
+ }
+
+ case Id.menu.signKey: {
+ mSelectedItem = groupPosition;
+ final int keyRingId = mListAdapter.getKeyRingId(groupPosition);
+ long keyId = 0;
+ Object keyRing = Apg.getKeyRing(keyRingId);
+ if (keyRing != null && keyRing instanceof PGPPublicKeyRing) {
+ keyId = Apg.getMasterKey((PGPPublicKeyRing) keyRing).getKeyID();
+ }
+
+ if (keyId == 0) {
+ // this shouldn't happen
+ return true;
+ }
+
+ Intent intent = new Intent(this, SignKeyActivity.class);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
+ startActivity(intent);
+
+ return true;
+ }
+
+ default: {
+ return super.onContextItemSelected(menuItem);
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case Id.menu.option.scanQRCode: {
+ Intent intent = new Intent(this, ImportFromQRCodeActivity.class);
+ intent.setAction(Apg.Intent.IMPORT_FROM_QR_CODE);
+ startActivityForResult(intent, Id.request.import_from_qr_code);
+
+ return true;
+ }
+
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.look_up_key_id: {
+ if (resultCode == RESULT_CANCELED || data == null
+ || data.getStringExtra(Apg.EXTRA_TEXT) == null) {
+ return;
+ }
+
+ Intent intent = new Intent(this, PublicKeyListActivity.class);
+ intent.setAction(Apg.Intent.IMPORT);
+ intent.putExtra(Apg.EXTRA_TEXT, data.getStringExtra(Apg.EXTRA_TEXT));
+ handleIntent(intent);
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+ break;
+ }
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/ui/SecretKeyListActivity.java b/org_apg/src/org/apg/ui/SecretKeyListActivity.java
new file mode 100644
index 000000000..a5d351bc6
--- /dev/null
+++ b/org_apg/src/org/apg/ui/SecretKeyListActivity.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.AskForSecretKeyPassPhrase;
+import org.apg.Constants;
+import org.apg.Id;
+import org.apg.Constants.path;
+import org.apg.Id.dialog;
+import org.apg.Id.menu;
+import org.apg.Id.message;
+import org.apg.Id.type;
+import org.apg.Id.menu.option;
+import org.apg.R;
+
+import android.app.Dialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ExpandableListView;
+import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.ExpandableListView.OnChildClickListener;
+
+import com.google.zxing.integration.android.IntentIntegrator;
+
+public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mExportFilename = Constants.path.APP_DIR + "/secexport.asc";
+ mKeyType = Id.type.secret_key;
+ super.onCreate(savedInstanceState);
+ mList.setOnChildClickListener(this);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
+ .setIcon(android.R.drawable.ic_menu_add);
+ 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.create, 2, R.string.menu_createKey)
+ .setIcon(android.R.drawable.ic_menu_add);
+ menu.add(3, Id.menu.option.search, 3, R.string.menu_search)
+ .setIcon(android.R.drawable.ic_menu_search);
+ menu.add(3, Id.menu.option.preferences, 4, R.string.menu_preferences)
+ .setIcon(android.R.drawable.ic_menu_preferences);
+ menu.add(3, Id.menu.option.about, 5, 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.create: {
+ createKey();
+ return true;
+ }
+
+ default: {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ ExpandableListView.ExpandableListContextMenuInfo info =
+ (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
+ int type = ExpandableListView.getPackedPositionType(info.packedPosition);
+
+ if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ // TODO: user id? menu.setHeaderTitle("Key");
+ menu.add(0, Id.menu.edit, 0, R.string.menu_editKey);
+ menu.add(0, Id.menu.export, 1, R.string.menu_exportKey);
+ menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
+ menu.add(0, Id.menu.share, 2, R.string.menu_share);
+ }
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem) {
+ ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
+ int type = ExpandableListView.getPackedPositionType(info.packedPosition);
+ int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
+
+ if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
+ return super.onContextItemSelected(menuItem);
+ }
+
+ switch (menuItem.getItemId()) {
+ case Id.menu.edit: {
+ mSelectedItem = groupPosition;
+ checkPassPhraseAndEdit();
+ return true;
+ }
+
+ case Id.menu.share: {
+ mSelectedItem = groupPosition;
+
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
+ String msg = keyId + "," + Apg.getFingerPrint(keyId);;
+
+ new IntentIntegrator(this).shareText(msg);
+ }
+
+ default: {
+ return super.onContextItemSelected(menuItem);
+ }
+ }
+ }
+
+ public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
+ int childPosition, long id) {
+ mSelectedItem = groupPosition;
+ checkPassPhraseAndEdit();
+ return true;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case Id.dialog.pass_phrase: {
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
+ return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
+ }
+
+ default: {
+ return super.onCreateDialog(id);
+ }
+ }
+ }
+
+ public void checkPassPhraseAndEdit() {
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
+ String passPhrase = Apg.getCachedPassPhrase(keyId);
+ if (passPhrase == null) {
+ showDialog(Id.dialog.pass_phrase);
+ } else {
+ Apg.setEditPassPhrase(passPhrase);
+ editKey();
+ }
+ }
+
+ @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, Id.message.create_key);
+ }
+
+ private void editKey() {
+ long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
+ Intent intent = new Intent(this, EditKeyActivity.class);
+ intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
+ startActivityForResult(intent, Id.message.edit_key);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.message.create_key: // intentionally no break
+ case Id.message.edit_key: {
+ if (resultCode == RESULT_OK) {
+ refreshList();
+ }
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+}
diff --git a/org_apg/src/org/apg/ui/SelectPublicKeyListActivity.java b/org_apg/src/org/apg/ui/SelectPublicKeyListActivity.java
new file mode 100644
index 000000000..5216e7a3d
--- /dev/null
+++ b/org_apg/src/org/apg/ui/SelectPublicKeyListActivity.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import java.util.Vector;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.Id.menu;
+import org.apg.Id.menu.option;
+import org.apg.R;
+
+import android.app.SearchManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SelectPublicKeyListActivity extends BaseActivity {
+ protected ListView mList;
+ protected SelectPublicKeyListAdapter mListAdapter;
+ protected View mFilterLayout;
+ protected Button mClearFilterButton;
+ protected TextView mFilterInfo;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.select_public_key);
+
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+
+ mList = (ListView) findViewById(R.id.list);
+ // needed in Android 1.5, where the XML attribute gets ignored
+ mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+
+ Button okButton = (Button) findViewById(R.id.btn_ok);
+ okButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ okClicked();
+ }
+ });
+
+ Button cancelButton = (Button) findViewById(R.id.btn_cancel);
+ cancelButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ cancelClicked();
+ }
+ });
+
+ mFilterLayout = findViewById(R.id.layout_filter);
+ mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
+ mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
+
+ mClearFilterButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ handleIntent(new Intent());
+ }
+ });
+
+ handleIntent(getIntent());
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleIntent(intent);
+ }
+
+ private void handleIntent(Intent intent) {
+ String searchString = null;
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ searchString = intent.getStringExtra(SearchManager.QUERY);
+ if (searchString != null && searchString.trim().length() == 0) {
+ searchString = null;
+ }
+ }
+
+ long selectedKeyIds[] = null;
+ selectedKeyIds = intent.getLongArrayExtra(Apg.EXTRA_SELECTION);
+
+ if (selectedKeyIds == null) {
+ Vector vector = new Vector();
+ for (int i = 0; i < mList.getCount(); ++i) {
+ if (mList.isItemChecked(i)) {
+ vector.add(mList.getItemIdAtPosition(i));
+ }
+ }
+ selectedKeyIds = new long[vector.size()];
+ for (int i = 0; i < vector.size(); ++i) {
+ selectedKeyIds[i] = vector.get(i);
+ }
+ }
+
+ if (searchString == null) {
+ mFilterLayout.setVisibility(View.GONE);
+ } else {
+ mFilterLayout.setVisibility(View.VISIBLE);
+ mFilterInfo.setText(getString(R.string.filterInfo, searchString));
+ }
+
+ if (mListAdapter != null) {
+ mListAdapter.cleanup();
+ }
+
+ mListAdapter = new SelectPublicKeyListAdapter(this, mList, searchString, selectedKeyIds);
+ mList.setAdapter(mListAdapter);
+
+ if (selectedKeyIds != null) {
+ for (int i = 0; i < mListAdapter.getCount(); ++i) {
+ long keyId = mListAdapter.getItemId(i);
+ for (int j = 0; j < selectedKeyIds.length; ++j) {
+ if (keyId == selectedKeyIds[j]) {
+ mList.setItemChecked(i, true);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private void cancelClicked() {
+ setResult(RESULT_CANCELED, null);
+ finish();
+ }
+
+ private void okClicked() {
+ Intent data = new Intent();
+ Vector keys = new Vector();
+ Vector userIds = new Vector();
+ for (int i = 0; i < mList.getCount(); ++i) {
+ if (mList.isItemChecked(i)) {
+ keys.add(mList.getItemIdAtPosition(i));
+ userIds.add((String) mList.getItemAtPosition(i));
+ }
+ }
+ long selectedKeyIds[] = new long[keys.size()];
+ for (int i = 0; i < keys.size(); ++i) {
+ selectedKeyIds[i] = keys.get(i);
+ }
+ String userIdArray[] = new String[0];
+ data.putExtra(Apg.EXTRA_SELECTION, selectedKeyIds);
+ data.putExtra(Apg.EXTRA_USER_IDS, userIds.toArray(userIdArray));
+ setResult(RESULT_OK, data);
+ finish();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.search, 0, R.string.menu_search)
+ .setIcon(android.R.drawable.ic_menu_search);
+ return true;
+ }
+}
diff --git a/org_apg/src/org/apg/ui/SelectPublicKeyListAdapter.java b/org_apg/src/org/apg/ui/SelectPublicKeyListAdapter.java
new file mode 100644
index 000000000..b2f49f74a
--- /dev/null
+++ b/org_apg/src/org/apg/ui/SelectPublicKeyListAdapter.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import java.util.Date;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.Id.database;
+import org.apg.provider.KeyRings;
+import org.apg.provider.Keys;
+import org.apg.provider.UserIds;
+import org.apg.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+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 LayoutInflater mInflater;
+ protected ListView mParent;
+ protected SQLiteDatabase mDatabase;
+ protected Cursor mCursor;
+ protected String mSearchString;
+ protected Activity mActivity;
+
+ public SelectPublicKeyListAdapter(Activity activity, ListView parent,
+ String searchString, long selectedKeyIds[]) {
+ mSearchString = searchString;
+
+ mActivity = activity;
+ mParent = parent;
+ mDatabase = Apg.getDatabase().db();
+ mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ long now = new Date().getTime() / 1000;
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
+
+ String inIdList = null;
+
+ if (selectedKeyIds != null && selectedKeyIds.length > 0) {
+ inIdList = KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " IN (";
+ for (int i = 0; i < selectedKeyIds.length; ++i) {
+ if (i != 0) {
+ inIdList += ", ";
+ }
+ inIdList += DatabaseUtils.sqlEscapeString("" + selectedKeyIds[i]);
+ }
+ inIdList += ")";
+ }
+
+ if (searchString != null && searchString.trim().length() > 0) {
+ String[] chunks = searchString.trim().split(" +");
+ qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
+ UserIds.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + UserIds.KEY_ID + " = " +
+ Keys.TABLE_NAME + "." + Keys._ID);
+ for (int i = 0; i < chunks.length; ++i) {
+ qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
+ qb.appendWhereEscapeString("%" + chunks[i] + "%");
+ }
+ qb.appendWhere("))");
+
+ if (inIdList != null) {
+ qb.appendWhere(" OR (" + inIdList + ")");
+ }
+ }
+
+ String orderBy = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
+ if (inIdList != null) {
+ orderBy = inIdList + " DESC, " + orderBy;
+ }
+
+ mCursor = qb.query(mDatabase,
+ new String[] {
+ KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_ENCRYPT + " = '1')", // 3
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_ENCRYPT + " = '1' AND " +
+ "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
+ "(tmp." + Keys.EXPIRY + " IS NULL OR " +
+ "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
+ },
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] { "" + Id.database.type_public },
+ null, null, orderBy);
+
+ activity.startManagingCursor(mCursor);
+ }
+
+ public void cleanup() {
+ if (mCursor != null) {
+ mActivity.stopManagingCursor(mCursor);
+ mCursor.close();
+ }
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ public Object getItem(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getString(2); // USER_ID
+ }
+
+ public long getItemId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ mCursor.moveToPosition(position);
+
+ View view = mInflater.inflate(R.layout.select_public_key_item, null);
+ boolean enabled = isEnabled(position);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknownUserId);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ keyId.setText(R.string.noKey);
+ TextView status = (TextView) view.findViewById(R.id.status);
+ status.setText(R.string.unknownStatus);
+
+ String userId = mCursor.getString(2); // USER_ID
+ if (userId != null) {
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mainUserIdRest.setText("<" + chunks[1]);
+ }
+ mainUserId.setText(userId);
+ }
+
+ long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
+ keyId.setText(Apg.getSmallFingerPrint(masterKeyId));
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+
+ if (enabled) {
+ status.setText(R.string.canEncrypt);
+ } else {
+ if (mCursor.getInt(3) > 0) {
+ // has some CAN_ENCRYPT keys, but col(4) = 0, so must be revoked or expired
+ status.setText(R.string.expired);
+ } else {
+ status.setText(R.string.noKey);
+ }
+ }
+
+ status.setText(status.getText() + " ");
+
+ CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
+
+ if (!enabled) {
+ mParent.setItemChecked(position, false);
+ }
+
+ selected.setChecked(mParent.isItemChecked(position));
+
+ view.setEnabled(enabled);
+ mainUserId.setEnabled(enabled);
+ mainUserIdRest.setEnabled(enabled);
+ keyId.setEnabled(enabled);
+ selected.setEnabled(enabled);
+ status.setEnabled(enabled);
+
+ return view;
+ }
+}
diff --git a/org_apg/src/org/apg/ui/SelectSecretKeyListActivity.java b/org_apg/src/org/apg/ui/SelectSecretKeyListActivity.java
new file mode 100644
index 000000000..191a0ecc7
--- /dev/null
+++ b/org_apg/src/org/apg/ui/SelectSecretKeyListActivity.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.Id.menu;
+import org.apg.Id.menu.option;
+import org.apg.R;
+
+import android.app.SearchManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SelectSecretKeyListActivity extends BaseActivity {
+ protected ListView mList;
+ protected SelectSecretKeyListAdapter mListAdapter;
+ protected View mFilterLayout;
+ protected Button mClearFilterButton;
+ protected TextView mFilterInfo;
+
+ protected long mSelectedKeyId = 0;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+
+ setContentView(R.layout.select_secret_key);
+
+ mList = (ListView) findViewById(R.id.list);
+
+ mList.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
+ Intent data = new Intent();
+ data.putExtra(Apg.EXTRA_KEY_ID, id);
+ data.putExtra(Apg.EXTRA_USER_ID, (String)mList.getItemAtPosition(position));
+ setResult(RESULT_OK, data);
+ finish();
+ }
+ });
+
+ mFilterLayout = findViewById(R.id.layout_filter);
+ mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
+ mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
+
+ mClearFilterButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ handleIntent(new Intent());
+ }
+ });
+
+ handleIntent(getIntent());
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ handleIntent(intent);
+ }
+
+ private void handleIntent(Intent intent) {
+ String searchString = null;
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ searchString = intent.getStringExtra(SearchManager.QUERY);
+ if (searchString != null && searchString.trim().length() == 0) {
+ searchString = null;
+ }
+ }
+
+ if (searchString == null) {
+ mFilterLayout.setVisibility(View.GONE);
+ } else {
+ mFilterLayout.setVisibility(View.VISIBLE);
+ mFilterInfo.setText(getString(R.string.filterInfo, searchString));
+ }
+
+ if (mListAdapter != null) {
+ mListAdapter.cleanup();
+ }
+
+ mListAdapter = new SelectSecretKeyListAdapter(this, mList, searchString);
+ mList.setAdapter(mListAdapter);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, Id.menu.option.search, 0, R.string.menu_search)
+ .setIcon(android.R.drawable.ic_menu_search);
+ return true;
+ }
+}
diff --git a/org_apg/src/org/apg/ui/SelectSecretKeyListAdapter.java b/org_apg/src/org/apg/ui/SelectSecretKeyListAdapter.java
new file mode 100644
index 000000000..1a7734245
--- /dev/null
+++ b/org_apg/src/org/apg/ui/SelectSecretKeyListAdapter.java
@@ -0,0 +1,176 @@
+package org.apg.ui;
+
+import java.util.Date;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.Id.database;
+import org.apg.provider.KeyRings;
+import org.apg.provider.Keys;
+import org.apg.provider.UserIds;
+import org.apg.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+public class SelectSecretKeyListAdapter extends BaseAdapter {
+ protected LayoutInflater mInflater;
+ protected ListView mParent;
+ protected SQLiteDatabase mDatabase;
+ protected Cursor mCursor;
+ protected String mSearchString;
+ protected Activity mActivity;
+
+ public SelectSecretKeyListAdapter(Activity activity, ListView parent, String searchString) {
+ mSearchString = searchString;
+
+ mActivity = activity;
+ mParent = parent;
+ mDatabase = Apg.getDatabase().db();
+ mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ long now = new Date().getTime() / 1000;
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
+ "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
+ Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
+ Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
+ ") " +
+ " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
+ "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
+ UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
+ UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
+
+ if (searchString != null && searchString.trim().length() > 0) {
+ String[] chunks = searchString.trim().split(" +");
+ qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
+ UserIds.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + UserIds.KEY_ID + " = " +
+ Keys.TABLE_NAME + "." + Keys._ID);
+ for (int i = 0; i < chunks.length; ++i) {
+ qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
+ qb.appendWhereEscapeString("%" + chunks[i] + "%");
+ }
+ qb.appendWhere(")");
+ }
+
+ mCursor = qb.query(mDatabase,
+ new String[] {
+ KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
+ KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
+ UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_SIGN + " = '1')", // 3,
+ "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
+ "tmp." + Keys.KEY_RING_ID + " = " +
+ KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
+ "tmp." + Keys.IS_REVOKED + " = '0' AND " +
+ "tmp." + Keys.CAN_SIGN + " = '1' AND " +
+ "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
+ "(tmp." + Keys.EXPIRY + " IS NULL OR " +
+ "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
+ },
+ KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
+ new String[] { "" + Id.database.type_secret },
+ null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
+
+ activity.startManagingCursor(mCursor);
+ }
+
+ public void cleanup() {
+ if (mCursor != null) {
+ mActivity.stopManagingCursor(mCursor);
+ mCursor.close();
+ }
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getInt(4) > 0; // valid CAN_SIGN
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mCursor.getCount();
+ }
+
+ public Object getItem(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getString(2); // USER_ID
+ }
+
+ public long getItemId(int position) {
+ mCursor.moveToPosition(position);
+ return mCursor.getLong(1); // MASTER_KEY_ID
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ mCursor.moveToPosition(position);
+
+ View view = mInflater.inflate(R.layout.select_secret_key_item, null);
+ boolean enabled = isEnabled(position);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknownUserId);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ keyId.setText(R.string.noKey);
+ TextView status = (TextView) view.findViewById(R.id.status);
+ status.setText(R.string.unknownStatus);
+
+ String userId = mCursor.getString(2); // USER_ID
+ if (userId != null) {
+ String chunks[] = userId.split(" <", 2);
+ userId = chunks[0];
+ if (chunks.length > 1) {
+ mainUserIdRest.setText("<" + chunks[1]);
+ }
+ mainUserId.setText(userId);
+ }
+
+ long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
+ keyId.setText(Apg.getSmallFingerPrint(masterKeyId));
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ }
+
+ if (enabled) {
+ status.setText(R.string.canSign);
+ } else {
+ if (mCursor.getInt(3) > 0) {
+ // has some CAN_SIGN keys, but col(4) = 0, so must be revoked or expired
+ status.setText(R.string.expired);
+ } else {
+ status.setText(R.string.noKey);
+ }
+ }
+
+ status.setText(status.getText() + " ");
+
+ view.setEnabled(enabled);
+ mainUserId.setEnabled(enabled);
+ mainUserIdRest.setEnabled(enabled);
+ keyId.setEnabled(enabled);
+ status.setEnabled(enabled);
+
+ return view;
+ }
+}
\ No newline at end of file
diff --git a/org_apg/src/org/apg/ui/SendKeyActivity.java b/org_apg/src/org/apg/ui/SendKeyActivity.java
new file mode 100644
index 000000000..c44e87469
--- /dev/null
+++ b/org_apg/src/org/apg/ui/SendKeyActivity.java
@@ -0,0 +1,100 @@
+package org.apg.ui;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.HkpKeyServer;
+import org.apg.Id;
+import org.apg.Constants.extras;
+import org.apg.Id.message;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.apg.R;
+
+import android.os.Bundle;
+import android.os.Message;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+/**
+ * gpg --send-key activity
+ *
+ * Sends the selected public key to a key server
+ */
+public class SendKeyActivity extends BaseActivity {
+
+ private Button export;
+ private Spinner keyServer;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.key_server_export_layout);
+
+ export = (Button) findViewById(R.id.btn_export_to_server);
+ keyServer = (Spinner) findViewById(R.id.keyServer);
+
+ ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, mPreferences.getKeyServers());
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ keyServer.setAdapter(adapter);
+ if (adapter.getCount() > 0) {
+ keyServer.setSelection(0);
+ } else {
+ export.setEnabled(false);
+ }
+
+ export.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startThread();
+ }
+ });
+ }
+
+ @Override
+ public void run() {
+ String error = null;
+ Bundle data = new Bundle();
+ Message msg = new Message();
+
+ HkpKeyServer server = new HkpKeyServer((String) keyServer.getSelectedItem());
+
+ int keyRingId = getIntent().getIntExtra(Apg.EXTRA_KEY_ID, -1);
+
+ PGPKeyRing keyring = Apg.getKeyRing(keyRingId);
+ if (keyring != null && keyring instanceof PGPPublicKeyRing) {
+ boolean uploaded = Apg.uploadKeyRingToServer(server, (PGPPublicKeyRing) keyring);
+ if (!uploaded) {
+ error = "Unable to export key to selected server";
+ }
+ }
+
+ data.putInt(Constants.extras.STATUS, Id.message.export_done);
+
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ }
+
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ Bundle data = msg.getData();
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ Toast.makeText(this, R.string.keySendSuccess, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+}
diff --git a/org_apg/src/org/apg/ui/SignKeyActivity.java b/org_apg/src/org/apg/ui/SignKeyActivity.java
new file mode 100644
index 000000000..ab145c921
--- /dev/null
+++ b/org_apg/src/org/apg/ui/SignKeyActivity.java
@@ -0,0 +1,294 @@
+package org.apg.ui;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SignatureException;
+import java.util.Iterator;
+
+import org.apg.Apg;
+import org.apg.Constants;
+import org.apg.HkpKeyServer;
+import org.apg.Id;
+import org.apg.Constants.extras;
+import org.apg.Id.dialog;
+import org.apg.Id.message;
+import org.apg.Id.request;
+import org.apg.Id.return_value;
+import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
+import org.spongycastle.openpgp.PGPUtil;
+import org.apg.R;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+/**
+ * gpg --sign-key
+ *
+ * signs the specified public key with the specified secret master key
+ */
+public class SignKeyActivity extends BaseActivity {
+ private static final String TAG = "SignKeyActivity";
+
+ private long pubKeyId = 0;
+ private long masterKeyId = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // check we havent already signed it
+ setContentView(R.layout.sign_key_layout);
+
+ final Spinner keyServer = (Spinner) findViewById(R.id.keyServer);
+ ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, mPreferences.getKeyServers());
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ keyServer.setAdapter(adapter);
+
+ final CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey);
+ if (!sendKey.isChecked()) {
+ keyServer.setEnabled(false);
+ } else {
+ keyServer.setEnabled(true);
+ }
+
+ sendKey.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (!isChecked) {
+ keyServer.setEnabled(false);
+ } else {
+ keyServer.setEnabled(true);
+ }
+ }
+ });
+
+ Button sign = (Button) findViewById(R.id.sign);
+ sign.setEnabled(false); // disabled until the user selects a key to sign with
+ sign.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (pubKeyId != 0) {
+ initiateSigning();
+ }
+ }
+ });
+
+ pubKeyId = getIntent().getLongExtra(Apg.EXTRA_KEY_ID, 0);
+ if (pubKeyId == 0) {
+ finish(); // nothing to do if we dont know what key to sign
+ } else {
+ // kick off the SecretKey selection activity so the user chooses which key to sign with first
+ Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
+ startActivityForResult(intent, Id.request.secret_keys);
+ }
+ }
+
+ /**
+ * handles the UI bits of the signing process on the UI thread
+ */
+ private void initiateSigning() {
+ PGPPublicKeyRing pubring = Apg.getPublicKeyRing(pubKeyId);
+ if (pubring != null) {
+ // if we have already signed this key, dont bother doing it again
+ boolean alreadySigned = false;
+
+ @SuppressWarnings("unchecked")
+ Iterator itr = pubring.getPublicKey(pubKeyId).getSignatures();
+ while (itr.hasNext()) {
+ PGPSignature sig = itr.next();
+ if (sig.getKeyID() == masterKeyId) {
+ alreadySigned = true;
+ break;
+ }
+ }
+
+ if (!alreadySigned) {
+ /*
+ * get the user's passphrase for this key (if required)
+ */
+ String passphrase = Apg.getCachedPassPhrase(masterKeyId);
+ if (passphrase == null) {
+ showDialog(Id.dialog.pass_phrase);
+ return; // bail out; need to wait until the user has entered the passphrase before trying again
+ } else {
+ startSigning();
+ }
+ } else {
+ final Bundle status = new Bundle();
+ Message msg = new Message();
+
+ status.putString(Apg.EXTRA_ERROR, "Key has already been signed");
+
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+
+ msg.setData(status);
+ sendMessage(msg);
+
+ setResult(Id.return_value.error);
+ finish();
+ }
+ }
+ }
+
+ @Override
+ public long getSecretKeyId() {
+ return masterKeyId;
+ }
+
+ @Override
+ public void passPhraseCallback(long keyId, String passPhrase) {
+ super.passPhraseCallback(keyId, passPhrase);
+ startSigning();
+ }
+
+ /**
+ * kicks off the actual signing process on a background thread
+ */
+ private void startSigning() {
+ showDialog(Id.dialog.signing);
+ startThread();
+ }
+
+ @Override
+ public void run() {
+ final Bundle status = new Bundle();
+ Message msg = new Message();
+
+ try {
+ String passphrase = Apg.getCachedPassPhrase(masterKeyId);
+ if (passphrase == null || passphrase.length() <= 0) {
+ status.putString(Apg.EXTRA_ERROR, "Unable to obtain passphrase");
+ } else {
+ PGPPublicKeyRing pubring = Apg.getPublicKeyRing(pubKeyId);
+
+ /*
+ * sign the incoming key
+ */
+ PGPSecretKey secretKey = Apg.getSecretKey(masterKeyId);
+ PGPPrivateKey signingKey = secretKey.extractPrivateKey(passphrase.toCharArray(), BouncyCastleProvider.PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256, BouncyCastleProvider.PROVIDER_NAME);
+ sGen.initSign(PGPSignature.DIRECT_KEY, signingKey);
+
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+
+ PGPSignatureSubpacketVector packetVector = spGen.generate();
+ sGen.setHashedSubpackets(packetVector);
+
+ PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), sGen.generate());
+ pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
+
+ // check if we need to send the key to the server or not
+ CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey);
+ if (sendKey.isChecked()) {
+ Spinner keyServer = (Spinner) findViewById(R.id.keyServer);
+ HkpKeyServer server = new HkpKeyServer((String) keyServer.getSelectedItem());
+
+ /*
+ * upload the newly signed key to the key server
+ */
+
+ Apg.uploadKeyRingToServer(server, pubring);
+ }
+
+ // store the signed key in our local cache
+ int retval = Apg.storeKeyRingInCache(pubring);
+ if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
+ status.putString(Apg.EXTRA_ERROR, "Failed to store signed key in local cache");
+ }
+ }
+ } catch (PGPException e) {
+ Log.e(TAG, "Failed to sign key", e);
+ status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+ return;
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(TAG, "Failed to sign key", e);
+ status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+ return;
+ } catch (NoSuchProviderException e) {
+ Log.e(TAG, "Failed to sign key", e);
+ status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+ return;
+ } catch (SignatureException e) {
+ Log.e(TAG, "Failed to sign key", e);
+ status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+ return;
+ }
+
+ status.putInt(Constants.extras.STATUS, Id.message.done);
+
+ msg.setData(status);
+ sendMessage(msg);
+
+ if (status.containsKey(Apg.EXTRA_ERROR)) {
+ setResult(Id.return_value.error);
+ } else {
+ setResult(Id.return_value.ok);
+ }
+
+ finish();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case Id.request.secret_keys: {
+ if (resultCode == RESULT_OK) {
+ masterKeyId = data.getLongExtra(Apg.EXTRA_KEY_ID, 0);
+
+ // re-enable the sign button so the user can initiate the sign process
+ Button sign = (Button) findViewById(R.id.sign);
+ sign.setEnabled(true);
+ }
+
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+ }
+
+ @Override
+ public void doneCallback(Message msg) {
+ super.doneCallback(msg);
+
+ removeDialog(Id.dialog.signing);
+
+ Bundle data = msg.getData();
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ Toast.makeText(this, R.string.keySignSuccess, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+}
diff --git a/org_apg/src/org/apg/ui/widget/Editor.java b/org_apg/src/org/apg/ui/widget/Editor.java
new file mode 100644
index 000000000..be95ad656
--- /dev/null
+++ b/org_apg/src/org/apg/ui/widget/Editor.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui.widget;
+
+public interface Editor {
+ public interface EditorListener {
+ public void onDeleted(Editor editor);
+ }
+
+ public void setEditorListener(EditorListener listener);
+}
diff --git a/org_apg/src/org/apg/ui/widget/IntegerListPreference.java b/org_apg/src/org/apg/ui/widget/IntegerListPreference.java
new file mode 100644
index 000000000..fa411a786
--- /dev/null
+++ b/org_apg/src/org/apg/ui/widget/IntegerListPreference.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * 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.apg.ui.widget;
+
+import android.content.Context;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+
+/**
+ * A list preference which persists its values as integers instead of strings.
+ * Code reading the values should use
+ * {@link android.content.SharedPreferences#getInt}.
+ * When using XML-declared arrays for entry values, the arrays should be regular
+ * string arrays containing valid integer values.
+ *
+ * @author Rodrigo Damazio
+ */
+public class IntegerListPreference extends ListPreference {
+
+ public IntegerListPreference(Context context) {
+ super(context);
+
+ verifyEntryValues(null);
+ }
+
+ public IntegerListPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ verifyEntryValues(null);
+ }
+
+ @Override
+ public void setEntryValues(CharSequence[] entryValues) {
+ CharSequence[] oldValues = getEntryValues();
+ super.setEntryValues(entryValues);
+ verifyEntryValues(oldValues);
+ }
+
+ @Override
+ public void setEntryValues(int entryValuesResId) {
+ CharSequence[] oldValues = getEntryValues();
+ super.setEntryValues(entryValuesResId);
+ verifyEntryValues(oldValues);
+ }
+
+ @Override
+ protected String getPersistedString(String defaultReturnValue) {
+ // During initial load, there's no known default value
+ int defaultIntegerValue = Integer.MIN_VALUE;
+ if (defaultReturnValue != null) {
+ defaultIntegerValue = Integer.parseInt(defaultReturnValue);
+ }
+
+ // When the list preference asks us to read a string, instead read an
+ // integer.
+ int value = getPersistedInt(defaultIntegerValue);
+ return Integer.toString(value);
+ }
+
+ @Override
+ protected boolean persistString(String value) {
+ // When asked to save a string, instead save an integer
+ return persistInt(Integer.parseInt(value));
+ }
+
+ private void verifyEntryValues(CharSequence[] oldValues) {
+ CharSequence[] entryValues = getEntryValues();
+ if (entryValues == null) {
+ return;
+ }
+
+ for (CharSequence entryValue : entryValues) {
+ try {
+ Integer.parseInt(entryValue.toString());
+ } catch (NumberFormatException nfe) {
+ super.setEntryValues(oldValues);
+ throw nfe;
+ }
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/ui/widget/KeyEditor.java b/org_apg/src/org/apg/ui/widget/KeyEditor.java
new file mode 100644
index 000000000..ef98f794a
--- /dev/null
+++ b/org_apg/src/org/apg/ui/widget/KeyEditor.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui.widget;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.util.Choice;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.apg.R;
+
+import android.app.DatePickerDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.DatePicker;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Vector;
+
+public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
+ private PGPSecretKey mKey;
+
+ private EditorListener mEditorListener = null;
+
+ private boolean mIsMasterKey;
+ ImageButton mDeleteButton;
+ TextView mAlgorithm;
+ TextView mKeyId;
+ Spinner mUsage;
+ TextView mCreationDate;
+ Button mExpiryDateButton;
+ GregorianCalendar mExpiryDate;
+
+ private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
+ new DatePickerDialog.OnDateSetListener() {
+ public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
+ GregorianCalendar date = new GregorianCalendar(year, monthOfYear, dayOfMonth);
+ setExpiryDate(date);
+ }
+ };
+
+ public KeyEditor(Context context) {
+ super(context);
+ }
+
+ public KeyEditor(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mAlgorithm = (TextView) findViewById(R.id.algorithm);
+ mKeyId = (TextView) findViewById(R.id.keyId);
+ mCreationDate = (TextView) findViewById(R.id.creation);
+ mExpiryDateButton = (Button) findViewById(R.id.expiry);
+ mUsage = (Spinner) findViewById(R.id.usage);
+ 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 adapter =
+ new ArrayAdapter(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.delete);
+ mDeleteButton.setOnClickListener(this);
+
+ setExpiryDate(null);
+
+ mExpiryDateButton.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ GregorianCalendar date = mExpiryDate;
+ if (date == null) {
+ date = new GregorianCalendar();
+ }
+
+ DatePickerDialog dialog =
+ new DatePickerDialog(getContext(), mExpiryDateSetListener,
+ date.get(Calendar.YEAR),
+ date.get(Calendar.MONTH),
+ date.get(Calendar.DAY_OF_MONTH));
+ dialog.setCancelable(true);
+ dialog.setButton(Dialog.BUTTON_NEGATIVE,
+ getContext().getString(R.string.btn_noDate),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ setExpiryDate(null);
+ }
+ });
+ dialog.show();
+ }
+ });
+
+ super.onFinishInflate();
+ }
+
+ public void setValue(PGPSecretKey key, boolean isMasterKey) {
+ mKey = key;
+
+ mIsMasterKey = isMasterKey;
+ if (mIsMasterKey) {
+ mDeleteButton.setVisibility(View.INVISIBLE);
+ }
+
+ mAlgorithm.setText(Apg.getAlgorithmInfo(key));
+ String keyId1Str = Apg.getSmallFingerPrint(key.getKeyID());
+ String keyId2Str = Apg.getSmallFingerPrint(key.getKeyID() >> 32);
+ mKeyId.setText(keyId1Str + " " + keyId2Str);
+
+ Vector choices = new Vector();
+ 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 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)));
+ }
+
+ ArrayAdapter adapter =
+ new ArrayAdapter(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)) {
+ selectId = Id.choice.usage.sign_and_encrypt;
+ } else {
+ selectId = Id.choice.usage.encrypt_only;
+ }
+ } else {
+ 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();
+ cal.setTime(Apg.getCreationDate(key));
+ mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime()));
+ cal = new GregorianCalendar();
+ Date date = Apg.getExpiryDate(key);
+ if (date == null) {
+ setExpiryDate(null);
+ } else {
+ cal.setTime(Apg.getExpiryDate(key));
+ setExpiryDate(cal);
+ }
+ }
+
+ public PGPSecretKey getValue() {
+ return mKey;
+ }
+
+ public void onClick(View v) {
+ final ViewGroup parent = (ViewGroup)getParent();
+ if (v == mDeleteButton) {
+ parent.removeView(this);
+ if (mEditorListener != null) {
+ mEditorListener.onDeleted(this);
+ }
+ }
+ }
+
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+
+ private void setExpiryDate(GregorianCalendar date) {
+ mExpiryDate = date;
+ if (date == null) {
+ mExpiryDateButton.setText(R.string.none);
+ } else {
+ mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime()));
+ }
+ }
+
+ public GregorianCalendar getExpiryDate() {
+ return mExpiryDate;
+ }
+
+ public int getUsage() {
+ return ((Choice) mUsage.getSelectedItem()).getId();
+ }
+}
diff --git a/org_apg/src/org/apg/ui/widget/KeyServerEditor.java b/org_apg/src/org/apg/ui/widget/KeyServerEditor.java
new file mode 100644
index 000000000..3d8634c76
--- /dev/null
+++ b/org_apg/src/org/apg/ui/widget/KeyServerEditor.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui.widget;
+
+import org.apg.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener {
+ private EditorListener mEditorListener = null;
+
+ ImageButton mDeleteButton;
+ TextView mServer;
+
+ public KeyServerEditor(Context context) {
+ super(context);
+ }
+
+ public KeyServerEditor(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mServer = (TextView) findViewById(R.id.server);
+
+ mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton.setOnClickListener(this);
+
+ super.onFinishInflate();
+ }
+
+ public void setValue(String value) {
+ mServer.setText(value);
+ }
+
+ public String getValue() {
+ return mServer.getText().toString().trim();
+ }
+
+ public void onClick(View v) {
+ final ViewGroup parent = (ViewGroup)getParent();
+ if (v == mDeleteButton) {
+ parent.removeView(this);
+ if (mEditorListener != null) {
+ mEditorListener.onDeleted(this);
+ }
+ }
+ }
+
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+}
diff --git a/org_apg/src/org/apg/ui/widget/SectionView.java b/org_apg/src/org/apg/ui/widget/SectionView.java
new file mode 100644
index 000000000..220699124
--- /dev/null
+++ b/org_apg/src/org/apg/ui/widget/SectionView.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui.widget;
+
+import org.apg.Apg;
+import org.apg.Id;
+import org.apg.ui.widget.Editor.EditorListener;
+import org.apg.util.Choice;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.apg.R;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.util.Vector;
+
+public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Runnable {
+ private LayoutInflater mInflater;
+ private View mAdd;
+ private ViewGroup mEditors;
+ private TextView mTitle;
+ private int mType = 0;
+
+ private Choice mNewKeyAlgorithmChoice;
+ private int mNewKeySize;
+
+ volatile private PGPSecretKey mNewKey;
+ private ProgressDialog mProgressDialog;
+ private Thread mRunningThread = null;
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ Bundle data = msg.getData();
+ if (data != null) {
+ boolean closeProgressDialog = data.getBoolean("closeProgressDialog");
+ if (closeProgressDialog) {
+ if (mProgressDialog != null) {
+ mProgressDialog.dismiss();
+ mProgressDialog = null;
+ }
+ }
+
+ String error = data.getString(Apg.EXTRA_ERROR);
+ if (error != null) {
+ Toast.makeText(getContext(),
+ getContext().getString(R.string.errorMessage, error),
+ Toast.LENGTH_SHORT).show();
+ }
+
+ boolean gotNewKey = data.getBoolean("gotNewKey");
+ if (gotNewKey) {
+ KeyEditor view =
+ (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
+ mEditors, false);
+ view.setEditorListener(SectionView.this);
+ boolean isMasterKey = (mEditors.getChildCount() == 0);
+ view.setValue(mNewKey, isMasterKey);
+ mEditors.addView(view);
+ SectionView.this.updateEditorsVisible();
+ }
+ }
+ }
+ };
+
+ public SectionView(Context context) {
+ super(context);
+ }
+
+ public SectionView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public ViewGroup getEditors() {
+ return mEditors;
+ }
+
+ public void setType(int type) {
+ mType = type;
+ switch (type) {
+ case Id.type.user_id: {
+ mTitle.setText(R.string.section_userIds);
+ break;
+ }
+
+ case Id.type.key: {
+ mTitle.setText(R.string.section_keys);
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void onFinishInflate() {
+ mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mAdd = findViewById(R.id.header);
+ mAdd.setOnClickListener(this);
+
+ mEditors = (ViewGroup) findViewById(R.id.editors);
+ mTitle = (TextView) findViewById(R.id.title);
+
+ updateEditorsVisible();
+ super.onFinishInflate();
+ }
+
+ /** {@inheritDoc} */
+ public void onDeleted(Editor editor) {
+ this.updateEditorsVisible();
+ }
+
+ protected void updateEditorsVisible() {
+ final boolean hasChildren = mEditors.getChildCount() > 0;
+ mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
+ }
+
+ /** {@inheritDoc} */
+ public void onClick(View v) {
+ switch (mType) {
+ case Id.type.user_id: {
+ UserIdEditor view =
+ (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
+ mEditors, false);
+ view.setEditorListener(this);
+ if (mEditors.getChildCount() == 0) {
+ view.setIsMainUserId(true);
+ }
+ mEditors.addView(view);
+ break;
+ }
+
+ 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(R.string.title_createKey);
+ dialog.setMessage(R.string.keyCreationElGamalInfo);
+
+ boolean wouldBeMasterKey = (mEditors.getChildCount() == 0);
+
+ final Spinner algorithm = (Spinner) view.findViewById(R.id.algorithm);
+ Vector choices = new Vector();
+ 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 adapter =
+ new ArrayAdapter(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.size(); ++i) {
+ if (choices.get(i).getId() == Id.choice.algorithm.rsa) {
+ algorithm.setSelection(i);
+ break;
+ }
+ }
+
+ final EditText keySize = (EditText) view.findViewById(R.id.size);
+
+ dialog.setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface di, int id) {
+ di.dismiss();
+ try {
+ mNewKeySize = Integer.parseInt("" + keySize.getText());
+ } catch (NumberFormatException e) {
+ mNewKeySize = 0;
+ }
+
+ mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem();
+ createKey();
+ }
+ });
+
+ dialog.setCancelable(true);
+ dialog.setNegativeButton(android.R.string.cancel,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface di, int id) {
+ di.dismiss();
+ }
+ });
+
+ dialog.create().show();
+ break;
+ }
+
+ default: {
+ break;
+ }
+ }
+ this.updateEditorsVisible();
+ }
+
+ public void setUserIds(Vector list) {
+ if (mType != Id.type.user_id) {
+ return;
+ }
+
+ mEditors.removeAllViews();
+ for (String userId : list) {
+ UserIdEditor view =
+ (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, mEditors, false);
+ view.setEditorListener(this);
+ view.setValue(userId);
+ if (mEditors.getChildCount() == 0) {
+ view.setIsMainUserId(true);
+ }
+ mEditors.addView(view);
+ }
+
+ this.updateEditorsVisible();
+ }
+
+ public void setKeys(Vector list) {
+ if (mType != Id.type.key) {
+ return;
+ }
+
+ mEditors.removeAllViews();
+ for (PGPSecretKey key : list) {
+ KeyEditor view =
+ (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, false);
+ view.setEditorListener(this);
+ boolean isMasterKey = (mEditors.getChildCount() == 0);
+ view.setValue(key, isMasterKey);
+ mEditors.addView(view);
+ }
+
+ this.updateEditorsVisible();
+ }
+
+ private void createKey() {
+ mProgressDialog = new ProgressDialog(getContext());
+ mProgressDialog.setMessage(getContext().getString(R.string.progress_generating));
+ mProgressDialog.setCancelable(false);
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialog.show();
+ mRunningThread = new Thread(this);
+ mRunningThread.start();
+ }
+
+ public void run() {
+ String error = null;
+ try {
+ 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;
+ } catch (NoSuchAlgorithmException e) {
+ error = "" + e;
+ } catch (PGPException e) {
+ error = "" + e;
+ } catch (InvalidParameterException e) {
+ error = "" + e;
+ } catch (InvalidAlgorithmParameterException e) {
+ error = "" + e;
+ } catch (Apg.GeneralException e) {
+ error = "" + e;
+ }
+
+ Message message = new Message();
+ Bundle data = new Bundle();
+ data.putBoolean("closeProgressDialog", true);
+ if (error != null) {
+ data.putString(Apg.EXTRA_ERROR, error);
+ } else {
+ data.putBoolean("gotNewKey", true);
+ }
+ message.setData(data);
+ mHandler.sendMessage(message);
+ }
+}
diff --git a/org_apg/src/org/apg/ui/widget/UserIdEditor.java b/org_apg/src/org/apg/ui/widget/UserIdEditor.java
new file mode 100644
index 000000000..b154803cf
--- /dev/null
+++ b/org_apg/src/org/apg/ui/widget/UserIdEditor.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.ui.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apg.R;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+
+public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
+ private EditorListener mEditorListener = null;
+
+ private ImageButton mDeleteButton;
+ private RadioButton mIsMainUserId;
+ private EditText mName;
+ private EditText mEmail;
+ private EditText mComment;
+
+ private static final Pattern EMAIL_PATTERN =
+ Pattern.compile("^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+[.]([a-zA-Z])+([a-zA-Z])+",
+ Pattern.CASE_INSENSITIVE);
+
+ public static class NoNameException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ public NoNameException(String message) {
+ super(message);
+ }
+ }
+
+ public static class NoEmailException extends Exception {
+ static final long serialVersionUID = 0xf812773344L;
+
+ public NoEmailException(String message) {
+ super(message);
+ }
+ }
+
+ public static class InvalidEmailException extends Exception {
+ static final long serialVersionUID = 0xf812773345L;
+
+ public InvalidEmailException(String message) {
+ super(message);
+ }
+ }
+
+ public UserIdEditor(Context context) {
+ super(context);
+ }
+
+ public UserIdEditor(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ setDrawingCacheEnabled(true);
+ setAlwaysDrawnWithCacheEnabled(true);
+
+ mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton.setOnClickListener(this);
+ mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
+ mIsMainUserId.setOnClickListener(this);
+
+ mName = (EditText) findViewById(R.id.name);
+ mEmail = (EditText) findViewById(R.id.email);
+ mComment = (EditText) findViewById(R.id.comment);
+
+ super.onFinishInflate();
+ }
+
+ public void setValue(String userId) {
+ mName.setText("");
+ mComment.setText("");
+ mEmail.setText("");
+
+ Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ mName.setText(matcher.group(1));
+ mComment.setText(matcher.group(2));
+ mEmail.setText(matcher.group(3));
+ return;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ mName.setText(matcher.group(1));
+ mEmail.setText(matcher.group(2));
+ return;
+ }
+ }
+
+ public String getValue() throws NoNameException, NoEmailException, InvalidEmailException {
+ String name = ("" + mName.getText()).trim();
+ String email = ("" + mEmail.getText()).trim();
+ String comment = ("" + mComment.getText()).trim();
+
+ if (email.length() > 0) {
+ Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
+ if (!emailMatcher.matches()) {
+ throw new InvalidEmailException(
+ getContext().getString(R.string.error_invalidEmail, email));
+ }
+ }
+
+ String userId = name;
+ if (comment.length() > 0) {
+ userId += " (" + comment + ")";
+ }
+ if (email.length() > 0) {
+ userId += " <" + email + ">";
+ }
+
+ if (userId.equals("")) {
+ // ok, empty one...
+ return userId;
+ }
+
+ // otherwise make sure that name and email exist
+ if (name.equals("")) {
+ throw new NoNameException("need a name");
+ }
+
+ if (email.equals("")) {
+ throw new NoEmailException("need an email");
+ }
+
+ return userId;
+ }
+
+ public void onClick(View v) {
+ final ViewGroup parent = (ViewGroup)getParent();
+ if (v == mDeleteButton) {
+ boolean wasMainUserId = mIsMainUserId.isChecked();
+ parent.removeView(this);
+ if (mEditorListener != null) {
+ mEditorListener.onDeleted(this);
+ }
+ if (wasMainUserId && parent.getChildCount() > 0) {
+ UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
+ editor.setIsMainUserId(true);
+ }
+ } else if (v == mIsMainUserId) {
+ for (int i = 0; i < parent.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) parent.getChildAt(i);
+ if (editor == this) {
+ editor.setIsMainUserId(true);
+ } else {
+ editor.setIsMainUserId(false);
+ }
+ }
+ }
+ }
+
+ public void setIsMainUserId(boolean value) {
+ mIsMainUserId.setChecked(value);
+ }
+
+ public boolean isMainUserId() {
+ return mIsMainUserId.isChecked();
+ }
+
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+}
diff --git a/org_apg/src/org/apg/util/ApgCon.java b/org_apg/src/org/apg/util/ApgCon.java
new file mode 100644
index 000000000..73ee66ad9
--- /dev/null
+++ b/org_apg/src/org/apg/util/ApgCon.java
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2011 Markus Doits
+ *
+ * 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.apg.util;
+
+import org.apg.util.ApgConInterface.OnCallFinishListener;
+import org.apg.IApgService;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * A APG-AIDL-Wrapper
+ *
+ *
+ * This class can be used by other projects to simplify connecting to the
+ * APG-AIDL-Service. Kind of wrapper of for AIDL.
+ *
+ *
+ *
+ * It is not used in this project.
+ *
+ *
+ * @author Markus Doits
+ * @version 1.1rc1
+ *
+ */
+public class ApgCon {
+ private static final boolean LOCAL_LOGV = true;
+ private static final boolean LOCAL_LOGD = true;
+
+ private final static String TAG = "ApgCon";
+ private final static int API_VERSION = 2; // aidl api-version it expects
+ private final static String BLOB_URI = "content://org.thialfihar.android.apg.provider.apgserviceblobprovider";
+
+ /**
+ * How many seconds to wait for a connection to AGP when connecting.
+ * Being unsuccessful for this number of seconds, a connection
+ * is assumed to be failed.
+ */
+ public int secondsToWaitForConnection = 15;
+
+ private class CallAsync extends AsyncTask {
+
+ @Override
+ protected Void doInBackground(String... arg) {
+ if( LOCAL_LOGD ) Log.d(TAG, "Async execution starting");
+ call(arg[0]);
+ return null;
+ }
+
+ protected void onPostExecute(Void res) {
+ if( LOCAL_LOGD ) Log.d(TAG, "Async execution finished");
+ mAsyncRunning = false;
+
+ }
+
+ }
+
+ private final Context mContext;
+ private final error mConnectionStatus;
+ private boolean mAsyncRunning = false;
+ private OnCallFinishListener mOnCallFinishListener;
+
+ private final Bundle mResult = new Bundle();
+ private final Bundle mArgs = new Bundle();
+ private final ArrayList mErrorList = new ArrayList();
+ private final ArrayList mWarningList = new ArrayList();
+
+ /** Remote service for decrypting and encrypting data */
+ private IApgService mApgService = null;
+
+ /** Set apgService accordingly to connection status */
+ private ServiceConnection mApgConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if( LOCAL_LOGD ) Log.d(TAG, "IApgService bound to apgService");
+ mApgService = IApgService.Stub.asInterface(service);
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if( LOCAL_LOGD ) Log.d(TAG, "IApgService disconnected");
+ mApgService = null;
+ }
+ };
+
+ /**
+ * Different types of local errors
+ */
+ public static enum error {
+ /**
+ * no error
+ */
+ NO_ERROR,
+ /**
+ * generic error
+ */
+ GENERIC,
+ /**
+ * connection to apg service not possible
+ */
+ CANNOT_BIND_TO_APG,
+ /**
+ * function to call not provided
+ */
+ CALL_MISSING,
+ /**
+ * apg service does not know what to do
+ */
+ CALL_NOT_KNOWN,
+ /**
+ * could not find APG being installed
+ */
+ APG_NOT_FOUND,
+ /**
+ * found APG but without AIDL interface
+ */
+ APG_AIDL_MISSING,
+ /**
+ * found APG but with wrong API
+ */
+ APG_API_MISSMATCH
+ }
+
+ private static enum ret {
+ ERROR, // returned from AIDL
+ RESULT, // returned from AIDL
+ WARNINGS, // mixed AIDL and LOCAL
+ ERRORS, // mixed AIDL and LOCAL
+ }
+
+ /**
+ * Constructor
+ *
+ *
+ * Creates a new ApgCon object and searches for the right APG version on
+ * initialization. If not found, errors are printed to the error log.
+ *
+ *
+ * @param ctx
+ * the running context
+ */
+ public ApgCon(Context ctx) {
+ if( LOCAL_LOGV ) Log.v(TAG, "EncryptionService created");
+ mContext = ctx;
+
+ error tmpError = null;
+ try {
+ if( LOCAL_LOGV ) Log.v(TAG, "Searching for the right APG version");
+ ServiceInfo apgServices[] = ctx.getPackageManager().getPackageInfo("org.thialfihar.android.apg",
+ PackageManager.GET_SERVICES | PackageManager.GET_META_DATA).services;
+ if (apgServices == null) {
+ Log.e(TAG, "Could not fetch services");
+ tmpError = error.GENERIC;
+ } else {
+ boolean apgServiceFound = false;
+ for (ServiceInfo inf : apgServices) {
+ if( LOCAL_LOGV ) Log.v(TAG, "Found service of APG: " + inf.name);
+ if (inf.name.equals("org.thialfihar.android.apg.ApgService")) {
+ apgServiceFound = true;
+ if (inf.metaData == null) {
+ Log.w(TAG, "Could not determine ApgService API");
+ Log.w(TAG, "This probably won't work!");
+ mWarningList.add("(LOCAL) Could not determine ApgService API");
+ tmpError = error.APG_API_MISSMATCH;
+ } else if (inf.metaData.getInt("api_version") != API_VERSION) {
+ Log.w(TAG, "Found ApgService API version " + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
+ Log.w(TAG, "This probably won't work!");
+ mWarningList.add("(LOCAL) Found ApgService API version " + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
+ tmpError = error.APG_API_MISSMATCH;
+ } else {
+ if( LOCAL_LOGV ) Log.v(TAG, "Found api_version " + API_VERSION + ", everything should work");
+ tmpError = error.NO_ERROR;
+ }
+ }
+ }
+
+ if (!apgServiceFound) {
+ Log.e(TAG, "Could not find APG with AIDL interface, this probably won't work");
+ mErrorList.add("(LOCAL) Could not find APG with AIDL interface, this probably won't work");
+ mResult.putInt(ret.ERROR.name(), error.APG_AIDL_MISSING.ordinal());
+ tmpError = error.APG_NOT_FOUND;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Could not find APG, is it installed?", e);
+ mErrorList.add("(LOCAL) Could not find APG, is it installed?");
+ mResult.putInt(ret.ERROR.name(), error.APG_NOT_FOUND.ordinal());
+ tmpError = error.APG_NOT_FOUND;
+ }
+
+ mConnectionStatus = tmpError;
+
+ }
+
+ /** try to connect to the apg service */
+ private boolean connect() {
+ if( LOCAL_LOGV ) Log.v(TAG, "trying to bind the apgService to context");
+
+ if (mApgService != null) {
+ if( LOCAL_LOGV ) Log.v(TAG, "allready connected");
+ return true;
+ }
+
+ try {
+ mContext.bindService(new Intent(IApgService.class.getName()), mApgConnection, Context.BIND_AUTO_CREATE);
+ } catch (Exception e) {
+ Log.e(TAG, "could not bind APG service", e);
+ return false;
+ }
+
+ int waitCount = 0;
+ while (mApgService == null && waitCount++ < secondsToWaitForConnection) {
+ if( LOCAL_LOGV ) Log.v(TAG, "sleeping 1 second to wait for apg");
+ android.os.SystemClock.sleep(1000);
+ }
+
+ if (waitCount >= secondsToWaitForConnection) {
+ if( LOCAL_LOGV ) Log.v(TAG, "slept waiting for nothing!");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Disconnects ApgCon from Apg
+ *
+ *
+ * This should be called whenever all work with APG is done (e.g. everything
+ * you wanted to encrypt is encrypted), since connections with AIDL should
+ * not be upheld indefinitely.
+ *
+ *
+ *
+ * Also, if you destroy you end using your ApgCon-instance, this must be
+ * called or else the connection to APG is leaked
+ *
+ */
+ public void disconnect() {
+ if( LOCAL_LOGV ) Log.v(TAG, "disconnecting apgService");
+ if (mApgService != null) {
+ mContext.unbindService(mApgConnection);
+ mApgService = null;
+ }
+ }
+
+ private boolean initialize() {
+ if (mApgService == null) {
+ if (!connect()) {
+ if( LOCAL_LOGV ) Log.v(TAG, "connection to apg service failed");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Calls a function from APG's AIDL-interface
+ *
+ *
+ * After you have set up everything with {@link #setArg(String, String)}
+ * (and variants), you can call a function of the AIDL-interface. This
+ * will:
+ *
+ *
start connection to the remote interface (if not already connected)
+ *
call the function passed with all parameters synchronously
+ *
set up everything to retrieve the result and/or warnings/errors
+ *
call the callback if provided
+ *
+ *
+ *
+ *
+ * Note your thread will be blocked during execution - if you want to call
+ * the function asynchronously, see {@link #callAsync(String)}.
+ *
+ *
+ * @param function
+ * a remote function to call
+ * @return true, if call successful (= no errors), else false
+ *
+ * @see #callAsync(String)
+ * @see #setArg(String, String)
+ * @see #setOnCallFinishListener(OnCallFinishListener)
+ */
+ public boolean call(String function) {
+ boolean success = this.call(function, mArgs, mResult);
+ if (mOnCallFinishListener != null) {
+ try {
+ if( LOCAL_LOGD ) Log.d(TAG, "About to execute callback");
+ mOnCallFinishListener.onCallFinish(mResult);
+ if( LOCAL_LOGD ) Log.d(TAG, "Callback executed");
+ } catch (Exception e) {
+ Log.w(TAG, "Exception on callback: (" + e.getClass() + ") " + e.getMessage(), e);
+ mWarningList.add("(LOCAL) Could not execute callback (" + e.getClass() + "): " + e.getMessage());
+ }
+ }
+ return success;
+ }
+
+ /**
+ * Calls a function of remote interface asynchronously
+ *
+ *
+ * This does exactly the same as {@link #call(String)}, but asynchronously.
+ * While connection to APG and work are done in background, your thread can
+ * go on executing.
+ *
+ *
+ *
+ * To see whether the task is finished, you have two possibilities:
+ *
+ *
In your thread, poll {@link #isRunning()}
+ *
Supply a callback with {@link #setOnCallFinishListener(OnCallFinishListener)}
+ *
+ *
+ *
+ * @param function
+ * a remote function to call
+ *
+ * @see #call(String)
+ * @see #isRunning()
+ * @see #setOnCallFinishListener(OnCallFinishListener)
+ */
+ public void callAsync(String function) {
+ mAsyncRunning = true;
+ new CallAsync().execute(function);
+ }
+
+ private boolean call(String function, Bundle pArgs, Bundle pReturn) {
+
+ if (!initialize()) {
+ mErrorList.add("(LOCAL) Cannot bind to ApgService");
+ mResult.putInt(ret.ERROR.name(), error.CANNOT_BIND_TO_APG.ordinal());
+ return false;
+ }
+
+ if (function == null || function.length() == 0) {
+ mErrorList.add("(LOCAL) Function to call missing");
+ mResult.putInt(ret.ERROR.name(), error.CALL_MISSING.ordinal());
+ return false;
+ }
+
+ try {
+ Boolean success = (Boolean) IApgService.class.getMethod(function, Bundle.class, Bundle.class).invoke(mApgService, pArgs, pReturn);
+ mErrorList.addAll(pReturn.getStringArrayList(ret.ERRORS.name()));
+ mWarningList.addAll(pReturn.getStringArrayList(ret.WARNINGS.name()));
+ return success;
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "Remote call not known (" + function + "): " + e.getMessage(), e);
+ mErrorList.add("(LOCAL) Remote call not known (" + function + "): " + e.getMessage());
+ mResult.putInt(ret.ERROR.name(), error.CALL_NOT_KNOWN.ordinal());
+ return false;
+ } catch (InvocationTargetException e) {
+ Throwable orig = e.getTargetException();
+ Log.w(TAG, "Exception of type '" + orig.getClass() + "' on AIDL call '" + function + "': " + orig.getMessage(), orig);
+ mErrorList.add("(LOCAL) Exception of type '" + orig.getClass() + "' on AIDL call '" + function + "': " + orig.getMessage());
+ return false;
+ } catch (Exception e) {
+ Log.e(TAG, "Generic error (" + e.getClass() + "): " + e.getMessage(), e);
+ mErrorList.add("(LOCAL) Generic error (" + e.getClass() + "): " + e.getMessage());
+ mResult.putInt(ret.ERROR.name(), error.GENERIC.ordinal());
+ return false;
+ }
+
+ }
+
+ /**
+ * Set a string argument for APG
+ *
+ *
+ * This defines a string argument for APG's AIDL-interface.
+ *
+ *
+ *
+ * To know what key-value-pairs are possible (or required), take a look into
+ * the IApgService.aidl
+ *
+ *
+ *
+ * Note that parameters are not reseted after a call, so you have to
+ * reset ({@link #clearArgs()}) them manually if you want to.
+ *
+ *
+ *
+ * @param key
+ * the key
+ * @param val
+ * the value
+ *
+ * @see #clearArgs()
+ */
+ public void setArg(String key, String val) {
+ mArgs.putString(key, val);
+ }
+
+ /**
+ * Set a string-array argument for APG
+ *
+ *
+ * If the AIDL-parameter is an {@literal ArrayList}, you have to use
+ * this function.
+ *
+ *
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, String vals[]) {
+ ArrayList list = new ArrayList();
+ for (String val : vals) {
+ list.add(val);
+ }
+ mArgs.putStringArrayList(key, list);
+ }
+
+ /**
+ * Set up a boolean argument for APG
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, boolean val) {
+ mArgs.putBoolean(key, val);
+ }
+
+ /**
+ * Set up a int argument for APG
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, int val) {
+ mArgs.putInt(key, val);
+ }
+
+ /**
+ * Set up a int-array argument for APG
+ *
+ * If the AIDL-parameter is an {@literal ArrayList}, you have to
+ * use this function.
+ *
+ *
+ * @param key
+ * the key
+ * @param vals
+ * the value
+ *
+ * @see #setArg(String, String)
+ */
+ public void setArg(String key, int vals[]) {
+ ArrayList list = new ArrayList();
+ for (int val : vals) {
+ list.add(val);
+ }
+ mArgs.putIntegerArrayList(key, list);
+ }
+
+ /**
+ * Set up binary data to en/decrypt
+ *
+ * @param is
+ * InputStream to get the data from
+ */
+ public void setBlob(InputStream is) {
+ if( LOCAL_LOGD ) Log.d(TAG, "setBlob() called");
+ // 1. get the new contentUri
+ ContentResolver cr = mContext.getContentResolver();
+ Uri contentUri = cr.insert(Uri.parse(BLOB_URI), new ContentValues());
+
+ // 2. insert binary data
+ OutputStream os = null;
+ try {
+ os = cr.openOutputStream(contentUri, "w");
+ } catch( Exception e ) {
+ Log.e(TAG, "... exception on setBlob", e);
+ }
+
+ byte[] buffer = new byte[8];
+ int len = 0;
+ try {
+ while( (len = is.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ if(LOCAL_LOGD) Log.d(TAG, "... write finished, now closing");
+ os.close();
+ } catch (Exception e) {
+ Log.e(TAG, "... error on writing buffer", e);
+ }
+
+ mArgs.putString("BLOB", contentUri.toString() );
+ }
+
+ /**
+ * Clears all arguments
+ *
+ *
+ * Anything the has been set up with the various
+ * {@link #setArg(String, String)} functions is cleared.
+ *
+ *
+ *
+ * Note that any warning, error, callback, result, etc. is NOT cleared with
+ * this.
+ *
+ *
+ * @see #reset()
+ */
+ public void clearArgs() {
+ mArgs.clear();
+ }
+
+ /**
+ * Return the object associated with the key
+ *
+ * @param key
+ * the object's key you want to return
+ * @return an object at position key, or null if not set
+ */
+ public Object getArg(String key) {
+ return mArgs.get(key);
+ }
+
+ /**
+ * Iterates through the errors
+ *
+ *
+ * With this method you can iterate through all errors. The errors are only
+ * returned once and deleted immediately afterwards, so you can only return
+ * each error once.
+ *
+ *
+ * @return a human readable description of a error that happened, or null if
+ * no more errors
+ *
+ * @see #hasNextError()
+ * @see #clearErrors()
+ */
+ public String getNextError() {
+ if (mErrorList.size() != 0)
+ return mErrorList.remove(0);
+ else
+ return null;
+ }
+
+ /**
+ * Check if there are any new errors
+ *
+ * @return true, if there are unreturned errors, false otherwise
+ *
+ * @see #getNextError()
+ */
+ public boolean hasNextError() {
+ return mErrorList.size() != 0;
+ }
+
+ /**
+ * Get the numeric representation of the last error
+ *
+ *
+ * Values <100 mean the error happened locally, values >=100 mean the error
+ * happened at the remote side (APG). See the IApgService.aidl (or get the
+ * human readable description with {@link #getNextError()}) for what
+ * errors >=100 mean.
+ *
+ *
+ * @return the id of the error that happened
+ */
+ public int getError() {
+ if (mResult.containsKey(ret.ERROR.name()))
+ return mResult.getInt(ret.ERROR.name());
+ else
+ return -1;
+ }
+
+ /**
+ * Iterates through the warnings
+ *
+ *
+ * With this method you can iterate through all warnings. Warnings are
+ * only returned once and deleted immediately afterwards, so you can only
+ * return each warning once.
+ *
+ *
+ * @return a human readable description of a warning that happened, or null
+ * if no more warnings
+ *
+ * @see #hasNextWarning()
+ * @see #clearWarnings()
+ */
+ public String getNextWarning() {
+ if (mWarningList.size() != 0)
+ return mWarningList.remove(0);
+ else
+ return null;
+ }
+
+ /**
+ * Check if there are any new warnings
+ *
+ * @return true, if there are unreturned warnings, false otherwise
+ *
+ * @see #getNextWarning()
+ */
+ public boolean hasNextWarning() {
+ return mWarningList.size() != 0;
+ }
+
+ /**
+ * Get the result
+ *
+ *
+ * This gets your result. After doing an encryption or decryption with APG,
+ * you get the output with this function.
+ *
+ *
+ *
+ * Note when your last remote call is unsuccessful, the result will
+ * still have the same value like the last successful call (or null, if no
+ * call was successful). To ensure you do not work with old call's results,
+ * either be sure to {@link #reset()} (or at least {@link #clearResult()})
+ * your instance before each new call or always check that
+ * {@link #hasNextError()} is false.
+ *
+ *
+ *
+ * Note: When handling binary data with {@link #setBlob(InputStream)}, you
+ * get your result with {@link #getBlobResult()}.
+ *
+ *
+ * @return the mResult of the last {@link #call(String)} or
+ * {@link #callAsync(String)}.
+ *
+ * @see #reset()
+ * @see #clearResult()
+ * @see #getResultBundle()
+ * @see #getBlobResult()
+ */
+ public String getResult() {
+ return mResult.getString(ret.RESULT.name());
+ }
+
+ /**
+ * Get the binary result
+ *
+ *
+ * This gets your binary result. It only works if you called {@link #setBlob(InputStream)} before.
+ *
+ * If you did not call encrypt nor decrypt, this will be the same data as you inputed.
+ *
+ *
+ * @return InputStream of the binary data which was en/decrypted
+ *
+ * @see #setBlob(InputStream)
+ * @see #getResult()
+ */
+ public InputStream getBlobResult() {
+ if(mArgs.containsKey("BLOB")) {
+ ContentResolver cr = mContext.getContentResolver();
+ InputStream in = null;
+ try {
+ in = cr.openInputStream(Uri.parse(mArgs.getString("BLOB")));
+ } catch( Exception e ) {
+ Log.e(TAG, "Could not return blob in result", e);
+ }
+ return in;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Get the result bundle
+ *
+ *
+ * Unlike {@link #getResult()}, which only returns any en-/decrypted
+ * message, this function returns the complete information that was returned
+ * by Apg. This also includes the "RESULT", but additionally the warnings,
+ * errors and any other information.
+ *
+ *
+ * For warnings and errors it is suggested to use the functions that are
+ * provided here, namely {@link #getError()}, {@link #getNextError()},
+ * {@link #get_next_Warning()} etc.), but if any call returns something non
+ * standard, you have access to the complete result bundle to extract the
+ * information.
+ *
+ *
+ * @return the complete result bundle of the last call to apg
+ */
+ public Bundle getResultBundle() {
+ return mResult;
+ }
+
+ public error getConnectionStatus() {
+ return mConnectionStatus;
+ }
+
+ /**
+ * Clears all unfetched errors
+ *
+ * @see #getNextError()
+ * @see #hasNextError()
+ */
+ public void clearErrors() {
+ mErrorList.clear();
+ mResult.remove(ret.ERROR.name());
+ }
+
+ /**
+ * Clears all unfetched warnings
+ *
+ * @see #getNextWarning()
+ * @see #hasNextWarning()
+ */
+ public void clearWarnings() {
+ mWarningList.clear();
+ }
+
+ /**
+ * Clears the last mResult
+ *
+ * @see #getResult()
+ */
+ public void clearResult() {
+ mResult.remove(ret.RESULT.name());
+ }
+
+ /**
+ * Set a callback listener when call to AIDL finishes
+ *
+ * @param obj
+ * a object to call back after async execution
+ * @see ApgConInterface
+ */
+ public void setOnCallFinishListener(OnCallFinishListener lis) {
+ mOnCallFinishListener = lis;
+ }
+
+ /**
+ * Clears any callback object
+ *
+ * @see #setOnCallFinishListener(OnCallFinishListener)
+ */
+ public void clearOnCallFinishListener() {
+ mOnCallFinishListener = null;
+ }
+
+ /**
+ * Checks if an async execution is running
+ *
+ *
+ * If you started something with {@link #callAsync(String)}, this will
+ * return true if the task is still running
+ *
+ *
+ * @return true, if an async task is still running, false otherwise
+ *
+ * @see #callAsync(String)
+ *
+ */
+ public boolean isRunning() {
+ return mAsyncRunning;
+ }
+
+ /**
+ * Completely resets your instance
+ *
+ *
+ * This currently resets everything in this instance. Errors, warnings,
+ * results, callbacks, ... are removed. Any connection to the remote
+ * interface is upheld, though.
+ *
+ *
+ *
+ * Note when an async execution ({@link #callAsync(String)}) is
+ * running, it's result, warnings etc. will still be evaluated (which might
+ * be not what you want). Also mind that any callback you set is also
+ * reseted, so when finishing the execution any before defined callback will
+ * NOT BE TRIGGERED.
+ *
+ */
+ public void reset() {
+ clearErrors();
+ clearWarnings();
+ clearArgs();
+ clearOnCallFinishListener();
+ mResult.clear();
+ }
+
+}
diff --git a/org_apg/src/org/apg/util/ApgConInterface.java b/org_apg/src/org/apg/util/ApgConInterface.java
new file mode 100644
index 000000000..2b66164f1
--- /dev/null
+++ b/org_apg/src/org/apg/util/ApgConInterface.java
@@ -0,0 +1,7 @@
+package org.apg.util;
+
+public interface ApgConInterface {
+ public static interface OnCallFinishListener {
+ public abstract void onCallFinish(android.os.Bundle result);
+ }
+}
diff --git a/org_apg/src/org/apg/util/Choice.java b/org_apg/src/org/apg/util/Choice.java
new file mode 100644
index 000000000..1dbd9b215
--- /dev/null
+++ b/org_apg/src/org/apg/util/Choice.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.util;
+
+public class Choice {
+ private String mName;
+ private int mId;
+
+ public Choice() {
+ mId = -1;
+ mName = "";
+ }
+
+ public Choice(int id, String name) {
+ mId = id;
+ mName = name;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ @Override
+ public String toString() {
+ return mName;
+ }
+}
diff --git a/org_apg/src/org/apg/util/Compatibility.java b/org_apg/src/org/apg/util/Compatibility.java
new file mode 100644
index 000000000..10b3c7f6b
--- /dev/null
+++ b/org_apg/src/org/apg/util/Compatibility.java
@@ -0,0 +1,79 @@
+package org.apg.util;
+
+import java.lang.reflect.Method;
+
+import android.content.Context;
+import android.util.Log;
+
+public class Compatibility {
+
+ private static final String clipboardLabel = "APG";
+
+ /**
+ * Wrapper around ClipboardManager based on Android version using Reflection API, from
+ * http://www.projectsexception.com/blog/?p=87
+ *
+ * @param context
+ * @param text
+ */
+ public static void copyToClipboard(Context context, String text) {
+ Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
+ try {
+ if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) {
+ Method method = clipboard.getClass().getMethod("setText", CharSequence.class);
+ method.invoke(clipboard, text);
+ } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) {
+ Class> clazz = Class.forName("android.content.ClipData");
+ Method method = clazz.getMethod("newPlainText", CharSequence.class,
+ CharSequence.class);
+ Object clip = method.invoke(null, clipboardLabel, text);
+ method = clipboard.getClass().getMethod("setPrimaryClip", clazz);
+ method.invoke(clipboard, clip);
+ }
+ } catch (Exception e) {
+ Log.e("ProjectsException", "There was and error copying the text to the clipboard: "
+ + e.getMessage());
+ }
+ }
+
+ /**
+ * Wrapper around ClipboardManager based on Android version using Reflection API
+ *
+ * @param context
+ * @param text
+ */
+ public static CharSequence getClipboardText(Context context) {
+ Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
+ try {
+ if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) {
+ // CharSequence text = clipboard.getText();
+ Method method = clipboard.getClass().getMethod("getText");
+ Object text = method.invoke(clipboard);
+
+ return (CharSequence) text;
+ } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) {
+ // ClipData clipData = clipboard.getPrimaryClip();
+ Method methodGetPrimaryClip = clipboard.getClass().getMethod("getPrimaryClip");
+ Object clipData = methodGetPrimaryClip.invoke(clipboard);
+
+ // ClipData.Item clipDataItem = clipData.getItemAt(0);
+ Method methodGetItemAt = clipData.getClass().getMethod("getItemAt", Integer.TYPE);
+ Object clipDataItem = methodGetItemAt.invoke(clipData, 0);
+
+ // CharSequence text = clipDataItem.coerceToText(context);
+ Method methodGetString = clipDataItem.getClass().getMethod("coerceToText",
+ Context.class);
+ Object text = methodGetString.invoke(clipDataItem, context);
+
+ return (CharSequence) text;
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ Log.e("ProjectsException", "There was and error getting the text from the clipboard: "
+ + e.getMessage());
+
+ return null;
+ }
+ }
+}
diff --git a/org_apg/src/org/apg/util/IterableIterator.java b/org_apg/src/org/apg/util/IterableIterator.java
new file mode 100644
index 000000000..be6632ac0
--- /dev/null
+++ b/org_apg/src/org/apg/util/IterableIterator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2010 Thialfihar
+ *
+ * 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.apg.util;
+
+import java.util.Iterator;
+
+public class IterableIterator implements Iterable {
+ private Iterator mIter;
+
+ public IterableIterator(Iterator iter) {
+ mIter = iter;
+ }
+
+ public Iterator iterator() {
+ return mIter;
+ }
+}
diff --git a/pom.xml b/pom.xml
deleted file mode 100644
index cadb2a38d..000000000
--- a/pom.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
- 4.0.0
- 1.0.9-SNAPSHOT
- org.thialfihar.android
- apg
- apk
- APG
-
-
- http://code.google.com/p/android-privacy-guard/
-
-
- UTF-8
- 1.6_r2
-
-
-
-
- com.google.android
- android
- provided
- 1.6_r2
-
-
- com.madgag
- scprov-jdk15
- 1.46.99.4-UNOFFICIAL-ROBERTO-RELEASE-SNAPSHOT
- jar
-
-
- com.google.zxing
- android-integration
- 1.6-SNAPSHOT
- jar
-
-
-
- src
- test
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- 2.3.2
-
- 1.6
- 1.6
-
-
-
- com.jayway.maven.plugins.android.generation2
- android-maven-plugin
- 3.0.0-alpha-11
-
-
- ${user.home}/android-sdk-linux_x86/
- 4
-
- true
- true
-
- true
-
-
-
-
diff --git a/project.properties b/project.properties
deleted file mode 100644
index 8da376af8..000000000
--- a/project.properties
+++ /dev/null
@@ -1,11 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-target=android-15
diff --git a/res/anim/push_left_in.xml b/res/anim/push_left_in.xml
deleted file mode 100644
index 45fb4875a..000000000
--- a/res/anim/push_left_in.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
diff --git a/res/anim/push_left_out.xml b/res/anim/push_left_out.xml
deleted file mode 100644
index 845679f16..000000000
--- a/res/anim/push_left_out.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/anim/push_right_in.xml b/res/anim/push_right_in.xml
deleted file mode 100644
index 09a244406..000000000
--- a/res/anim/push_right_in.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
diff --git a/res/anim/push_right_out.xml b/res/anim/push_right_out.xml
deleted file mode 100644
index e8893a69a..000000000
--- a/res/anim/push_right_out.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/drawable-finger/btn_circle.xml b/res/drawable-finger/btn_circle.xml
deleted file mode 100644
index 6c3c7fc1a..000000000
--- a/res/drawable-finger/btn_circle.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/drawable-hdpi-finger/btn_circle_disable.png b/res/drawable-hdpi-finger/btn_circle_disable.png
deleted file mode 100644
index ae063b545..000000000
Binary files a/res/drawable-hdpi-finger/btn_circle_disable.png and /dev/null differ
diff --git a/res/drawable-hdpi-finger/btn_circle_disable_focused.png b/res/drawable-hdpi-finger/btn_circle_disable_focused.png
deleted file mode 100644
index 7a5d4fe4e..000000000
Binary files a/res/drawable-hdpi-finger/btn_circle_disable_focused.png and /dev/null differ
diff --git a/res/drawable-hdpi-finger/btn_circle_normal.png b/res/drawable-hdpi-finger/btn_circle_normal.png
deleted file mode 100644
index 5eda66883..000000000
Binary files a/res/drawable-hdpi-finger/btn_circle_normal.png and /dev/null differ
diff --git a/res/drawable-hdpi-finger/btn_circle_pressed.png b/res/drawable-hdpi-finger/btn_circle_pressed.png
deleted file mode 100644
index 88848bac2..000000000
Binary files a/res/drawable-hdpi-finger/btn_circle_pressed.png and /dev/null differ
diff --git a/res/drawable-hdpi-finger/btn_circle_selected.png b/res/drawable-hdpi-finger/btn_circle_selected.png
deleted file mode 100644
index 74690705f..000000000
Binary files a/res/drawable-hdpi-finger/btn_circle_selected.png and /dev/null differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_minus.png b/res/drawable-hdpi-finger/ic_btn_round_minus.png
deleted file mode 100644
index 27af3faf4..000000000
Binary files a/res/drawable-hdpi-finger/ic_btn_round_minus.png and /dev/null differ
diff --git a/res/drawable-hdpi-finger/ic_btn_round_plus.png b/res/drawable-hdpi-finger/ic_btn_round_plus.png
deleted file mode 100644
index b24168c32..000000000
Binary files a/res/drawable-hdpi-finger/ic_btn_round_plus.png and /dev/null differ
diff --git a/res/drawable-hdpi/encrypted.png b/res/drawable-hdpi/encrypted.png
deleted file mode 100644
index 541781cd1..000000000
Binary files a/res/drawable-hdpi/encrypted.png and /dev/null differ
diff --git a/res/drawable-hdpi/encrypted_large.png b/res/drawable-hdpi/encrypted_large.png
deleted file mode 100644
index 209278377..000000000
Binary files a/res/drawable-hdpi/encrypted_large.png and /dev/null differ
diff --git a/res/drawable-hdpi/encrypted_small.png b/res/drawable-hdpi/encrypted_small.png
deleted file mode 100644
index 3ff8e9b97..000000000
Binary files a/res/drawable-hdpi/encrypted_small.png and /dev/null differ
diff --git a/res/drawable-hdpi/ic_next.png b/res/drawable-hdpi/ic_next.png
deleted file mode 100644
index d71058055..000000000
Binary files a/res/drawable-hdpi/ic_next.png and /dev/null differ
diff --git a/res/drawable-hdpi/ic_previous.png b/res/drawable-hdpi/ic_previous.png
deleted file mode 100644
index d610e4667..000000000
Binary files a/res/drawable-hdpi/ic_previous.png and /dev/null differ
diff --git a/res/drawable-hdpi/icon.png b/res/drawable-hdpi/icon.png
deleted file mode 100644
index d36080251..000000000
Binary files a/res/drawable-hdpi/icon.png and /dev/null differ
diff --git a/res/drawable-hdpi/key.png b/res/drawable-hdpi/key.png
deleted file mode 100644
index af4742ec0..000000000
Binary files a/res/drawable-hdpi/key.png and /dev/null differ
diff --git a/res/drawable-hdpi/key_large.png b/res/drawable-hdpi/key_large.png
deleted file mode 100644
index 590f7d5a4..000000000
Binary files a/res/drawable-hdpi/key_large.png and /dev/null differ
diff --git a/res/drawable-hdpi/key_small.png b/res/drawable-hdpi/key_small.png
deleted file mode 100644
index 6966048a1..000000000
Binary files a/res/drawable-hdpi/key_small.png and /dev/null differ
diff --git a/res/drawable-hdpi/overlay_error.png b/res/drawable-hdpi/overlay_error.png
deleted file mode 100644
index e6d7e60ba..000000000
Binary files a/res/drawable-hdpi/overlay_error.png and /dev/null differ
diff --git a/res/drawable-hdpi/overlay_ok.png b/res/drawable-hdpi/overlay_ok.png
deleted file mode 100644
index 0672f869d..000000000
Binary files a/res/drawable-hdpi/overlay_ok.png and /dev/null differ
diff --git a/res/drawable-hdpi/signed.png b/res/drawable-hdpi/signed.png
deleted file mode 100644
index ab9495e7b..000000000
Binary files a/res/drawable-hdpi/signed.png and /dev/null differ
diff --git a/res/drawable-hdpi/signed_large.png b/res/drawable-hdpi/signed_large.png
deleted file mode 100644
index c209f4167..000000000
Binary files a/res/drawable-hdpi/signed_large.png and /dev/null differ
diff --git a/res/drawable-hdpi/signed_small.png b/res/drawable-hdpi/signed_small.png
deleted file mode 100644
index 54c4906e8..000000000
Binary files a/res/drawable-hdpi/signed_small.png and /dev/null differ
diff --git a/res/drawable-ldpi/encrypted.png b/res/drawable-ldpi/encrypted.png
deleted file mode 100644
index bcd8cfc8e..000000000
Binary files a/res/drawable-ldpi/encrypted.png and /dev/null differ
diff --git a/res/drawable-ldpi/encrypted_large.png b/res/drawable-ldpi/encrypted_large.png
deleted file mode 100644
index 34c3d3f97..000000000
Binary files a/res/drawable-ldpi/encrypted_large.png and /dev/null differ
diff --git a/res/drawable-ldpi/encrypted_small.png b/res/drawable-ldpi/encrypted_small.png
deleted file mode 100644
index 5e7294a4b..000000000
Binary files a/res/drawable-ldpi/encrypted_small.png and /dev/null differ
diff --git a/res/drawable-ldpi/ic_next.png b/res/drawable-ldpi/ic_next.png
deleted file mode 100644
index 474ed8faa..000000000
Binary files a/res/drawable-ldpi/ic_next.png and /dev/null differ
diff --git a/res/drawable-ldpi/ic_previous.png b/res/drawable-ldpi/ic_previous.png
deleted file mode 100644
index 6fd885e6b..000000000
Binary files a/res/drawable-ldpi/ic_previous.png and /dev/null differ
diff --git a/res/drawable-ldpi/icon.png b/res/drawable-ldpi/icon.png
deleted file mode 100644
index 5b9c33f0c..000000000
Binary files a/res/drawable-ldpi/icon.png and /dev/null differ
diff --git a/res/drawable-ldpi/key.png b/res/drawable-ldpi/key.png
deleted file mode 100644
index c806b6041..000000000
Binary files a/res/drawable-ldpi/key.png and /dev/null differ
diff --git a/res/drawable-ldpi/key_large.png b/res/drawable-ldpi/key_large.png
deleted file mode 100644
index aa499a5e1..000000000
Binary files a/res/drawable-ldpi/key_large.png and /dev/null differ
diff --git a/res/drawable-ldpi/key_small.png b/res/drawable-ldpi/key_small.png
deleted file mode 100644
index 073b95029..000000000
Binary files a/res/drawable-ldpi/key_small.png and /dev/null differ
diff --git a/res/drawable-ldpi/overlay_error.png b/res/drawable-ldpi/overlay_error.png
deleted file mode 100644
index e5a88e18f..000000000
Binary files a/res/drawable-ldpi/overlay_error.png and /dev/null differ
diff --git a/res/drawable-ldpi/overlay_ok.png b/res/drawable-ldpi/overlay_ok.png
deleted file mode 100644
index 63374d47f..000000000
Binary files a/res/drawable-ldpi/overlay_ok.png and /dev/null differ
diff --git a/res/drawable-ldpi/signed.png b/res/drawable-ldpi/signed.png
deleted file mode 100644
index 4202c3f97..000000000
Binary files a/res/drawable-ldpi/signed.png and /dev/null differ
diff --git a/res/drawable-ldpi/signed_large.png b/res/drawable-ldpi/signed_large.png
deleted file mode 100644
index d2917644c..000000000
Binary files a/res/drawable-ldpi/signed_large.png and /dev/null differ
diff --git a/res/drawable-ldpi/signed_small.png b/res/drawable-ldpi/signed_small.png
deleted file mode 100644
index 19d45f8da..000000000
Binary files a/res/drawable-ldpi/signed_small.png and /dev/null differ
diff --git a/res/drawable-mdpi-finger/btn_circle_disable.png b/res/drawable-mdpi-finger/btn_circle_disable.png
deleted file mode 100644
index 33b74a66c..000000000
Binary files a/res/drawable-mdpi-finger/btn_circle_disable.png and /dev/null differ
diff --git a/res/drawable-mdpi-finger/btn_circle_disable_focused.png b/res/drawable-mdpi-finger/btn_circle_disable_focused.png
deleted file mode 100644
index 005ad8dca..000000000
Binary files a/res/drawable-mdpi-finger/btn_circle_disable_focused.png and /dev/null differ
diff --git a/res/drawable-mdpi-finger/btn_circle_normal.png b/res/drawable-mdpi-finger/btn_circle_normal.png
deleted file mode 100644
index fc5af1c9f..000000000
Binary files a/res/drawable-mdpi-finger/btn_circle_normal.png and /dev/null differ
diff --git a/res/drawable-mdpi-finger/btn_circle_pressed.png b/res/drawable-mdpi-finger/btn_circle_pressed.png
deleted file mode 100644
index 8f40afdfc..000000000
Binary files a/res/drawable-mdpi-finger/btn_circle_pressed.png and /dev/null differ
diff --git a/res/drawable-mdpi-finger/btn_circle_selected.png b/res/drawable-mdpi-finger/btn_circle_selected.png
deleted file mode 100644
index c74fac227..000000000
Binary files a/res/drawable-mdpi-finger/btn_circle_selected.png and /dev/null differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_minus.png b/res/drawable-mdpi-finger/ic_btn_round_minus.png
deleted file mode 100644
index 96dbb17d2..000000000
Binary files a/res/drawable-mdpi-finger/ic_btn_round_minus.png and /dev/null differ
diff --git a/res/drawable-mdpi-finger/ic_btn_round_plus.png b/res/drawable-mdpi-finger/ic_btn_round_plus.png
deleted file mode 100644
index 1ec8a956a..000000000
Binary files a/res/drawable-mdpi-finger/ic_btn_round_plus.png and /dev/null differ
diff --git a/res/drawable-mdpi/encrypted.png b/res/drawable-mdpi/encrypted.png
deleted file mode 100644
index 34c3d3f97..000000000
Binary files a/res/drawable-mdpi/encrypted.png and /dev/null differ
diff --git a/res/drawable-mdpi/encrypted_large.png b/res/drawable-mdpi/encrypted_large.png
deleted file mode 100644
index 541781cd1..000000000
Binary files a/res/drawable-mdpi/encrypted_large.png and /dev/null differ
diff --git a/res/drawable-mdpi/encrypted_small.png b/res/drawable-mdpi/encrypted_small.png
deleted file mode 100644
index bcd8cfc8e..000000000
Binary files a/res/drawable-mdpi/encrypted_small.png and /dev/null differ
diff --git a/res/drawable-mdpi/ic_next.png b/res/drawable-mdpi/ic_next.png
deleted file mode 100644
index 8271c1380..000000000
Binary files a/res/drawable-mdpi/ic_next.png and /dev/null differ
diff --git a/res/drawable-mdpi/ic_previous.png b/res/drawable-mdpi/ic_previous.png
deleted file mode 100644
index ef90db972..000000000
Binary files a/res/drawable-mdpi/ic_previous.png and /dev/null differ
diff --git a/res/drawable-mdpi/icon.png b/res/drawable-mdpi/icon.png
deleted file mode 100644
index fec2d62ce..000000000
Binary files a/res/drawable-mdpi/icon.png and /dev/null differ
diff --git a/res/drawable-mdpi/key.png b/res/drawable-mdpi/key.png
deleted file mode 100644
index aa499a5e1..000000000
Binary files a/res/drawable-mdpi/key.png and /dev/null differ
diff --git a/res/drawable-mdpi/key_large.png b/res/drawable-mdpi/key_large.png
deleted file mode 100644
index af4742ec0..000000000
Binary files a/res/drawable-mdpi/key_large.png and /dev/null differ
diff --git a/res/drawable-mdpi/key_small.png b/res/drawable-mdpi/key_small.png
deleted file mode 100644
index c806b6041..000000000
Binary files a/res/drawable-mdpi/key_small.png and /dev/null differ
diff --git a/res/drawable-mdpi/overlay_error.png b/res/drawable-mdpi/overlay_error.png
deleted file mode 100644
index 5fe017433..000000000
Binary files a/res/drawable-mdpi/overlay_error.png and /dev/null differ
diff --git a/res/drawable-mdpi/overlay_ok.png b/res/drawable-mdpi/overlay_ok.png
deleted file mode 100644
index b4f332260..000000000
Binary files a/res/drawable-mdpi/overlay_ok.png and /dev/null differ
diff --git a/res/drawable-mdpi/signed.png b/res/drawable-mdpi/signed.png
deleted file mode 100644
index d2917644c..000000000
Binary files a/res/drawable-mdpi/signed.png and /dev/null differ
diff --git a/res/drawable-mdpi/signed_large.png b/res/drawable-mdpi/signed_large.png
deleted file mode 100644
index ab9495e7b..000000000
Binary files a/res/drawable-mdpi/signed_large.png and /dev/null differ
diff --git a/res/drawable-mdpi/signed_small.png b/res/drawable-mdpi/signed_small.png
deleted file mode 100644
index 4202c3f97..000000000
Binary files a/res/drawable-mdpi/signed_small.png and /dev/null differ
diff --git a/res/drawable-xhdpi/icon.png b/res/drawable-xhdpi/icon.png
deleted file mode 100644
index ffeb8cdf3..000000000
Binary files a/res/drawable-xhdpi/icon.png and /dev/null differ
diff --git a/res/drawable/btn_circle_disable.png b/res/drawable/btn_circle_disable.png
deleted file mode 100644
index 33b74a66c..000000000
Binary files a/res/drawable/btn_circle_disable.png and /dev/null differ
diff --git a/res/drawable/btn_circle_disable_focused.png b/res/drawable/btn_circle_disable_focused.png
deleted file mode 100644
index 005ad8dca..000000000
Binary files a/res/drawable/btn_circle_disable_focused.png and /dev/null differ
diff --git a/res/drawable/btn_circle_normal.png b/res/drawable/btn_circle_normal.png
deleted file mode 100644
index fc5af1c9f..000000000
Binary files a/res/drawable/btn_circle_normal.png and /dev/null differ
diff --git a/res/drawable/btn_circle_pressed.png b/res/drawable/btn_circle_pressed.png
deleted file mode 100644
index 8f40afdfc..000000000
Binary files a/res/drawable/btn_circle_pressed.png and /dev/null differ
diff --git a/res/drawable/btn_circle_selected.png b/res/drawable/btn_circle_selected.png
deleted file mode 100644
index c74fac227..000000000
Binary files a/res/drawable/btn_circle_selected.png and /dev/null differ
diff --git a/res/drawable/encrypted.png b/res/drawable/encrypted.png
deleted file mode 100644
index 2783804bc..000000000
Binary files a/res/drawable/encrypted.png and /dev/null differ
diff --git a/res/drawable/encrypted_large.png b/res/drawable/encrypted_large.png
deleted file mode 100644
index 6d7c616a4..000000000
Binary files a/res/drawable/encrypted_large.png and /dev/null differ
diff --git a/res/drawable/encrypted_small.png b/res/drawable/encrypted_small.png
deleted file mode 100644
index 7f4ab803f..000000000
Binary files a/res/drawable/encrypted_small.png and /dev/null differ
diff --git a/res/drawable/ic_btn_round_minus.png b/res/drawable/ic_btn_round_minus.png
deleted file mode 100644
index 96dbb17d2..000000000
Binary files a/res/drawable/ic_btn_round_minus.png and /dev/null differ
diff --git a/res/drawable/ic_btn_round_plus.png b/res/drawable/ic_btn_round_plus.png
deleted file mode 100644
index 1ec8a956a..000000000
Binary files a/res/drawable/ic_btn_round_plus.png and /dev/null differ
diff --git a/res/drawable/ic_launcher_folder.png b/res/drawable/ic_launcher_folder.png
deleted file mode 100644
index ed31ba580..000000000
Binary files a/res/drawable/ic_launcher_folder.png and /dev/null differ
diff --git a/res/drawable/ic_launcher_folder_small.png b/res/drawable/ic_launcher_folder_small.png
deleted file mode 100644
index 5df8d60f0..000000000
Binary files a/res/drawable/ic_launcher_folder_small.png and /dev/null differ
diff --git a/res/drawable/ic_next.png b/res/drawable/ic_next.png
deleted file mode 100644
index 8271c1380..000000000
Binary files a/res/drawable/ic_next.png and /dev/null differ
diff --git a/res/drawable/ic_previous.png b/res/drawable/ic_previous.png
deleted file mode 100644
index ef90db972..000000000
Binary files a/res/drawable/ic_previous.png and /dev/null differ
diff --git a/res/drawable/key.png b/res/drawable/key.png
deleted file mode 100644
index de7e72524..000000000
Binary files a/res/drawable/key.png and /dev/null differ
diff --git a/res/drawable/key_large.png b/res/drawable/key_large.png
deleted file mode 100644
index 6f18c0240..000000000
Binary files a/res/drawable/key_large.png and /dev/null differ
diff --git a/res/drawable/key_small.png b/res/drawable/key_small.png
deleted file mode 100644
index 121803508..000000000
Binary files a/res/drawable/key_small.png and /dev/null differ
diff --git a/res/drawable/overlay_error.png b/res/drawable/overlay_error.png
deleted file mode 100644
index 2372de59e..000000000
Binary files a/res/drawable/overlay_error.png and /dev/null differ
diff --git a/res/drawable/overlay_ok.png b/res/drawable/overlay_ok.png
deleted file mode 100644
index 2f0005898..000000000
Binary files a/res/drawable/overlay_ok.png and /dev/null differ
diff --git a/res/drawable/signed.png b/res/drawable/signed.png
deleted file mode 100644
index 490e94fbd..000000000
Binary files a/res/drawable/signed.png and /dev/null differ
diff --git a/res/drawable/signed_large.png b/res/drawable/signed_large.png
deleted file mode 100644
index 92e64dc51..000000000
Binary files a/res/drawable/signed_large.png and /dev/null differ
diff --git a/res/drawable/signed_small.png b/res/drawable/signed_small.png
deleted file mode 100644
index 590220281..000000000
Binary files a/res/drawable/signed_small.png and /dev/null differ
diff --git a/res/layout/about_activity.xml b/res/layout/about_activity.xml
deleted file mode 100644
index a85439ee1..000000000
--- a/res/layout/about_activity.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/account_item.xml b/res/layout/account_item.xml
deleted file mode 100644
index 0aa76719a..000000000
--- a/res/layout/account_item.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/res/layout/add_account_dialog.xml b/res/layout/add_account_dialog.xml
deleted file mode 100644
index d44ce9766..000000000
--- a/res/layout/add_account_dialog.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/res/layout/create_key.xml b/res/layout/create_key.xml
deleted file mode 100644
index d8ccc73ec..000000000
--- a/res/layout/create_key.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/decrypt.xml b/res/layout/decrypt.xml
deleted file mode 100644
index 8bfa76f01..000000000
--- a/res/layout/decrypt.xml
+++ /dev/null
@@ -1,216 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/edit_key.xml b/res/layout/edit_key.xml
deleted file mode 100644
index 88be75d86..000000000
--- a/res/layout/edit_key.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/edit_key_key_item.xml b/res/layout/edit_key_key_item.xml
deleted file mode 100644
index 850879f64..000000000
--- a/res/layout/edit_key_key_item.xml
+++ /dev/null
@@ -1,143 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/edit_key_section.xml b/res/layout/edit_key_section.xml
deleted file mode 100644
index d14748d4b..000000000
--- a/res/layout/edit_key_section.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/edit_key_user_id_item.xml b/res/layout/edit_key_user_id_item.xml
deleted file mode 100644
index 6e7b552dd..000000000
--- a/res/layout/edit_key_user_id_item.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/encrypt.xml b/res/layout/encrypt.xml
deleted file mode 100644
index 68808c8bf..000000000
--- a/res/layout/encrypt.xml
+++ /dev/null
@@ -1,382 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/file_dialog.xml b/res/layout/file_dialog.xml
deleted file mode 100644
index bc5a2a214..000000000
--- a/res/layout/file_dialog.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/filter_info.xml b/res/layout/filter_info.xml
deleted file mode 100644
index 9fef6548e..000000000
--- a/res/layout/filter_info.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/general.xml b/res/layout/general.xml
deleted file mode 100644
index c6702efac..000000000
--- a/res/layout/general.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/info.xml b/res/layout/info.xml
deleted file mode 100644
index 2507029c4..000000000
--- a/res/layout/info.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
diff --git a/res/layout/key_list.xml b/res/layout/key_list.xml
deleted file mode 100644
index ac07801ab..000000000
--- a/res/layout/key_list.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/layout/key_list_child_item_master_key.xml b/res/layout/key_list_child_item_master_key.xml
deleted file mode 100644
index 998ba256f..000000000
--- a/res/layout/key_list_child_item_master_key.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_list_child_item_sub_key.xml b/res/layout/key_list_child_item_sub_key.xml
deleted file mode 100644
index ac7c217a6..000000000
--- a/res/layout/key_list_child_item_sub_key.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_list_child_item_user_id.xml b/res/layout/key_list_child_item_user_id.xml
deleted file mode 100644
index 598dcef22..000000000
--- a/res/layout/key_list_child_item_user_id.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_list_group_item.xml b/res/layout/key_list_group_item.xml
deleted file mode 100644
index 35d0ab367..000000000
--- a/res/layout/key_list_group_item.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_server_editor.xml b/res/layout/key_server_editor.xml
deleted file mode 100644
index a02540c2c..000000000
--- a/res/layout/key_server_editor.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_server_export_layout.xml b/res/layout/key_server_export_layout.xml
deleted file mode 100644
index b2270417d..000000000
--- a/res/layout/key_server_export_layout.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_server_preference.xml b/res/layout/key_server_preference.xml
deleted file mode 100644
index 2f5645f62..000000000
--- a/res/layout/key_server_preference.xml
+++ /dev/null
@@ -1,115 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_server_query_layout.xml b/res/layout/key_server_query_layout.xml
deleted file mode 100644
index 6af4f3644..000000000
--- a/res/layout/key_server_query_layout.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_server_query_result_item.xml b/res/layout/key_server_query_result_item.xml
deleted file mode 100644
index 29c8b88f4..000000000
--- a/res/layout/key_server_query_result_item.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/key_server_query_result_user_id.xml b/res/layout/key_server_query_result_user_id.xml
deleted file mode 100644
index 9d3a4a1ab..000000000
--- a/res/layout/key_server_query_result_user_id.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
diff --git a/res/layout/mailbox_message_item.xml b/res/layout/mailbox_message_item.xml
deleted file mode 100644
index 05a267fb6..000000000
--- a/res/layout/mailbox_message_item.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/main.xml b/res/layout/main.xml
deleted file mode 100644
index 803b2abe9..000000000
--- a/res/layout/main.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/pass_phrase.xml b/res/layout/pass_phrase.xml
deleted file mode 100644
index d66ffca07..000000000
--- a/res/layout/pass_phrase.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/select_public_key.xml b/res/layout/select_public_key.xml
deleted file mode 100644
index 9ce35a7a8..000000000
--- a/res/layout/select_public_key.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/select_public_key_item.xml b/res/layout/select_public_key_item.xml
deleted file mode 100644
index beca23176..000000000
--- a/res/layout/select_public_key_item.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/select_secret_key.xml b/res/layout/select_secret_key.xml
deleted file mode 100644
index feabc6160..000000000
--- a/res/layout/select_secret_key.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/select_secret_key_item.xml b/res/layout/select_secret_key_item.xml
deleted file mode 100644
index 022545152..000000000
--- a/res/layout/select_secret_key_item.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/sign_key_layout.xml b/res/layout/sign_key_layout.xml
deleted file mode 100644
index 17be03b21..000000000
--- a/res/layout/sign_key_layout.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
deleted file mode 100644
index 418372fb9..000000000
--- a/res/values-da/strings.xml
+++ /dev/null
@@ -1,261 +0,0 @@
-
-
-
-
-
-
- Mail Inbox
- Administrér Offentlige Nøgler
- Administrér Private Nøgler
- Vælg Modtagere
- Vælg Signatur
- Kryptér
- Afkryptér
- Kodeord
- Opret Nøgle
- Redigér Nøgle
- Indstillinger
- Skift Kodeord
- Sæt Kodeord
- "Send Mail..."
- Kryptér Til Fil
- Afkryptér Til Fil
- Tilføj Konto
- Importér Nøgle
- Eksportér Nøgle
- Eksportér Nøgler
- Ingen Nøgle Matchede
-
-
- Bruger ID
- Nøgler
- Generelt
- Standard Indstillinger
-
-
- Kryptér Til Clipboard
- Kryptér Og Mail
- Kryptér
- Afkryptér
- Bekræft
- Vælg Modtagere
- Svar
- Kryptér Besked
- Afkryptér Besked
- Kryptér Fil
- Afkryptér Fil
- Gem
- Fortryd
- Slet
- Ingen
- Ryd Filter
- Skift Kodeord
- Sæt Kodeord
-
-
- Om APG
- Tilføj GMail Konto
- Slet Konto
- Administrér Offentlige Nøgler
- Administrér Private Nøgler
- Indstillinger
- Importér Nøgler
- Eksportér Nøgler
- Eksportér Nøgle
- Slet Nøgle
- Opret Nøgle
- Redigér Nøgle
- Søg
-
-
- Signér
- Besked
- Fil
- Kodeord
- Igen
- Algoritme
- ASCII Rustning
- Offentlig(e) Nøgle(r)
- Slet Efter Kryptering
- Slet Efter Afkryptering
- Krypteringsalgoritme
- Hash-algoritme
- Offentlig Nøgle
- Kodeord
- Kodeords Cache
- Besked Komprimering
- Fil Komprimering
- Vælg
- 1 Valgt
- Valgt
- <ukendt>
- <ingen>
- <ingen nøgle>
- -
- <udløber ikke>
-
- kan kryptere
- kan signere
- udløbet
- ugyldig
-
-
- Ingen
- Kun signering
- Kun kryptering
- Signér og kryptér
- 15 secs
- 1 min
- 3 min
- 5 min
- 10 min
- 20 min
- 40 min
- 1 timer
- 2 timer
- 4 timer
- 8 timer
- Indtil slut
- DSA
- ElGamal
- RSA
- Åbn...
- Gem Som...
- Vælg Fil Som Skal Krypteres...
- Vælg Fil Som Skal Afkrypteres...
- Åbn
- Gem
- Advarsel
- Fejl
- Advarsel: %s
- Fejl: %s
-
-
- Forkert kodeord.
- Bruger clipboardets indhold.
- Nøgle gemt.
- Sæt et kodeord først.
- Der er ikke installeret en kompatibel fil håndtering.
- Kodeordet matchede ikke.
- Et tomt kodeord er ikke tilladt.
- Symmetrisk kryptering.
- %s
- Er du sikker på at du vil slette \n%s?
- Filen er slettet.
- Vælg en fil først.
- Afkryptering lykkedes.
- Kryptering lykkedes.
- Kryptering til clipboard lykkedes.
- Tast kodeordet to gange.
- Vælg mindst én krypteringsnøgle.
- Vælg mindst én krypteringsnøgle eller signaturnøgle.
- Angiv navnet på filen der skal krypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
- Angiv navnet på filen der skal afkrypteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
- Angiv den Google Mail konto du ønsker at tilføje.
- Angiv hvilken fil du ønsker at importere nøgler fra. (.asc eller .gpg)
- Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
- Angiv hvilken fil der skal eksporteres til.\nADVARSEL! Du er ved at eksportere PRIVATE nøgler.\nADVARSEL! Filen vil blive overskrevet hvis den allerede eksisterer.
- Ønsker du at slette \'%s\'-nøglen?\nDu kan ikke fortryde denne handling!
- Ønsker du at slette den PRIVATE nøgle, \'%s\'?\nDu kan ikke fortryde denne handling!
- Tilføjelse af %1$s nøgle(r) og opdatering af %2$s nøgle(r) lykkedes."
- Tilføjelse af %s nøgle(r) lykkedes.
- Opdatring af %s nøgle(r) lykkedes.
- Ingen nøgler blev tilføjet eller opdateret.
- Eksport af 1 nøgle lykkedes.
- Eksport af %s nøgler lykkedes.
- Ingen nøgler blev eksporteret.
- NB: Kun undernøgler understøtter ElGamal, og for ELGamal vil den nærmeste nøglestørrelse - 1536, 2048, 3072, 4096, eller 8192 - blive brugt.
- Kunne ikke finde %08X nøglen.
-
-
- \'%s\' kunne ikke slettes
- filen findes ikke
- ingen egnet privat nøgle fundet
- ingen kendt krypterings måde fundet
- ekstern storage er ikke parat
- \'%s\'-kontoen blev ikke fundet
- \'%s\'-kontoen kunne ikke tilføjes
- ugyldig email \'%s\'
- nøgle størrelsen skal være på mindst 512bit
- hoved nøglen kan ikke være en ElGamal nøgle
- ukendt algoritme valg
- angiv et navn
- angiv en email adresse
- der skal bruges mindst ét bruger-id
- hovedbruger-id kan ikke være tomt
- der skal mindst bruges en hovednøgle
- udløbsdatoen skal være senere end oprettelsesdatoen
- hverken krypteringsnøgler eller kodeord modtaget
- signering mislykkedes
- intet kodeord modtaget
- ingen signatur nøgle modtaget
- ikke gyldig krypteringsdata
- beskadiget data
- kunne ikke finde en pakke med symmetrisk kryptering
- forkert kodeord
- der opstod en fejl da nøglen eller nøglerne skulle gemmes
-
-
- færdig.
- starter op...
- gemmer...
- importerer...
- eksporterer...
- genererer nøgle, dette kan tage et stykke tid...
- opbygger nøgle...
- forbereder hovednøgle...
- bekræfter hovednøgle...
- bygger hovednøglering...
- tilføjer undernøgler...
- gemmer nøglering...
- importerer private nøgler...
- importerer offentlige nøgler...
- genloader nøgler...
- eksporterer nøgle...
- eksporterer nøgler...
- henter signatur nøgle...
- henter nøgle...
- forbereder streams...
- krypterer data...
- afkrypterer data...
- forbereder signatur...
- skaber signatur...
- behandler signatur...
- bekræfter signatur...
- signerer...
- læser data...
- søger nøgle...
- afkomprimerer data...
- bekræfter integritet...
-
-
- Læs nøgle detaljer fra APG.
- Læs om offentlige og private nøgler gemt i APG, f. eks. nøgle ID og bruger ID. Selve nøglerne kan IKKE læses.
-
-
- Kryptér
- Afkryptér
- Importér Offentlige Nøgler
- Importér Private Nøgler
- Søg I Offentlige Nøgler
- Søg I Private Nøgler
- Filter: \"%s\"
-
-
\ No newline at end of file
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
deleted file mode 100644
index 732d58b0a..000000000
--- a/res/values-de/strings.xml
+++ /dev/null
@@ -1,269 +0,0 @@
-
-
-
-
-
-
- Posteingang
- Öffentliche Schlüssel verwalten
- Private Schlüssel verwalten
- Empfänger auswählen
- Signatur auswählen
- Verschlüsseln
- Entschlüsseln
- Passwort
- Schlüssel erzeugen
- Schlüssel bearbeiten
- Einstellungen
- Passwort ändern
- Passwort wählen
- "Mail senden ..."
- In eine Datei verschlüsseln
- In eine Datei entschlüsseln
- Konto hinzufügen
- Schlüssel importieren
- Einzelnen Schlüssel exportieren
- Mehrere Schlüssel exportieren
- Kein Schlüssel gefunden
-
-
- Benutzer-IDs
- Schlüssel
- Allgemein
- Voreinstellungen
-
-
-
- In die Zwischenablage verschlüsseln
- Verschlüsseln und per Mail senden
- Verschlüsseln
- Entschlüsseln
- Verifizieren
- Empfänger auswählen
- Antworten
- Nachricht verschlüsseln
- Nachricht entschlüsseln
- Datei verschlüsseln
- Datei entschlüsseln
- Speichern
- Abbrechen
- Löschen
- Niemals
- Filter zurücksetzen
- Passwort ändern
- Passwort wählen
-
-
- Über
- GMail-Konto hinzufügen
- Konto entfernen
- Öffentliche Schlüssel verwalten
- Private Schlüssel verwalten
- Einstellungen
- Schlüssel importieren
- Mehrere Schlüssel exportieren
- Einzelnen Schlüssel exportieren
- Einzelnen Schlüssel löschen
- Schlüssel erzeugen
- Schlüssel bearbeiten
- Suche
-
-
- Signieren
- Nachricht
- Datei
- Passwort
- Wiederholen
- Algorithmus
- ASCII Armor
- Öffentliche(r) Schlüssel
- Nach Verschlüsselung löschen
- Nach Entschlüsselung löschen
- Verschlüsselungs-Algorithmus
- Hash-Algorithmus
- Öffentlicher Schlüssel
- Passwort
- Passwort-Cache
- Nachrichten-Kompression
- Datei-Kompression
- Auswählen
- 1 ausgewählt
- Ausgewählt
- <Unbekannt>
- <Kein>
- <Kein Schlüssel>
- -
- <Kein Verfallsdatum>
-
- Kann verschlüsseln
- Kann signieren
- Verfallen
- Ungültig
-
-
- Keine Auswahl
- Nur signieren
- Nur verschlüsseln
- Signieren und verschlüsseln
- 15 Sek.
- 1 Min.
- 3 Min.
- 5 Min.
- 10 Min.
- 20 Min.
- 40 Min.
- 1 Std.
- 2 Std.
- 4 Std.
- 8 Std.
- Bis Programmende
- DSA
- ElGamal
- RSA
- Öffnen ...
- Speichern unter ...
- Zu entschlüsselnde Datei wählen ...
- Zu verschlüsselnde Datei wählen ...
- Öffnen
- Speichern
- Achtung
- Fehler
- Achtung: %s
- Fehler: %s
-
-
- Falsches Passwort.
- Zwischenablage wird verwendet.
- Schlüssel gespeichert.
- Zuerst Passwort festlegen.
- Kein kompatibler Dateimanager installiert.
- Passwörter stimmen nicht überein.
- Leere Passwörter sind unzulässig.
- Symmetrische Verschlüsselung.
- %s
- Soll\n%s wirklich gelöscht werden?
- Erfolgreich gelöscht.
- Zuerst eine Datei auswählen.
- Entschlüsselung erfolgreich.
- Verschlüsselung erfolgreich.
- Verschlüsselung in die Zwischenablage erfolgreich.
- Passwort bitte zwei Mal eingeben.
- Bitte mindestens einen Schlüssel zur Verschlüsselung auswählen.
- Bitte mindestens einen Schlüssel zur Verschlüsselung oder zum Signieren auswählen.
- Bitte Zieldatei für Verschlüsselung angeben.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
- Bitte Zieldatei für Entschlüsselung angeben.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
- Bitte Google-Mail-Konto angeben, das hinzugefügt werden soll.
- Bitte Datei angeben, aus der Schlüssel importiert werden sollen. (.asc oder .gpg)
- Bitte Zieldatei zum Export angeben.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
- Bitte Zieldatei zum Export angeben.\nACHTUNG! Es sollen private Schlüssel exportiert werden.\nACHTUNG! Vorhandene Datei gleichen Namens wird überschrieben.
- Soll der Schlüssel \'%s\' wirklich gelöscht werden?\nDiese Aktion lässt sich nicht rückgängig machen!
- Soll der PRIVATE Schlüssel \'%s\' wirklich gelöscht werden?\nDiese Aktion lässt sich nicht rückgängig machen!
- Erfolgreich %1$s Schlüssel hinzugefügt und %2$s Schlüssel aktualisiert.
- Erfolgreich %s Schlüssel hinzugefügt.
- Erfolgreich %s Schlüssel aktualisiert.
- Keine Schlüssel hinzugefügt oder aktualisiert.
- Erfolgreich 1 Schlüssel exportiert.
- Erfolgreich %s Schlüssel exportiert.
- Kein Schlüssel exportiert.
- Hinweis: Nur Subschlüssel unterstützen ElGamal, und für ElGamal wird die nächstmögliche Schlüssellänge 1536, 2048, 3072, 4096 oder 8192 genutzt.
- Es konnte kein Schlüssel %08X gefunden werden.
-
-
- Löschen von \'%s\' fehlgeschlagen
- Datei nicht gefunden
- Kein geeigneter privater Schlüssel gefunden
- Keine bekannte Verschlüsselungsmethode gefunden
- Externer Speicher nicht bereit
- Konto \'%s\' nicht gefunden
- Hinzufügen von Konto \'%s\' fehlgeschlagen
- Ungültige E-Mail \'%s\'
- Schlüssellänge von mindestens 512bit erforderlich
- Primärschlüssel darf kein ElGamal-Schlüssel sein
- Unbekannter Algorithmus ausgewählt
- Angabe eines Namens erforderlich
- Angabe einer E-Mail-Adresse erforderlich
- Mindestens eine Benutzer-ID erforderlich
- Haupt-Benutzer-ID darf nicht leer bleiben
- Mindestens ein Primärschlüssel erforderlich
- Verfallsdatum muss später als Erstellungsdatum liegen
- Kein(e) Schlüssel oder Passwort zur Verschlüsselung angegeben
- Signieren fehlgeschlagen
- Kein Passwort angegeben
- Kein Schlüssel zum Signieren angegeben
- Keine gültigen Verschlüsselungsdaten
- Daten nicht lesbar
- Kein Paket mit symmetrischer Verschlüsselung gefunden
- Falsches Passwort
- Fehler beim Abspeichern eines oder mehrerer Schlüssel
-
-
- Abgeschlossen.
- Initialisierung ...
- Speicherung ...
- Import ...
- Export ...
- Schlüssel wird erzeugt, dies kann eine Weile dauern ...
- Schlüssel wird erstellt ...
- Primärschlüssel wird vorbereitet ...
- Primärschlüssel wird zertifiziert ...
- Primärschlüsselbund wird erstellt ...
- Unterschlüssel werden hinzugefügt ...
- Schlüsselbund wird gespeichert ...
- Private Schlüssel werden importiert ...
- Öffentliche Schlüssel werden importiert ...
- Schlüssel werden neu geladen ...
- Schlüssel wird exportiert ...
- Schlüssel werden exportiert ...
- Signaturschlüssel wird extrahiert ...
- Schlüssel wird extrahiert ...
- Daten werden vorbereitet ...
- Daten werden verschlüsselt ...
- Daten werden entschlüsselt ...
- Signatur wird vorbereitet ...
- Signatur wird erzeugt ...
- Signatur wird verarbeitet...
- Signatur wird verifiziert ...
- Signieren ...
- Daten werden gelesen ...
- Schlüssel wird gesucht ...
- Daten werden dekomprimiert ...
- Integrität wird verifiziert ...
-
-
- Schlüsseldetails von APG auslesen.
- Details der öffentlichen und privaten Schlüssel können von APG ausgelesen werden, etwa Schlüssel-ID und Benutzer-IDs. Die Schlüssel selbst können NICHT gelesen werden.
-
-
- Verschlüsseln
- Entschlüsseln
- Öffentliche Schlüssel importieren
- Private Schlüssel importieren
- Öffentliche Schlüssel suchen
- Private Schlüssel suchen
- Filter: \"%s\"
-
-
- schnell
- langsam
- sehr langsam
-
-
\ No newline at end of file
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
deleted file mode 100644
index a2faf56d9..000000000
--- a/res/values-es/strings.xml
+++ /dev/null
@@ -1,303 +0,0 @@
-
-
-
-
-
-
- Bandeja de Entrada
- Administrar Llaves Publicas
- Administrar Llaves Privadas
- Seleccionar Destinatarios
- Seleccionar Firma
- Cifrar
- Descifrar
- Frase de Paso
- Crear Llave
- Editar Llave
- Preferencias
- Preferencias del Servidor de Llaves
- Cambiar Frase de Paso
- Establecer Frase de Paso
- "Enviar Correo..."
- Cifrar en Fichero
- Descifrar en Fichero
- Añadir Cuenta
- Importar Llaves
- Exportar Llave
- Exportar Llaves
- Llave no Encontrada
- Empezando
- Petición al Servidor de Llaves
-
-
- Identidades de Usuario
- Llaves
- General
- Valores por Defecto
- Avanzado
-
-
- Firmar En Portapapeles
- Cifrar En Portapapeles
- Cifrar y Enviar
- Firmar y Enviar
- Cifrar
- Firmar
- Descifrar
- Verificar
- Seleccionar Destinatarios
- Contestar
- Cifrar Mensaje
- Descifrar Mensaje
- Cifrar Fichero
- Descifrar Mensaje
- Guardar
- Cancelar
- Borrar
- Ninguno
- Limpiar Filtro
- Cambiar Frase de Paso
- Establecer Frase de Paso
- Buscar
-
-
- Acerca De
- Añadir Cuenta de Gmail
- Borrar Cuenta
- Administrar Llaves Publicas
- Administrar Llaves Privadas
- Preferencias
- Importar Llaves
- Exportar Llaves
- Exportar Llave
- Borrar Llave
- Crear Llave
- Editar Llave
- Buscar
- Ayuda
- Servidor de Llaves
- Actualizar
-
-
- Firmar
- Mensaje
- Fichero
- Frase de Paso
- Otra Vez
- Algoritmo
- Armadura ASCII
- Llave(s) Publica(s)
- Borrar Una Vez Cifrado
- Borrar Una Vez Descifrado
- Borrar Tras Importar
- Algoritmo De Cifrado
- Algoritmo Hash
- Llave Pública
- Frase de Paso
- Caché De La Frase de Paso
- Compresión de Mensaje
- Compresión de Fichero
- Idioma
- Forzar Firmas V3
- Servidores De Llaves
- Identidad de Llave
- Creación
- Expira
- Uso
- Tamaño De La Llave
- Identidad Principal Del Usuario
- Nombre
- Comentario
- Email
- Seleccionar
- 1 Seleccionada
- Seleccionada
- <desconocido>
- <ninguno>
- <sin llave>
- -
- <no expira>
-
- puede cifrar
- puede firmar
- expirada
- no valida
- %s servidor de llave(s)
-
-
- Ninguno
- Sólo firmar
- Sólo cifrar
- Firmar y Cifrar
- 15 segs
- 1 min
- 3 mins
- 5 mins
- 10 mins
- 20 mins
- 40 mins
- hasta salir
- Sistema por defecto
- DSA
- ElGamal
- RSA
- Abrir...
- Guardar Como...
- Seleccionar Fichero Para Cifrar...
- Seleccionar Fichero Para Descifrar...
- Abrir
- Guardar
- Advertencia
- Error
- Advertencia: %s
- Error: %s
-
-
- Frase de paso incorrecta.
- Usando contenido del portapapeles.
- Llave guardada.
- Establece primero una frase de paso.
- Administrador de ficheros instalado no compatible.
- La frase de paso no concuerda.
- No se permiten palabras de paso vacías.
- Cifrado simetrico.
- %s
- ¿Seguro que quieres borrar\n%s?
- Borrado completo.
- Selecciona primero un fichero.
- Descifrado correcto.
- Cifrado correcto.
- Cifrado en portapapeles correcto.
- Introduce la frase de paso dos veces.
- Selecciona al menos una llave de cifrado.
- Selecciona al menos una llave de cifrado o de firma.
- Por favor, indica el nombre del fichero a cifrar.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
- Por favor, indica el nombre del fichero a descifrar.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
- Indica la cuenta de Google Mail que quieres añadir.
- Por favor, indica el fichero desde el cual importar las llaves. (.asc o .gpg)
- Por favor, indica el fichero al que exportar.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
- Por favor, indica el fichero al que exportar.\nADVERTENCIA! Estás a punto de exportar las llaves PRIVADAS.\nADVERTENCIA! El fichero se sobre-escribirá si ya existe.
- ¿Realmente quieres borrar la llave \'%s\'?\nNo puede deshacerse!
- ¿Realmente quieres borrar la llave SECRETA \'%s\'?\nNo puede deshacerse!
- Se han añadido %1$s llave(s) y actualizado %2$s llave(s) de forma correcta."
- Se ha(n) añadido %s llave(s) de forma correcta.
- Se ha(n) actualizado %s llave(s) de forma correcta.
- No se ha añadido ni actualizado ninguna llave.
- Se ha exportado correctamente 1 llave.
- Se han exportado %s llaves de forma correcta.
- No se han exportado llaves.
- Nota: solamente las sub-llaves admiten ElGamal, y para ElGamal se usará el tamaño más cercano a 1536, 2048, 3072, 4096, o 8192.
- No se pudo encontrar la llave %08X.
- Se ha(n) encontrado %s llave(s).
- Firma desconocida, pulsa para buscarla.
- Edición de llaves aún en fase beta.
-
-
- borrado de \'%s\' fallido
- no se encontró el fichero
- no se encontró la llave secreta adecuada
- no se encontró un medio de cifrado
- el sistema de almacenamiento externo no está preparado
- la cuenta \'%s\' no se encontró
- no tienes permisos para leer esta cuenta
- la creación de la cuenta \'%s\' ha fallado
- dirección de correo inválida \'%s\'
- el tamaño mínimo debe ser de 512bits
- la llave maestra no puede ser ElGamal
- algoritmo desconocido
- necesitas especificar un nombre
- necesitas indicat una dirección de correo
- necesitas al menos una identidad de usuario
- el usuario principal no puede estar vacío
- necesitas al menos una llae maestra
- la fecha de expiración debe ser posterior a la fecha de creación
- no se ha(n) indicado llave(s) de cifrado o frase de paso
- el proceso de firma ha fallado
- no se ha indicado frase de paso
- no se ha indicado llave de firma
- datos de cifrado no válidos
- datos corruptos
- no se ha podido encontrar un paquete con cifrado simétrico
- frase de paso incorrecta
- hubo un error al guardar alguna(s) llave(s)
-
-
- hecho.
- iniciando...
- guardando...
- importando...
- exportando...
- generando la llave, esto puede tardar un rato...
- creando la llave...
- preparando la llave maestra...
- certificando la llave maestra...
- creando el anillo maestro de llaves...
- añadiendo sub-llaves...
- guardando anillo de llaves...
- importando llaves privadas...
- importando llaves públicas...
- re-cargando llaves...
- exportando llave...
- exportando llaves...
- extrayendo llave de firma...
- extrayendo llave...
- preparando flujos...
- cifrando datos...
- descifrando datos...
- preparando firma...
- generando firma...
- procesanto firma...
- verificando firma...
- firmando...
- leyendo datos...
- buscando la llave...
- descomprimiendo datos...
- verificando integridad...
- borrando \'%s\' de forma segura...
- realizando petición...
- realizando %s petición(es)...
-
-
- Leer detalles de llave desde APG.
- Leer detalles de llaves pública y privada almacenados en APG, como identidades de llave y usuario. Las llaves en si NO se pueden leer.
-
-
- Cifrar
- Descifrar
- Importar Llaves Públicas
- Importar Llaves Privadas
- Buscar Llaves Públicas
- Buscar Llaves Privadas
- Filtro: \"%s\"
-
-
- rápido
- lento
- muy lento
-
-
-
- uedes instalar K-9 Mail para una mejor integración, soporta APG para PGP de forma nativa y permite difrar/descifrar mensajes directamente.
-\n\nSe recomienda instalar OI File Manager o ASTRO para poder usar el botón de navegación para seleccioar ficheros en APG.
-\n\nPrimero necesitarás algunas llaves. Puedes importarlas mediante los menús de opciones en \"Administrar Llaves Públicas\" y \"Administrar Llaves Privadas\" o crearlas mediante \"Administrar Llaves Privadas\".
-\n\nTambién puedes añadir una cuenta GMail como actividad principal mediante \"Añadir Cuenta\", lo que simplifica el descifrado de mensajes recibidos en esa cuenta.
-\n\nDale un vistazo a los menús de opciones para encontrar más funciones.
-
-
\ No newline at end of file
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
deleted file mode 100644
index 21a6d3486..000000000
--- a/res/values-it/strings.xml
+++ /dev/null
@@ -1,314 +0,0 @@
-
-
-
-
-
-
- Posta in arrivo
- Gestione Chiave Pubblica
- Gestione Chiave Privata
- Seleziona i Destinatari
- Seleziona Firma
- Critta
- Decritta
- Pass Phrase
- Crea Chiave
- Modifica Chiave
- Preferenze
- Preferenze Key Server
- Cambia Pass Phrase
- Imposta Pass Phrase
- "Invia mail..."
- Critta su File
- Decritta su File
- Aggiungi Acconto
- Importa Chiavi
- Esporta Chiave
- Esporta Chiavi
- Chiave non trovata
- Guida
- Query Key Server
- Firma chiave sconosciuta
-
-
- User IDs
- Chiavi
- Generale
- Defaults
- Avanzate
-
-
- Firma su Clipboard
- Critta su Clipboard
- Critta ed invia per mail
- Firma ed invia per mail
- Critta
- Firma
- Decritta
- Verifica
- Seleziona i Destinatari
- Rispondi
- Critta Messaggio
- Decritta Messaggio
- Critta File
- Decritta File
- Salva
- Annulla
- Cancella
- Nessuno
- Cancella Filtro
- Cambia Pass Phrase
- Imposta Pass Phrase
- Cerca
-
-
- Informazioni
- Aggiungi Acconto GMail
- Rimuovi Acconto
- Gestisci Chiavi Pubbliche
- Gestisci Chiavi Private
- Impostazioni
- Importa Chiavi
- Esporta Chiavi
- Esporta Chiave
- Rimuovi Chiave
- Crea Chiave
- Modifica Chiave
- Cerca
- Aiuto
- Key Server
- Aggiorna
-
-
- Firma
- Messaggio
- File
- Pass Phrase
- Conferma Pass Phrase
- Algoritmo
- ASCII Armour
- Chiavi Pubbliche
- Cancella dopo la crittazione
- Cancella dopo la decrittazione
- Cancella dopo l\'importazione
- Algoritmo di Crittazione
- Algoritmo di Hash
- Chiave Pubblica
- Pass Phrase
- Pass Phrase Cache
- Compressione Messaggio
- Compressione File
- Lingua
- Forza l\'uso di firme V3
- Key Servers
- ID chiave
- Creazione
- Scadenza
- Utilizzo
- Dimensione chiave
- User ID principale
- Nome
- Commento
- Email
- Seleziona
- 1 Selezionato
- Selezionato
- <sconosciuto>
- <nessuno>
- <nessuna chiave>
- -
- <nessuna scadenza>
-
- crittazione consentita
- firma consentita
- scaduto
- non valido
- %s key server(s)
- fingerprint
-
-
- Nessuno
- Solo Firma
- Critta solamente
- Firma e critta
- 15 sec
- 1 min
- 3 min
- 5 min
- 10 min
- 20 min
- 40 min
- 1 ora
- 2 ore
- 4 ore
- 8 ore
- tutta la sessione
- Default di sistema
- DSA
- ElGamal
- RSA
- Apri...
- Salva con nome...
- Seleziona il file da crittare...
- Seleziona il file da decrittare...
- Apri
- Salva
- Attenzione
- Errore
- Attenzione: %s
- Errore: %s
-
-
- Pass Phrase errata.
- Utilizza il contenuto della clipboard.
- Chiave salvata..
- Imposta prima una Pass Phrase.
- Nessun file manager compatibile installato.
- La Pass Phrase non è corretta
- La Pass Phrases non può essere vuota.
- Crittazione simmetrica.
- %s
- Sei sicuro di procedere con la cancellazione di\n%s?
- Cancellazione eseguita..
- Selezionare un file.
- Decrittazione eseguita.
- Crittazione eseguita.
- Crittazione vero la clipboard eseguita.
- Scrivere la Pass Phrase due volte.
- Selezionare almeno una chiave.
- Selezionare almeno una chiave oppure una firma.
- Specificare il file che si desidera crittare.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
- Specificare il file che si desidera decrittare.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
- Specificare l\'acconto Google Mail che si vuole aggiungere.
- Specificare il file da cui importare le chiavi (.asc or .gpg).
- Specificare il file per l\'esportazione.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
- Specificare il file per l\'esportazione.\nATTENZIONE! Stai esportando la chiave privata.\nATTENZIONE! Se il file esiste, sarà sovrascritto.
- Confermi la cancellazione della chiave \'%s\'?\nL\'operazione è irreversibile!
- Confermi la cancellazione della chiave PRIVATA \'%s\'?\nL\'operazione è irreversibile!
- Sono state correttamente inserite %1$s chiavi ed aggiornate %2$s chiavi."
- Sono state correttamente inserite %s chiavi.
- Sono state correttamente aggiornate %s chiavi.
- Nessuna chiave è stata aggiunta o modificata.
- La chiave è stata correttamente esportata.
- Sono state correttamente esportate %s chiavi.
- Nessuna chiave è stata esportata.
- Nota: solo le sotto-chiavi supportano ElGamal, e per ElGamal sarà utilizzata la dimensione più vicina a 1536, 2048, 3072, 4096, oppure 8192.
- Non è stato possibile trovare la chiave %08X.
- Trovate %s chiavi.
- Firma sconosciuta, premi per controllare la chiave.
- L\'editing delle chiavi è una funzionalità ancora in fase beta.
- %s chiavi segrete ignorate. Se hai esportato le chiavi con l\'opzione\n --export-secret-subkeys\nprovare ad utilizzare il parametro\n --export-secret-keys\n.
- La chiave %s è sconosciuta; si desidera provare a cercarla su un keyserver?
-
-
- cancellazione di \'%s\' fallita
- file non trovato
- non è stata trovata nessuna chiave privata utilizzabile
- non è stato trovato nessun tipo di crittazione valido
- la scheda di memoria non è pronta o non è utilizzabile
- acconto \'%s\' non trovato
- accesso in lettura all\'account negato
- non è stato possibile aggiungere l\'acconto \'%s\'
- mail non valida \'%s\'
- la dimensione della chiave deve essere almeno di 512bit
- la chiave master non può essere una chiave ElGamal
- algoritmo selezionato sconosciuto
- è necessario specificare un nome
- è necessario specificare una email
- è necessario almeno uno user id
- lo user id principale non può essere vuoto
- è necessaria almeno una chiave master
- la data di scadenza deve essere successiva alla data di creazione
- non è stata specifica una chiave oppure una Pass Phrase
- firma fallita
- Pass Phrase non specificata
- firma non specificata
- dati di crittazione non validi
- dati corrotti
- non è stato possibile trovare pacchetto con crittazione simmetrica
- Pass Phrase errata
- errore durante il salvataggio di alcune chiavi
- non è stato possibile estrarre la chiave privata
-
-
- eseguito.
- inizializzazione in corso...
- salvataggio in corso...
- importazione in corso...
- esportazione in corso...
- generazione della chiave in corso; questo processo potrebbe richiedere alcuni minuti...
- costruzione della chiave in corso...
- impostazione della chiave master in corso...
- certificazione della chiave master in corso...
- costruzione del master key ring in corso...
- aggiunta delle sotto-chiavi in corso...
- salvataggio del key ring in corso...
- importazione delle chiavi private in corso...
- importazione delle chiavi pubbliche in corso...
- ricaricamento chiavi in corso...
- esportazione chiave in corso...
- esportazione chiavi in corso...
- estrazione firma in corso...
- estrazione chiave in corso...
- preparazione del flusso dati in corso...
- crittazione in corso...
- decrittazione in corso...
- impostazione della firma in corso...
- generazione della firma in corso...
- elaborazione della firma in corso...
- verifica della firma in corso...
- applicazione della firma in corso...
- lettura in corso...
- ricerca chiave in corso...
- decompressione dei dati in corso...
- verifica integrità dati in corso...
- rimozione sicura di \'%s\'...
- interrogazione in corso...
- interrogazione di %s in corso...
-
-
- Accesso in lettura ai dettagli della chiave da APG.
- Accesso in lettura ai dettagli delle chiavi pubbliche e private memorizzate in APG, compresi ID della chiave e ID dell\'utente. Non è possibile accedere in lettura alle chiavi in sè.
-
-
- Critta
- Decritta
- Importa Chiavi Pubbliche
- Importa Chiavi Private
- Cerca Chiavi Pubbliche
- Cerca Chiavi Private
- Filtra: \"%s\"
-
-
- veloce
- lenta
- molto lenta
-
-
-
- er una migliore integrazione, si consiglia di installare K-9 Mail; questo applicativo supporta infatti APG e consente di crittare/decrittare direttamente le mail.
-\n\nPer poter utilizzare il bottone \"sfoglia\" per la selezione dei file su APG, si consiglia di installare OI File Manager oppure ASTRO.
-\n\nCome prima cosa, è necessario disporre di una o più chiavi. E\' possibile importare chiavi esistenti tramite l\'opzione \"Manage Public Keys\" e \"Manage Secret Keys\", oppure crearne una nuova tramite l\'opzione \"Manage Secret Keys\".
-\n\nE\' possibile aggiungere un account GMail tramite l\'opzione \"Aggiungi Acconto GMail\"; questo semplifica il processo di decrittaggio delle mail ricevute sulla propria casella di posta su GMail.
-\n\nProva e controlla le opzioni dei vari menù per esplorare e conoscere tutte le funzionalità di APG.
-
-
\ No newline at end of file
diff --git a/res/values-no/strings.xml b/res/values-no/strings.xml
deleted file mode 100644
index 825701f38..000000000
--- a/res/values-no/strings.xml
+++ /dev/null
@@ -1,313 +0,0 @@
-
-
-
-
-
-
- Epost innboks
- Håndter Offentlige Nøkler
- Håndter Private Nøkler
- Velg Mottakere
- SVelg Signatur
- Krypter
- Dekrypter
- Passord
- Lag Nøkkel
- Rediger Nøkkel
- Egenskaper
- Nøkkelserver Egenskaper
- Endre Passord
- Sett Passord
- "Send Epost..."
- Krypter Til Fil
- Dekrypter Til Fil
- Legg Til Konto
- Importer Nøkler
- Eksporter Nøkkel
- Eksporter Nøkler
- Finner Ikke Nøkkel
- Kom I Gang
- Søk Nøkkelserver
-
-
- Bruker IDer
- Nøkler
- Generelt
- Standard Innstillinger
- Avansert
-
-
- Signer Til Utklippstavle
- Krypter Til Utklippstavle
- Krypter Og Send Epost
- Signer Og Send Epost
- Krypter
- Signer
- Dekrypter
- Verifiser
- Velg Mottakere
- Svar
- Krypter Melding
- Dekrypter Melding
- Krypter Fil
- Dekrypter Fil
- Lagre
- Avbryt
- Slett
- Ingen
- Tøm Filter
- Endre Passord
- Sett Passord
- Søk
-
-
- Om APG
- Legg til GMail Konto
- Slett Konto
- Håndter Offentlige Nøkler
- Håndter Private Nøkler
- Instillinger
- Importer Nøkler
- Eksporter Nøkler
- Eksporter Nøkkel
- Slett Nøkkel
- Opprett Nøkkel
- Rediger Nøkkel
- Søk
- Hjelp
- Nøkkelserver
- Oppdater
-
-
- Signer
- Melding
- Fil
- Passord
- Igjen
- Algoritme
- ASCII Armour
- Offentlig(e) Nøkkel(/Nøkler)
- Slett Etter Kryptering
- Slett Etter Dekryptering
- Slett Etter Import
- Krypteringsalgoritme
- Hash Algoritme
- Offentlig Nøkkel
- Passord
- Passord Mellomlager
- Melding komprimering
- Fil Komprimering
- Språk
- Tving V3 Signaturer
- Nøkkelserver
- Nøkkel ID
- Laget
- Utløper
- Bruk
- Nøkkelstørrelse
- Hoved Bruker ID
- Navn
- Kommentar
- Epost
- Velg
- 1 Valgt
- Valgt
- <ukjent>
- <ingen>
- <ingen nøkkel>
- -
- <utløper ikke>
-
- kan kryptere
- kan signere
- utløpt
- ugyldig
- %s nøkkelserver(e)
-
-
- Ingen
- Kun Signer
- Kun Krypter
- Signer Og Krypter
- 15 sek
- 1 min
- 3 min
- 5 min
- 10 min
- 20 min
- 40 min
- 1 time
- 2 timer
- 4 timer
- 8 timer
- til avsluttning
- System
- DSA
- ElGamal
- RSA
- Åpne...
- Lagre Som...
- Velg Fil Som Skal Krypteres...
- Velg Fil Som Skal Dekrypteres...
- Åpne
- Lagre
- Advarsel
- Feil
- Advarsel: %s
- Feil: %s
-
-
- Feil passord.
- Bruker innhold i utklippstavle.
- Nøkkel lagret.
- Sett en passord først.
- Det er ikke installert en kompitabel filhåndtering.
- Passordet matchet ikke.
- Tomme passord er ikke lov.
- Symmetrisk kryptering.
- %s
- Er du sikker på at du vil slette\n%s?
- Filen er slettet.
- Velg en fil først.
- Dekryptering lykktes.
- Krytering lyktes.
- Kryptering til utklippstavle lykktes.
- Tast passordet to ganger.
- Velg minst én krypteringsnøkkel.
- Velg minst én krypteringsnøkkel eller signaturnøkkel.
- Vennligst angi hvilken fil det skal krypteres til.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
- Vennligst angi hvilken fil det skal dekrypteres til.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
- Angi hvilken Google Mail konto du vil legge til
- Vennligst angi hvilken fil du vil importere fra. (.asc eller .gpg)
- Vennligst angi hvilken fil du vil eksportere til.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
- Vennligst angi hvilken fil du vil eksportere til.\nADVARSEL! Du er på vei til p eksportere HEMMELIGE nøkkler.\nADVARSEL! Filen vil bli overskrevet, hvis den eksisterer.
- Vil du virkelig slette nøkkelen? \'%s\'?\nDu kan ikke gjøre om dette!
- Vil du virkelig slette den HEMMELIGE nøkkelen? \'%s\'?\nDu kan ikke gjøre om dette!
- Lykktes å legge til %1$s nøkkel(er) og oppdatere %2$s nøkkel(er)."
- Lykktes å legge til %s nøkkel(er).
- Lykktes å oppdatere %s nøkkel(er).
- Ingen nøkler lagt til eller oppdatert.
- Lykktes å eksportere 1 nøkkel.
- Lykktes å eksportere %s nøkler.
- Ingen nøkler eksportert.
- Noter: kun undernøkler støtter ElGamal, og for ElGamal vil den nærmeste nøkkelstørrelsen 1536, 2048, 3072, 4096, eller 8192 bli brukt.
- Kunne ikke finne nøkkel %08X.
- Fang %s nøkkel(er).
- Ukjent signatur, trykk for å sjekke nøkkel.
- Nøkkelredigering er enda beta.
-
-
- sletting \'%s\' mislyktes
- finner ikke fil
- finner ingen passende hemmelig nøkkel
- ingen kjente krypteringsmåter funnet
- ekstern lagringsplass ikke klar
- finner ikke kontoen - \'%s\'
- ikke tillatelse til lese konto
- kunne ikke legge til konto - \'%s\'
- ugyldig epost \'%s\'
- størrelsen på nøkkel må være minst 512bit
- hovednøkkelen kan ikke være en ElGamal nøkkel
- ukjent algoritme valgt
- Du må spesifisere et navn
- du må spesifisere en epost adresse
- må ha minst en bruker id
- hovedbruker id kan ikke være tom
- trenger minst én hovednøkkel
- utløpsdatoen må komme etter opprettelsesdatoen
- ingen krypteringsnøkkel(er) eller passord gitt
- signering feilet
- ingen passord gitt
- ingen signaturnøkkel gitt
- ikke gyldig krypteringsdata
- korrupt data
- kunne ikke finne en pakke med symmetrisk kryptering
- Feil passord
- Feil under lagring av noen nøkkel(er)
-
-
- ferdig.
- initialiserer...
- lagrer...
- importerer...
- eksporterer...
- genererer nøkkel, dette kan ta en stund...
- bygger nøkkel...
- forbereder hovednøkkel...
- berefter hovednøkkel...
- bygger hovednøkkelring...
- legger til undernøkler...
- lagrer nøkkelring...
- importerer hemmelige nøkler...
- importerer offentlige nøkler...
- gjennlaster nøkler...
- eksporterer nøkkel...
- eksporterer nøkler...
- henter signaturnøkkel...
- henter nøkkel...
- forereder strømmer...
- krypterer data...
- dekrypterer data...
- forbereder signatur...
- genererer signatur...
- behandler signatur...
- bekrefter signatur...
- signerer...
- leser data...
- finner nøkkel...
- dekomprimerer data...
- bekrefter integritet...
- sletter \'%s\' sikkert...
- søker...
- søker %s...
-
-
- Les nøkkel-detaljer fra APG.
- Les nøkkel-detaljer fra APG om offentlige og private nøkler lagret i APG, som f.eks. nøkkel-ID og bruker-IDer. Nøklene i seg selv kan IKKE bli lest.
-
-
- Krypter
- Dekrypter
- Importer Offentlige Nøkler
- Importer Private Nøkler
- Søk I Offentlige Nøkler
- Søk I Private Nøkler
- Filter: \"%s\"
-
-
- rask
- treg
- veldig treg
-
-
-
- Installer K-9 Mail for den beste integrasjonen, det støtter APG for PGP/INLINE og lar deg kryptere/dekryptere epost direkte.
-\n\nDet er anbefalt at du installerer OI File Manager eller ASTRO for å kunne velge filer igjennom APG, med manuelt søk.
-\n\nFørst trenger du noen nøkler. Du kan enten importere dem via menyene i \"Håndter Offentlige Nøkler\" og \"Håndter Private Nøkler\" eller lage dem selv i \"Håndter Private Nøkler\".
-\n\nDu kan også legge til en GMail-konto i hovedmenyen via \"Legg Til Konto\", noe som forenkler dekryptering av epost mottatt der.
-\n\nSjekk menyene under de forskjellige aktivitetene for å finne flere funksjoner.
-
-
\ No newline at end of file
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
deleted file mode 100644
index 76e0cb244..000000000
--- a/res/values-pt/strings.xml
+++ /dev/null
@@ -1,312 +0,0 @@
-
-
-
-
-
-
- Caixa de Entrada
- Gerir Chaves Públicas
- Gerir Chaves Privadas
- Escolher Destinatários
- Escolher Assinatura
- Cifrar
- Decifrar
- Senha
- Criar Chave
- Editar Chave
- Preferências
- Preferências de Servidor de Chaves
- Alterar Senha
- Definir Senha
- "Enviar Email..."
- Cifrar Para Arquivo
- Descifrar Para Arquivo
- Adicionar Conta
- Importar Chaves
- Exportar Chave
- Exportar Chaves
- Chave Não Encontrada
- Conhecendo
- Busca no Servidor de Chaves
- Assinatura Desconhecida
-
-
- IDs de Usuários
- Chaves
- Geral
- Padrões
- Avançado
-
-
- Assinar Para Clipboard
- Cifrar Para Clipboard
- Cifrar Para Email
- Assinar Para Email
- Cifrar
- Assinar
- Decifrar
- Verificar
- Escolher Destinatários
- Responder
- Cifrar Mensagem
- Decifrar Mesagem
- Cifrar Arquivo
- Decifrar Arquivo
- Salvar
- Cancelar
- Apagar
- Nenhuma
- Limpar Filtro
- Alterar Senha
- Definir Senha
- Buscar
-
-
- Sobre
- Adicionar Conta GMail
- Apagar Conta
- Gerir Chaves Públicas
- Gerir Chaves Privadas
- Configurações
- Importar Chaves
- Exportar Chaves
- Exportar Chave
- Apagar Chave
- Criar Chave
- Editar Chave
- Buscar
- Ajuda
- Servidor de Chaves
- Atualizar
-
-
- Assinar
- Mensagem
- Arquivo
- Senha
- Novamente
- Algoritmo
- Armadura ASCII
- Chave(s) Pública(s)
- Apagar Após Cifrar
- Apagar Após Decifrar
- Apagar Após Importar
- Algoritmo de Cifragem
- Algoritmo de Hash
- Chave Pública
- Senha
- Cache de Senhas
- Compressão de Mensagem
- Compressão de Arquivo
- Língua
- Forçar Assinaturas V3
- Servidores de Chave
- ID da Chave
- Criação
- Expiração
- Uso
- Tamanho da Chave
- ID do Usuário Principal
- Nome
- Comentário
- Email
- Selecionar
- 1 Selecionada
- Selecionada
- <desconhecido>
- <ninguém>
- <sem chave>
- -
- <não expira>
-
- pode cifrar
- pode assinar
- expirada
- não válida
- %s servidor(es) de chave(s)
- fingerprint
-
-
- Nenhum
- Assinar apenas
- Cifrar apenas
- Assinar e Cifrar
- 15 segs
- 1 min
- 3 mins
- 5 mins
- 10 mins
- 20 mins
- 40 mins
- 1 hora
- 2 horas
- 4 horas
- 8 horas
- até sair
- Padrões do sistema
- DSA
- ElGamal
- RSA
- Abrir...
- Salvar Como...
- Selecionar Arquivo para Cifrar...
- Selecionar Arquivo para Decifrar...
- Abrir
- Salvar
- Advertência
- Erro
- Advertência: %s
- Erro: %s
-
-
- Chave errada.
- Usando conteúdo do clipboard.
- Chave salva.
- Defina uma senha antes.
- Não há gerenciador de arquivos compatível instalado.
- A senha não confere.
- Não são permitidas senhas vazias.
- Cifragem simétrica.
- %s
- Tem certeza que deseja apagar\n%s?
- Apagado com sucesso.
- Selecione um arquivo primeiro.
- Decifrado com sucesso.
- Cifrado com sucesso.
- Cifrado para clipboard com sucesso.
- Insira a senha duas vezes.
- Selecione pelo menos uma chave de cifragem.
- Selecione pelo menos uma chave de cifragem ou uma chave de assinatura.
- Especifique o arquivo a cifrar sobre.\nATENÇÃO! O arquivo, se existir, será sobrescrito!
- Especifique o arquivo a decifrar sobre.\nATENÇÃO! O arquivo, se existir, será sobrescrito!
- Especifique a conta de Email do Google que deseja adicionar.
- Especifique o arquivo para importação de chaves. (.asc or .gpg)
- Especifique o arquivo de exportação.\nATENÇÃO! O arquivo, se existir, será sobrescrito!
- Especifique o arquivo de exportação.\nATENÇÃO! Você está exportando suas chaves PRIVADAS (SECRETAS).\nATENÇÃO! O arquivo, se existir, será sobrescrito.
- Você realmente deseja apagar a chave \'%s\'?\nEssa ação não pode ser desfeita!
- Você realmente deseja apagar a chave PRIVADA \'%s\'?\nEssa ação não pode ser desfeita!
- Adicionada(s) com sucesso %1$s chave(s) e atualizada(s) %2$s chave(s).
- Adicionada(s) %s chave(s) com sucesso.
- Atualizada(s) %s chaves(s) com sucesso.
- Nenhuma chave adicionada ou atualizada.
- 1 chave exportada com sucesso.
- %s chaves exportadas com sucesso.
- Nenhuma chave exportada.
- Nota: apenas as sub-chaves suportam ElGamal, e para ElGamal se usuára o tamanho mais próximo a 1536, 2048, 3072, 4096, ou 8192.
- Não se pode encontrar a chave %08X.
- %s chave(s) encontrada(s).
- Assinatura desconhecida, toque para buscar a chave.
- A edição de chaves ainda está na versão beta.
- %s chave(s) secreta(s) ignoradas. Provavelmente foram exportadas com a opção\n --export-secret-subkeys\nCertifique-se de exporta com a opção\n --export-secret-keys
- Chave %s desconhecida, você gostaria de procurá-la em um servidor de chaves?
-
-
- erro ao apagar \'%s\'
- arquivo não encontrado
- não foi encontrado uma chave secreta adequada
- não foi encontrado tipo de cifragem conhecido
- armazenamento externo não disponível
- conta \'%s\' não encontrada
- sem permissões para ler a conta
- falha ao adicionar a conta \'%s\'
- email inválido \'%s\'
- o tamanho da chave deve ser pelo menos 512bit
- a chave primária não pode ser do tipo ElGamal
- algoritmo selecionado desconhecido
- é necessário informar um nome
- é necessário informar um endereço de email
- é necessário pelo menos um id de usuário
- id de usuário principal não pode ser vazio
- é necessário pelo menos uma chave primária
- data de expiração deve ser após a data de criação
- não foram inseridos chave(s) de cifragem ou senha
- falha na assinatura
- senha não inserida
- chave para assinatura não inserida
- dados a serem cifrados inválidos
- dados corrompidos
- não foi encontrado um pacote com criptografia simétrica
- senha inválida
- erro ao salvar chave(s)
- não foi possível extrair chave privada
-
-
- pronto.
- inicializando...
- salvando...
- importando...
- exportando...
- criando chave, isso pode demorar um pouco...
- criando chave...
- preparando chave primária...
- certificando chave primária...
- criando anel de chave primária...
- adicionando sub-chaves...
- salvando anel de chaves...
- importando chaves secretas...
- importando chaves públicas...
- recarregando chaves...
- exportando chave...
- exportando chaves...
- extraindo chave de assinatura...
- extraindo chave...
- preparando fluxos...
- cifrando dados...
- decifrando dados...
- preparando assinatura...
- gerando assinatura...
- processando assinatura...
- verificando assinatura...
- assinando...
- carregando dados...
- buscando chave...
- descompactando dados...
- verificando integridade...
- apagando \'%s\' de forma segura...
- buscando...
- buscando %s...
-
-
- Ler detalhes de chaves do APG.
- Ler detalhes de chaves públicas e privadas salvas no APG, tais como ID da chave e ID do usuário. As chaves NÃO podem ser lidas.
-
-
- Cifrar
- Decifrar
- Importar Chaves Públicas
- Importar Chaves Privadas
- Buscar Chaves Públicas
- Buscar Chaves Privadas
- Filter: \"%s\"
-
-
- rápido
- lento
- muito lento
-
-
-
- Instale o leitor de emails K-9 para uma melhor integração. Ele suporta o APG para PGP/INLINE e permite cifragem e decifragem de emails diretamente.
-\n\nÉ recomendado o uso dos Gerenciadores de Arquivos OI ou ASTRO para ser possível a utilização do botão de buscar arquivos no APG.
-\n\nSão necessárias chaves para o APG. Importe-as pela opção do menu \"Gerir Chaves Públicas\" ou \"Gerir Chaves Privadas\" ou crie-as no menu \"Gerir Chaves Privadas\".
-\n\nVocê também pode adicionar uma conta GMail pela opção \"Adicionar Conta\", isso simplifica a decifragem de emails.
-\n\nVerifique as opções de menus para conhecer as funcionalides disponíveis.
-
-
\ No newline at end of file
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
deleted file mode 100644
index 576d6e56b..000000000
--- a/res/values-sl/strings.xml
+++ /dev/null
@@ -1,304 +0,0 @@
-
-
-
-
-
-
- Poštni nabiralnik
- Upravljanje javnih ključev
- Upravljanje zasebnih ključev
- Izberi prejemnike
- Izberi podpis
- Šifriraj
- Dešifriraj
- Geslo
- Ustvari ključ
- Uredi ključ
- Nastavitve
- Nastavitve strežnikov s ključi
- Spremeni geslo
- Določi geslo
- "Pošlji e-pošto..."
- Šifriraj v datoteko
- Dešifriraj v datoteko
- Dodaj račun
- Uvozi ključe
- Izvozi ključ
- Izvozi ključe
- Ključ ni bil najden
- Kako začeti
- Iskanje po strežnikih s ključi
-
-
- Uporabniške identitete
- Ključi
- Splošno
- Privzete nastavitve
- Napredno
-
-
- Podpiši in kopiraj v odložišče
- Šifriraj in kopiraj v odložišče
- Šifriraj in pošlji
- Podpiši in pošlji
- Šifriraj
- Podpiši
- Dešifriraj
- Overi
- Izberi prejemnike
- Odgovori
- Šifriraj sporočilo
- Dešifriraj sporočilo
- Šifriraj datoteko
- Dešifriraj datoteko
- Shrani
- Prekliči
- Izbriši
- Brez
- Umakni filtriranje
- Spremeni geslo
- Določi geslo
- Išči
-
-
- O programu
- Dodaj GMail račun
- Izbriši račun
- Upravljanje javnih ključev
- Upravljanje zasebnih ključev
- Nastavitve
- Uvozi ključe
- Izvozi ključe
- Izvozi ključ
- Izbriši ključ
- Ustvari ključ
- Uredi ključ
- Išči
- Pomoč
- Strežnik s ključi
- Posodobi
-
-
- Podpiši
- Sporočilo
- Datoteka
- Geslo
- Ponovi
- Algoritem
- ASCII Armour
- Javni ključ(i)
- Po šifriranju izbriši
- Po dešifriranju izbriši
- Po uvozu izbriši
- Šifrirni algoritem
- Hash algoritem
- Javni ključ
- Geslo
- Predpomnilnik gesel
- Zgoščevanje sporočil
- Zgoščevanje datotek
- Jezik
- Vsili podpis V3
- Strežniki s ključi
- Identiteta ključa
- Izdelava
- Pretek
- Uporaba
- Velikost ključa
- Glavna uporabniška identiteta
- Ime
- Komentar
- E-pošta
- Izberi
- 1 izbran
- Izbrani
- <nepoznan>
- <brez>
- <brez ključa>
- -
- <nikoli>
-
- lahko šifrira
- lahko podpiše
- potečeno
- neveljavno
- %s strežnik(i) s ključi
-
-
- Brez
- Samo podpis
- Samo šifriranje
- Podpis in šifriranje
- 15 sek
- 1 min
- 3 min
- 5 min
- 10 min
- 20 min
- 40 min
- do izhoda
- Sistemsko nastavljeno
- DSA
- ElGamal
- RSA
- Odpri...
- Shrani kot...
- Izberi datoteko za šifriranje...
- Izberi datoteko za dešifriranje...
- Odpri
- Shrani
- Opozorilo
- Napaka
- Opozorilo: %s
- Napaka: %s
-
-
- Napačno geslo.
- Uporabljam vsebino odložišča.
- Ključ shranjen.
- Najprej določite geslo.
- Nameščen ni noben združljiv upravitelj datotek.
- Gesli se ne ujemata.
- Prazna gesla niso dovoljena.
- Simetrična enkripcija.
- %s
- Ali ste prepričani, da želite izbrisati\n%s?
- Uspešno izbrisano.
- Najprej izberite datoteko.
- Uspešno dešifrirano.
- Uspešno šifrirano.
- Uspešno šifrirano v odložišče.
- Vstavite geslo dvakrat.
- Izberite vsaj en šifrirni ključ.
- Izberite vsaj en šifrirni ključ ali ključ za podpis.
- Določite datoteko v katero želite šifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
- Določite datoteko v katero želite dešifrirati.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
- Določite Google Mail račun, ki ga želite dodati.
- Določite iz katere datoteke želite uvoziti ključe. (.asc ali .gpg)
- Določite v katero datoteko želite izvoziti.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
- Določite v katero datoteko želite izvoziti.\nPOZOR! Izvozili boste ZASEBNI ključ.\nPOZOR! Če ta datoteka že obstaja, bo prepisana.
- Ali zares želite izbrisati ključ \'%s\'?\nTega ne boste mogli popraviti!
- Ali zares želite izbrisati ZASEBNI ključ \'%s\'?\nTega ne boste mogli popraviti!
- Uspešno dodani ključi: %1$s. Uspešno posodobljeni ključi: %2$s."
- Uspešno dodani ključi: %s.
- Uspešno posodobljeni ključi: %s.
- Noben ključ ni bil dodan ali posodobljen.
- Uspešno izvožen 1 ključ.
- Uspešno izvoženi ključi: %s
- Noben ključ ni bil izvožen.
- Opomba: le podključi podpirajo ElGamal. Za ElGamal bo uporabljena velikost najbližja 1536, 2048, 3072, 4096, ali 8192.
- Ne najdem ključa %08X.
- Najdeni ključi: %s
- Neznan podpis, za ogled pritisni.
- Urejanje ključev je še vedno v precej testni fazi.
-
-
- izbris \'%s\' ni uspel
- ne najdem datoteke
- najden ni bil noben ustrezen zasebni kluč
- najdena ni bila nobena poznana vrsta enkripcije
- zunanji pomnilnik ni pripravljen
- račun \'%s\' ni najden
- branje računa ni dovoljeno
- dodajanje računa \'%s\' ni uspelo
- neveljaven e-naslov \'%s\'
- velikost ključa mora biti vsaj 512bit
- statični ključ ne more biti ključ ElGamal
- neznana izbira algoritma
- določiti morate ime
- določiti morate naslov e-pošte
- potrebujem vsaj eno uporabniško identiteto
- glavna uporabniška identiteta ne more biti prazna
- potrebujem vsaj statični ključ
- datum poteka mora biti kasnejši od datuma nastanka
- dan ni bil noben šifrirni ključ ali geslo
- podpis ni bil uspešen
- dano ni bilo nobeno geslo
- dan ni bil noben podpisni ključ
- neveljavni šifrirni podatki
- pokvarjeni podatki
- ne najdem podatkov s simetrično enkripcijo
- napačno geslo
- napaka pri shranjevanju nakaterih ključev
-
-
- končano.
- inicializiram...
- shranjujem...
- uvažam...
- izvažam...
- generiram ključ, to lahko traja nekaj časa...
- gradim ključ...
- pripravljam statični ključ...
- potrjujem statični ključ...
- gradim datoteko s statičnimi ključi...
- dodajam podključe...
- shranjujem datoteko s ključi...
- uvažam zasebne ključe...
- uvažam javne ključe...
- reloading keys...
- izvažam ključ...
- izvažam ključe...
- izvlačim podpisni kluč...
- izvlačim ključ...
- pripravljam tok...
- šifriram podatke...
- dešifriram podatke...
- pripravljam podpis...
- generiram podpis...
- obdelujem podpis...
- overovljam podpis...
- podpisujem...
- berem podatke...
- iščem ključ...
- raztezam podatke...
- overovljam integriteto...
- varno brišem \'%s\'...
- poizvedujem na %s...
-
-
- Preverite podrobnosti ključev v APG.
- Preverite podrobnosti javnih in zasebnih ključev shranjenih v APG, kot so identiteta ključev in identiteta uporabnikov. Samih ključev NI MOGOČE preveriti.
-
-
- Šifriraj
- Dešifriraj
- Uvozi javne ključe
- Uvozi zasebne ključe
- Poišči javne ključe
- Poišči zasebne ključe
- Filter: \"%s\"
-
-
- hitro
- počasi
- zelo počasi
-
-
-
- Za boljšo integracijo namestite program K-9 Mail, ki omogoča način PGP/INLINE in neposredno šifriranje/dešifriranje e-pošte.
- \n\nZaželjeno je, da namestite programa OI File Manager ali ASTRO, ki omogočata iskanje, izbiro in vnos datotek v APG.
- \n\nZa začetek potrebujete nekaj ključev. Lahko jih uvozite s klikom na menija \"Upravljanje javnih ključev\" in \"Upravljanje zasebnih ključev\" ali jih ustvarite v meniju \"Upravljanje zasebnih ključev\".
- \n\nPreko menija \"Dodaj račun\" lahko dodate vaše GMail račune in tako poenostavite dešifriranje e-pošte prejete nanje.
- \n\nDa bi odkrili dodatne funkcije in zmožnosti programa APG, se sprehodite skozi njegove menije.
-
-
\ No newline at end of file
diff --git a/res/values-zh/strings.xml b/res/values-zh/strings.xml
deleted file mode 100644
index 8fa3d23fc..000000000
--- a/res/values-zh/strings.xml
+++ /dev/null
@@ -1,309 +0,0 @@
-
-
-
-
-
-
- 收件箱
- 管理公钥
- 管理私钥
- 选择接收人
- 选择签名
- 加密
- 解密
- 密码口令
- 创建密钥
- 编辑密钥
- 设置
- 密钥服务器设置
- 更改密码口令
- 设置密码口令
- \"发送邮件...\"
- 加密至文件
- 解密至文件
- 添加账户
- 导入密钥
- 导出密钥
- 导出密钥
- 密钥未发现
- 准备开始
- 查询密钥服务器
- 未知签名密钥
-
-
- 用户标识
- 密钥
- 常规
- 默认
- 高级
-
-
- 签名至剪贴板
- 加密至剪贴板
- 加密并发送邮件
- 签名并发送邮件
- 加密
- 签名
- 解密
- 验证
- 选择接收人
- 回复
- 加密信息
- 解密信息
- 加密文件
- 解密文件
- 保存
- 取消
- 删除
- 无
- 清空过滤器
- 更改密码口令
- 设置密码口令
- 搜索
-
-
- 关于
- 添加GMail账户
- 删除账户
- 管理公钥
- 管理私钥
- 设置
- 导入密钥
- 导出密钥
- 导出密钥
- 删除密钥
- 创建密钥
- 编辑密钥
- 搜索
- 帮助
- 密钥服务器
- 更新密钥
-
-
- 签名
- 信息
- 文件
- 密码口令
- 确认口令
- 算法
- 二进制转文本编码
- 选择公钥
- 加密后删除
- 解密后删除
- 导入后删除
- 加密算法
- 哈希算法
- 非对称算法
- 对称算法
- 口令缓存
- 信息压缩
- 文件压缩
- 语言
- 强制V3签名
- 密钥服务器
- 密钥标识
- 创建日期
- 期限
- 用途
- 密钥尺寸
- 主用户标识
- 姓名
- 注释
- 电子邮件
- 选择
- 1个选定
- 个选定
- <未知>
- <无>
- <无密钥>
- -
- <不过期>
-
- 可以加密
- 可以签名
- 已过期
- 不合法
- %s 个密钥服务器
-
-
- 无
- 仅签名
- 仅加密
- 签名且加密
- 15 秒
- 1 分钟
- 3 分钟
- 5 分钟
- 10 分钟
- 20 分钟
- 40 分钟
- 直到退出
- 系统默认
- DSA
- ElGamal
- RSA
- 打开...
- 另存为...
- 选择要加密的文件...
- 选择要解密的文件...
- 打开
- 保存
- 警告
- 错误
- 警告: %s
- 错误: %s
-
-
- 密码口令错误.
- 使用剪贴板内容.
- 密钥已保存.
- 请先设置密码口令.
- 没有兼容的文件管理器被安装.
- 密码口令不匹配.
- 不允许空的密码口令.
- 对称加密.
- %s
- 确定要删除\n%s?
- 删除成功.
- 请先选择文件.
- 解密成功.
- 加密成功.
- 成功加密至剪贴板.
- 输入密码口令两次.
- 至少选择一个加密密钥.
- 至少选择一个加密密钥或一个签名密钥.
- 请指定加密生成的文件.\n警告! 文件如存在会被覆盖.
- 请指定解密生成的文件.\n警告! 文件如存在会被覆盖.
- 指定你想添加的Google Mail帐号.
- 请指定导入密钥的文件. (.asc or .gpg)
- 请指定导出文件.\n警告! 文件如存在会被覆盖.
- 请指定导出文件.\n警告! 你正准备导出私钥.\n警告! 文件如存在会被覆盖.
- 确认需要删除 \'%s\'?\n操作无法被恢复!
- 确认需要删除私钥 \'%s\'?\n操作无法被恢复!
- 成功添加 %1$s 个密钥和更新 %2$s 个密钥.
- 成功添加 %s 个密钥.
- 成功更新 %s 个密钥.
- 无密钥被添加或更新.
- 成功导出 1 个密钥.
- 成功导出 %s 个密钥.
- 无密钥导出.
- 注意: 只有子密钥支持ElGamal, 最接近的密钥大小如1536, 2048, 3072, 4096, 8192会被其使用.
- 无法找到密钥 %08X.
- 发现 %s 个密钥.
- 未知签名, 请查找密钥.
- 密钥编辑仍处于测试阶段.
- 忽略 %s 个错误的密钥. 或许导出时你使用了\n --export-secret-subkeys\n请确保使用\n --export-secret-keys\n代替前者.
- 未知密钥 %s, 是否需要在密钥服务器上查找?
-
-
- 删除 \'%s\' 失败
- 文件没有找到
- 未发现合适的私钥
- 未知加密方式
- 外部存储设备未准备就绪
- 账户 \'%s\' 未发现
- 无读取账户权限
- 添加账户 \'%s\' 失败
- 无效电子邮件 \'%s\'
- 文件尺寸至少需512比特
- 主密钥不可以是ElGamal
- 选择了未知算法
- 需指定姓名
- 需指定电子邮件
- 至少需要一个用户标识
- 主用户标识不能为空
- 至少需要选择一个主密钥
- 期限日期必需在创建日期之后
- 无加密密钥或密码口令被提供
- 签名失败
- 无密码口令被提供
- 无签名密钥被提供
- 加密数据不合法
- 数据损坏
- 未发现对称加密包
- 密码口令错误
- 保存密钥出错
- 无法提取私钥
-
-
- 完成.
- 初始化中...
- 保存中...
- 导入中...
- 导出中...
- 正在创建密钥, 请等待...
- 生成密钥中...
- 正在准备主密钥...
- 正在认证主密钥...
- 正在生成主密钥圈...
- 正在添加子密钥...
- 正在保存密钥圈...
- 导入私钥中...
- 导入公钥中...
- 重新加载密钥...
- 导出密钥中...
- 导出密钥中...
- 提取签名密钥中...
- 提取密钥中...
- 准备数据流...
- 加密数据中...
- 解密数据中...
- 正在准备签名...
- 正在生成签名...
- 处理签名中...
- 验证签名中...
- 正在签名...
- 读取数据中...
- 正在搜索密钥...
- 解压数据中...
- 正在验证集成...
- 正在安全删除 \'%s\' ...
- 正在查询...
- 正在查询 %s...
-
-
- 从APG读取密钥信息.
- 读取保存在APG中的公钥和私钥信息, 比如密钥标识和用户标识. 这些密钥本身无法被读取.
-
-
- 加密
- 解密
- 导入公钥
- 导入私钥
- 搜索公钥
- 搜索私钥
- 过滤信息: \"%s\"
-
-
- 快
- 慢
- 非常慢
-
-
-
- 装 K-9 Mail 以便达到最好的集成, K9支持APG处理PGP/INLINE并让你直接加密或解密邮件.
-\n\n建议安装 OI File Manager 或 ASTRO 以便在APG中浏览文件夹.
-\n\n首先你需要一些密钥. 可以通过菜单 \"管理公钥\" 和 \"管理私钥\" 来导入, 或者在菜单 \"管理私钥\" 中创建.
-\n\n你也可以通过菜单 \"添加账户\" 来添加GMail账户, 这样可以简化解密那些账户中邮件的过程.
-\n\n可以通过查看选项菜单寻找更多的功能.
-
-
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
deleted file mode 100644
index 25a4e66df..000000000
--- a/res/values/arrays.xml
+++ /dev/null
@@ -1,214 +0,0 @@
-
-
-
-
-
-
- @string/choice_15secs
- @string/choice_1min
- @string/choice_3mins
- @string/choice_5mins
- @string/choice_10mins
- @string/choice_20mins
- @string/choice_40mins
- @string/choice_1hour
- @string/choice_2hours
- @string/choice_4hours
- @string/choice_8hours
-
-
-
- 15
- 60
- 180
- 300
- 600
- 1200
- 2400
- 3600
- 7200
- 14400
- 28800
-
-
-
- @string/choice_language_system
- Afrikaans
- Bahasa indonesia
- Bahasa melayu
- Bamanankan
- Català
- Cymraeg
- Dansk
- Deutsch
- Dholuo
- Eesti
- English
- Español
- Fulfulde, Pulaar, Pular
- Gaeilge
- Galego
- Hausa
- Hrvatski
- Kinyarwanda
- Kirundi
- Kiswahili
- Latviešu
- Lietuviškai
- Magyar
- Malti
- Nederlands
- Norsk bokmål
- Pyccĸий
- Română
- Slovenčina
- Slovenščina
- Somali
- Српски
- Tiếng Việt
- Tϋrkçe
- Wolof
- Yorùbá
- Azərbaycan
- Euskera
- Français
- isiXhosa
- isiZulu
- Italiano
- O\'zbek
- Polski
- Português
- Shqip
- Suomi
- Svenska
- Íslenska
- Čeština
- Ɛʋɛ
- Ελληνικά
- Беларуская
- Български
- Кыргыз
- Македонски
- Українська
- аҧсуа бызшәа
- Қазақ
- Հայերեն
- עברית
- اردو
- العربية
- فارسی
- پښتو
- हिंदी
- తెలుగు
- ಕನ್ನಡ
- ภาษาไทย
- አማርኛ
- 中文
- 日本語
- 한국어
-
-
-
-
- af
- id
- ms
- bm
- ca
- cy
- da
- de
- luo
- et
- en
- es
- ff
- ga
- gl
- ha
- hr
- rw
- rn
- sw
- lv
- lt
- hu
- mt
- nl
- no
- ru
- ro
- sk
- sl
- so
- sr
- vi
- tr
- wo
- yo
- az
- eu
- fr
- xs
- zu
- it
- uz
- pl
- pt
- sq
- fi
- sv
- is
- cs
- ee
- el
- be
- bg
- ky
- mk
- uk
- ab
- kk
- hy
- he
- ur
- ar
- fa
- ps
- hi
- te
- kn
- th
- am
- zh
- ja
- ko
-
-
-
-
- da
- de
- en
- es
- it
- no
- pt
- sl
- zh
-
-
-
diff --git a/res/values/static_strings.xml b/res/values/static_strings.xml
deleted file mode 100644
index c8a6a5026..000000000
--- a/res/values/static_strings.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- APG+
- https://github.com/dschuermann/apg
-
-
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
deleted file mode 100644
index 4cfc99df8..000000000
--- a/res/values/strings.xml
+++ /dev/null
@@ -1,333 +0,0 @@
-
-
-
-
-
-
- Mail Inbox
- Manage Public Keys
- Manage Secret Keys
- Select Recipients
- Select Signature
- Encrypt
- Decrypt
- Pass Phrase
- Create Key
- Edit Key
- Preferences
- Key Server Preference
- Change Pass Phrase
- Set Pass Phrase
- "Send Mail..."
- Encrypt To File
- Decrypt To File
- Add Account
- Import Keys
- Export Key
- Export Keys
- Key Not Found
- Getting Started
- Query Key Server
- Export to Key Server
- Unknown Signature Key
- Import from QR Code
- Sign Key
- About
-
-
- User IDs
- Keys
- General
- Defaults
- Advanced
-
-
- Sign To Clipboard
- Encrypt To Clipboard
- Encrypt And Email
- Sign And Email
- Encrypt
- Sign
- Decrypt
- Verify
- Select Recipients
- Reply
- Encrypt Message
- Decrypt Message
- Encrypt File
- Decrypt File
- Save
- Cancel
- Delete
- None
- Clear Filter
- Change Pass Phrase
- Set Pass Phrase
- Search
- Export To Server
-
-
- About
- Add GMail Account
- Delete Account
- Manage Public Keys
- Manage Secret Keys
- Settings
- Import Keys
- Export Keys
- Export Key
- Delete Key
- Create Key
- Edit Key
- Search
- Help
- Key Server
- Update
- Export To Server
- Share
- Scan QR Code
- Sign Key
-
-
- Sign
- Message
- File
- Pass Phrase
- Again
- Algorithm
- ASCII Armour
- Public Key(s)
- Delete After Encryption
- Delete After Decryption
- Delete After Import
- Encryption Algorithm
- Hash Algorithm
- Public Key
- Pass Phrase
- Pass Phrase Cache
- Message Compression
- File Compression
- Language
- Force V3 Signatures
- Key Servers
- Key ID
- Creation
- Expiry
- Usage
- Key Size
- Main User ID
- Name
- Comment
- Email
- Send Key to Server?
- Select
- 1 Selected
- Selected
- <unknown>
- <none>
- <no key>
- -
- <no expiry>
-
- can encrypt
- can sign
- expired
- not valid
- %s key server(s)
- fingerprint
-
-
- None
- Sign only
- Encrypt only
- Sign and Encrypt
- 15 secs
- 1 min
- 3 mins
- 5 mins
- 10 mins
- 20 mins
- 40 mins
- 1 hour
- 2 hours
- 4 hours
- 8 hours
- until quit
- System default
- DSA
- ElGamal
- RSA
- Open...
- Save As...
- Select File To Encrypt...
- Select File To Decrypt...
- Open
- Save
- Warning
- Error
- Warning: %s
- Error: %s
-
-
- Wrong pass phrase.
- Using clipboard content.
- Key saved.
- Set a pass phrase first.
- No compatible file manager installed.
- The pass phrases didn\'t match.
- Empty pass phrases are not allowed.
- Symmetric encryption.
- %s
- Are you sure you want to delete\n%s?
- Successfully deleted.
- Select a file first.
- Successfully decrypted.
- Successfully encrypted.
- Successfully encrypted to clipboard.
- Enter the pass phrase twice.
- Select at least one encryption key.
- Select at least one encryption key or a signature key.
- Please specify which file to encrypt to.\nWARNING! File will be overwritten if it exists.
- Please specify which file to decrypt to.\nWARNING! File will be overwritten if it exists.
- Specify the Google Mail account you want to add.
- Please specify which file to import keys from. (.asc or .gpg)
- Please specify which file to export to.\nWARNING! File will be overwritten if it exists.
- Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.
- Do you really want to delete the key \'%s\'?\nYou can\'t undo this!
- Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!
- Successfully added %1$s key(s) and updated %2$s key(s).
- Successfully added %s key(s).
- Successfully updated %s key(s).
- No keys added or updated.
- Successfully exported 1 key.
- Successfully exported %s keys.
- No keys exported.
- Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.
- Couldn\'t find key %08X.
- Found %s key(s).
- Unknown signature, touch to look up key.
- Key editing is still kind of beta.
- %s bad secret key(s) ignored. Perhaps you exported with the option\n --export-secret-subkeys\nMake sure you export with\n --export-secret-keys\ninstead.
- Unknown key %s, do you want to try finding it on a keyserver?
- Successfully sent key to server
- Successfully signed key
- Successfully validated and imported key
-
-
- deleting \'%s\' failed
- file not found
- no suitable secret key found
- no known kind of encryption found
- external storage not ready
- account \'%s\' not found
- no permission to read the account
- adding account \'%s\' failed
- invalid email \'%s\'
- key size must be at least 512bit
- the master key cannot be an ElGamal key
- unknown algorithm choice
- you need to specify a name
- you need to specify an email address
- need at least one user id
- main user id must not be empty
- need at least a master key
- expiry date must come after creation date
- no encryption key(s) or pass phrase given
- signature failed
- no pass phrase given
- no signature key given
- not valid encryption data
- corrupt data
- couldn\'t find a packet with symmetric encryption
- wrong pass phrase
- error saving some key(s)
- could not extract private key
-
-
- done.
- initializing...
- saving...
- importing...
- exporting...
- generating key, this can take a while...
- building key...
- preparing master key...
- certifying master key...
- building master key ring...
- adding sub keys...
- saving key ring...
- importing secret keys...
- importing public keys...
- reloading keys...
- exporting key...
- exporting keys...
- extracting signature key...
- extracting key...
- preparing streams...
- encrypting data...
- decrypting data...
- preparing signature...
- generating signature...
- processing signature...
- verifying signature...
- signing...
- reading data...
- finding key...
- decompressing data...
- verifying integrity...
- deleting \'%s\' securely...
- querying...
- querying %s...
-
-
- Read key details from APG.
- Read key details of public and secret keys stored in APG, such as key ID and user IDs. The keys themselves can NOT be read.
- Store blobs to en/decrypt with APG.
- Store and read files on the android file system through APG. It cannot read files of other applications.
-
-
- Encrypt
- Decrypt
- Import Public Keys
- Import Secret Keys
- Search Public Keys
- Search Secret Keys
- Filter: \"%s\"
-
-
- fast
- slow
- very slow
-
-
- Android Privacy Guard (APG) is a OpenPGP implementation for Android.
- License: Apache License 2.0
- Version:
- Developer: Thialfihar (Main developer), Senecaso (QRCode, send key, sign key), Markus Doits (AIDL), Oliver Runge, Dominik Schürmann
-
-
-
- Install K-9 Mail for the best integration, it supports APG for PGP/INLINE and lets you directly encrypt/decrypt emails.
-\n\nIt is recommended that you install OI File Manager or ASTRO to be able to use the browse button for file selection in APG.
-\n\nFirst you need some keys. Either import them via the option menus in \"Manage Public Keys\" and \"Manage Secret Keys\" or create them in \"Manage Secret Keys\".
-\n\nYou can also add a GMail account in the main activity via \"Add Account\", which simplifies decrypting emails received there.
-\n\nCheck out the option menus in the various activities to find more functions.
-
-
\ No newline at end of file
diff --git a/res/values/styles.xml b/res/values/styles.xml
deleted file mode 100644
index 2a2a69341..000000000
--- a/res/values/styles.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/xml/apg_preferences.xml b/res/xml/apg_preferences.xml
deleted file mode 100644
index 7c0dd2206..000000000
--- a/res/xml/apg_preferences.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/xml/searchable_public_keys.xml b/res/xml/searchable_public_keys.xml
deleted file mode 100644
index e9602b121..000000000
--- a/res/xml/searchable_public_keys.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/res/xml/searchable_secret_keys.xml b/res/xml/searchable_secret_keys.xml
deleted file mode 100644
index a7e8873d6..000000000
--- a/res/xml/searchable_secret_keys.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/org/apg/Apg.java b/src/org/apg/Apg.java
deleted file mode 100644
index 2bfdb31e6..000000000
--- a/src/org/apg/Apg.java
+++ /dev/null
@@ -1,2283 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg;
-
-import org.apg.KeyServer.AddKeyException;
-import org.apg.provider.DataProvider;
-import org.apg.provider.Database;
-import org.apg.provider.KeyRings;
-import org.apg.provider.Keys;
-import org.apg.provider.UserIds;
-import org.apg.ui.BaseActivity;
-import org.apg.ui.widget.KeyEditor;
-import org.apg.ui.widget.SectionView;
-import org.apg.ui.widget.UserIdEditor;
-import org.apg.util.IterableIterator;
-import org.spongycastle.bcpg.ArmoredInputStream;
-import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.BCPGOutputStream;
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
-import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
-import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-import org.spongycastle.jce.spec.ElGamalParameterSpec;
-import org.spongycastle.openpgp.PGPCompressedData;
-import org.spongycastle.openpgp.PGPCompressedDataGenerator;
-import org.spongycastle.openpgp.PGPEncryptedData;
-import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
-import org.spongycastle.openpgp.PGPEncryptedDataList;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPKeyPair;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPKeyRingGenerator;
-import org.spongycastle.openpgp.PGPLiteralData;
-import org.spongycastle.openpgp.PGPLiteralDataGenerator;
-import org.spongycastle.openpgp.PGPObjectFactory;
-import org.spongycastle.openpgp.PGPOnePassSignature;
-import org.spongycastle.openpgp.PGPOnePassSignatureList;
-import org.spongycastle.openpgp.PGPPBEEncryptedData;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureList;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
-import org.spongycastle.openpgp.PGPUtil;
-import org.spongycastle.openpgp.PGPV3SignatureGenerator;
-import org.apg.R;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Message;
-import android.view.ViewGroup;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.SignatureException;
-import java.util.Calendar;
-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;
-
-public class Apg {
- private static final String mApgPackageName = "org.thialfihar.android.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 final String DECRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.DECRYPT_AND_RETURN";
- public static final String ENCRYPT_AND_RETURN = "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN";
- public static final String SELECT_PUBLIC_KEYS = "org.thialfihar.android.apg.intent.SELECT_PUBLIC_KEYS";
- public static final String SELECT_SECRET_KEY = "org.thialfihar.android.apg.intent.SELECT_SECRET_KEY";
- public static final String IMPORT = "org.thialfihar.android.apg.intent.IMPORT";
- public static final String LOOK_UP_KEY_ID = "org.thialfihar.android.apg.intent.LOOK_UP_KEY_ID";
- public static final String LOOK_UP_KEY_ID_AND_RETURN = "org.thialfihar.android.apg.intent.LOOK_UP_KEY_ID_AND_RETURN";
- public static final String GENERATE_SIGNATURE = "org.thialfihar.android.apg.intent.GENERATE_SIGNATURE";
- public static final String EXPORT_KEY_TO_SERVER = "org.thialfihar.android.apg.intent.EXPORT_KEY_TO_SERVER";
- public static final String IMPORT_FROM_QR_CODE = "org.thialfihar.android.apg.intent.IMPORT_FROM_QR_CODE";
- }
-
- public static final String EXTRA_TEXT = "text";
- public static final String EXTRA_DATA = "data";
- public static final String EXTRA_ERROR = "error";
- public static final String EXTRA_DECRYPTED_MESSAGE = "decryptedMessage";
- public static final String EXTRA_DECRYPTED_DATA = "decryptedData";
- public static final String EXTRA_ENCRYPTED_MESSAGE = "encryptedMessage";
- public static final String EXTRA_ENCRYPTED_DATA = "encryptedData";
- public static final String EXTRA_RESULT_URI = "resultUri";
- public static final String EXTRA_SIGNATURE = "signature";
- public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId";
- public static final String EXTRA_SIGNATURE_USER_ID = "signatureUserId";
- public static final String EXTRA_SIGNATURE_SUCCESS = "signatureSuccess";
- public static final String EXTRA_SIGNATURE_UNKNOWN = "signatureUnknown";
- public static final String EXTRA_SIGNATURE_DATA = "signatureData";
- public static final String EXTRA_SIGNATURE_TEXT = "signatureText";
- public static final String EXTRA_USER_ID = "userId";
- public static final String EXTRA_USER_IDS = "userIds";
- public static final String EXTRA_KEY_ID = "keyId";
- public static final String EXTRA_REPLY_TO = "replyTo";
- public static final String EXTRA_SEND_TO = "sendTo";
- public static final String EXTRA_SUBJECT = "subject";
- public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds";
- public static final String EXTRA_SELECTION = "selection";
- public static final String EXTRA_ASCII_ARMOUR = "asciiArmour";
- public static final String EXTRA_BINARY = "binary";
- public static final String EXTRA_KEY_SERVERS = "keyServers";
- public static final String EXTRA_EXPECTED_FINGERPRINT = "org.thialfihar.android.apg.EXPECTED_FINGERPRINT";
-
- public static final String AUTHORITY = DataProvider.AUTHORITY;
-
- public static final Uri CONTENT_URI_SECRET_KEY_RINGS = Uri.parse("content://" + AUTHORITY
- + "/key_rings/secret/");
- public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_KEY_ID = Uri.parse("content://"
- + AUTHORITY + "/key_rings/secret/key_id/");
- public static final Uri CONTENT_URI_SECRET_KEY_RING_BY_EMAILS = Uri.parse("content://"
- + AUTHORITY + "/key_rings/secret/emails/");
-
- public static final Uri CONTENT_URI_PUBLIC_KEY_RINGS = Uri.parse("content://" + AUTHORITY
- + "/key_rings/public/");
- public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_KEY_ID = Uri.parse("content://"
- + AUTHORITY + "/key_rings/public/key_id/");
- public static final Uri CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS = Uri.parse("content://"
- + AUTHORITY + "/key_rings/public/emails/");
-
- private static String VERSION = null;
-
- private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] {
- SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
- SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
- SymmetricKeyAlgorithmTags.TRIPLE_DES };
- private static final int[] PREFERRED_HASH_ALGORITHMS = new int[] { HashAlgorithmTags.SHA1,
- HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160 };
- private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[] {
- CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
- CompressionAlgorithmTags.ZIP };
-
- public static Pattern PGP_MESSAGE = Pattern.compile(
- ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
-
- public static Pattern PGP_SIGNED_MESSAGE = Pattern
- .compile(
- ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
- Pattern.DOTALL);
-
- public static Pattern PGP_PUBLIC_KEY = Pattern.compile(
- ".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
- Pattern.DOTALL);
-
- private static HashMap mPassPhraseCache = new HashMap();
- private static String mEditPassPhrase = null;
-
- private static Database mDatabase = null;
-
- public static class GeneralException extends Exception {
- static final long serialVersionUID = 0xf812773342L;
-
- public GeneralException(String message) {
- super(message);
- }
- }
-
- public static class NoAsymmetricEncryptionException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public NoAsymmetricEncryptionException() {
- super();
- }
- }
-
- public static void initialize(Context context) {
- if (mDatabase == null) {
- mDatabase = new Database(context);
- }
- }
-
- public static Database getDatabase() {
- return mDatabase;
- }
-
- public static void setEditPassPhrase(String passPhrase) {
- mEditPassPhrase = passPhrase;
- }
-
- public static String getEditPassPhrase() {
- return mEditPassPhrase;
- }
-
- public static void setCachedPassPhrase(long keyId, String passPhrase) {
- mPassPhraseCache.put(keyId, new CachedPassPhrase(new Date().getTime(), passPhrase));
- }
-
- public static String getCachedPassPhrase(long keyId) {
- long realId = keyId;
- if (realId != Id.key.symmetric) {
- PGPSecretKeyRing keyRing = getSecretKeyRing(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 int cleanUpCache(int ttl, int initialDelay) {
- int delay = initialDelay;
- long realTtl = ttl * 1000;
- long now = new Date().getTime();
- Vector oldKeys = new Vector();
- for (Map.Entry pair : mPassPhraseCache.entrySet()) {
- long lived = now - pair.getValue().timestamp;
- if (lived >= realTtl) {
- oldKeys.add(pair.getKey());
- } else {
- // see, whether the remaining time for this cache entry improves our
- // check delay
- long nextCheck = realTtl - lived + 1000;
- if (nextCheck < delay) {
- delay = (int) nextCheck;
- }
- }
- }
-
- for (long keyId : oldKeys) {
- mPassPhraseCache.remove(keyId);
- }
-
- return delay;
- }
-
- public static PGPSecretKey createKey(Context context, int algorithmChoice, int keySize,
- String passPhrase, PGPSecretKey masterKey) throws NoSuchAlgorithmException,
- PGPException, NoSuchProviderException, GeneralException,
- InvalidAlgorithmParameterException {
-
- if (keySize < 512) {
- throw new GeneralException(context.getString(R.string.error_keySizeMinimum512bit));
- }
-
- Security.addProvider(new BouncyCastleProvider());
-
- if (passPhrase == null) {
- passPhrase = "";
- }
-
- int algorithm = 0;
- KeyPairGenerator keyGen = null;
-
- switch (algorithmChoice) {
- case Id.choice.algorithm.dsa: {
- keyGen = KeyPairGenerator.getInstance("DSA", new BouncyCastleProvider());
- keyGen.initialize(keySize, new SecureRandom());
- algorithm = PGPPublicKey.DSA;
- break;
- }
-
- 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 = Primes.getBestPrime(keySize);
- BigInteger g = new BigInteger("2");
-
- ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
-
- keyGen.initialize(elParams);
- algorithm = PGPPublicKey.ELGAMAL_ENCRYPT;
- break;
- }
-
- case Id.choice.algorithm.rsa: {
- keyGen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
- keyGen.initialize(keySize, new SecureRandom());
-
- algorithm = PGPPublicKey.RSA_GENERAL;
- break;
- }
-
- default: {
- throw new GeneralException(context.getString(R.string.error_unknownAlgorithmChoice));
- }
- }
-
- PGPKeyPair keyPair = new PGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date());
-
- 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 = it.next();
- }
-
- return secretKey;
- }
-
- private static long getNumDaysBetween(GregorianCalendar first, GregorianCalendar second) {
- GregorianCalendar tmp = new GregorianCalendar();
- tmp.setTime(first.getTime());
- long numDays = (second.getTimeInMillis() - first.getTimeInMillis()) / 1000 / 86400;
- tmp.add(Calendar.DAY_OF_MONTH, (int) numDays);
- while (tmp.before(second)) {
- tmp.add(Calendar.DAY_OF_MONTH, 1);
- ++numDays;
- }
- return numDays;
- }
-
- public static void buildSecretKey(Activity context, SectionView userIdsView,
- SectionView keysView, String oldPassPhrase, String newPassPhrase,
- ProgressDialogUpdater progress) throws Apg.GeneralException, NoSuchProviderException,
- PGPException, NoSuchAlgorithmException, SignatureException, IOException,
- Database.GeneralException {
-
- if (progress != null)
- progress.setProgress(R.string.progress_buildingKey, 0, 100);
-
- Security.addProvider(new BouncyCastleProvider());
-
- if (oldPassPhrase == null || oldPassPhrase.equals("")) {
- oldPassPhrase = "";
- }
-
- if (newPassPhrase == null || newPassPhrase.equals("")) {
- newPassPhrase = "";
- }
-
- Vector userIds = new Vector();
- Vector keys = new Vector();
-
- ViewGroup userIdEditors = userIdsView.getEditors();
- ViewGroup keyEditors = keysView.getEditors();
-
- boolean gotMainUserId = false;
- for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
- String userId = null;
- try {
- userId = editor.getValue();
- } catch (UserIdEditor.NoNameException e) {
- throw new Apg.GeneralException(context.getString(R.string.error_userIdNeedsAName));
- } catch (UserIdEditor.NoEmailException e) {
- throw new Apg.GeneralException(
- context.getString(R.string.error_userIdNeedsAnEmailAddress));
- } catch (UserIdEditor.InvalidEmailException e) {
- throw new Apg.GeneralException("" + e);
- }
-
- if (userId.equals("")) {
- continue;
- }
-
- if (editor.isMainUserId()) {
- userIds.insertElementAt(userId, 0);
- gotMainUserId = true;
- } else {
- userIds.add(userId);
- }
- }
-
- if (userIds.size() == 0) {
- throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsAUserId));
- }
-
- if (!gotMainUserId) {
- throw new Apg.GeneralException(
- context.getString(R.string.error_mainUserIdMustNotBeEmpty));
- }
-
- if (keyEditors.getChildCount() == 0) {
- throw new Apg.GeneralException(context.getString(R.string.error_keyNeedsMasterKey));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
- keys.add(editor.getValue());
- }
-
- if (progress != null)
- progress.setProgress(R.string.progress_preparingMasterKey, 10, 100);
- KeyEditor keyEditor = (KeyEditor) keyEditors.getChildAt(0);
- 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);
-
- PGPSecretKey masterKey = keys.get(0);
- PGPPublicKey tmpKey = masterKey.getPublicKey();
- PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(),
- tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime());
- PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(oldPassPhrase.toCharArray(),
- new BouncyCastleProvider());
-
- if (progress != null)
- progress.setProgress(R.string.progress_certifyingMasterKey, 20, 100);
- for (int i = 0; i < userIds.size(); ++i) {
- String userId = userIds.get(i);
-
- PGPSignatureGenerator sGen = new PGPSignatureGenerator(masterPublicKey.getAlgorithm(),
- HashAlgorithmTags.SHA1, new BouncyCastleProvider());
-
- sGen.initSign(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
-
- PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
-
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
- }
-
- // TODO: cross-certify the master key with every sub key
-
- PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
-
- PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
-
- int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- hashedPacketsGen.setKeyFlags(true, keyFlags);
-
- hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
- hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
- hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
-
- // TODO: this doesn't work quite right yet
- if (keyEditor.getExpiryDate() != null) {
- GregorianCalendar creationDate = new GregorianCalendar();
- creationDate.setTime(getCreationDate(masterKey));
- GregorianCalendar expiryDate = keyEditor.getExpiryDate();
- long numDays = getNumDaysBetween(creationDate, expiryDate);
- if (numDays <= 0) {
- throw new GeneralException(
- context.getString(R.string.error_expiryMustComeAfterCreation));
- }
- hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
- }
-
- if (progress != null)
- progress.setProgress(R.string.progress_buildingMasterKeyRing, 30, 100);
- PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
- masterKeyPair, mainUserId, PGPEncryptedData.CAST5, newPassPhrase.toCharArray(),
- hashedPacketsGen.generate(), unhashedPacketsGen.generate(), new SecureRandom(),
- new BouncyCastleProvider().getName());
-
- if (progress != null)
- progress.setProgress(R.string.progress_addingSubKeys, 40, 100);
- for (int i = 1; i < keys.size(); ++i) {
- if (progress != null)
- progress.setProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100);
- PGPSecretKey subKey = keys.get(i);
- keyEditor = (KeyEditor) keyEditors.getChildAt(i);
- PGPPublicKey subPublicKey = subKey.getPublicKey();
- PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(oldPassPhrase.toCharArray(),
- new BouncyCastleProvider());
- PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey.getAlgorithm(),
- subPublicKey.getKey(new BouncyCastleProvider()), subPrivateKey.getKey(),
- subPublicKey.getCreationTime());
-
- hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
-
- keyFlags = 0;
- 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;
- }
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- hashedPacketsGen.setKeyFlags(true, keyFlags);
-
- // TODO: this doesn't work quite right yet
- if (keyEditor.getExpiryDate() != null) {
- GregorianCalendar creationDate = new GregorianCalendar();
- creationDate.setTime(getCreationDate(masterKey));
- GregorianCalendar expiryDate = keyEditor.getExpiryDate();
- long numDays = getNumDaysBetween(creationDate, expiryDate);
- if (numDays <= 0) {
- throw new GeneralException(
- context.getString(R.string.error_expiryMustComeAfterCreation));
- }
- hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400);
- }
-
- keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate());
- }
-
- PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
- PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
-
- if (progress != null)
- progress.setProgress(R.string.progress_savingKeyRing, 90, 100);
- mDatabase.saveKeyRing(secretKeyRing);
- mDatabase.saveKeyRing(publicKeyRing);
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
- }
-
- public static PGPKeyRing decodeKeyRing(InputStream is) throws IOException {
- InputStream in = PGPUtil.getDecoderStream(is);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
- Object obj = objectFactory.nextObject();
-
- if (obj instanceof PGPKeyRing) {
- return (PGPKeyRing) obj;
- }
-
- return null;
- }
-
- public static int storeKeyRingInCache(PGPKeyRing keyring) {
- int status = Integer.MIN_VALUE; // out of bounds value (Id.retrun_value.*)
- try {
- if (keyring instanceof PGPSecretKeyRing) {
- PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
- boolean save = true;
- try {
- PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey(
- new char[] {}, new BouncyCastleProvider());
- if (testKey == null) {
- // this is bad, something is very wrong... likely a --export-secret-subkeys
- // export
- save = false;
- status = Id.return_value.bad;
- }
- } catch (PGPException e) {
- // all good if this fails, we likely didn't use the right password
- }
-
- if (save) {
- status = mDatabase.saveKeyRing(secretKeyRing);
- }
- } else if (keyring instanceof PGPPublicKeyRing) {
- PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
- status = mDatabase.saveKeyRing(publicKeyRing);
- }
- } catch (IOException e) {
- status = Id.return_value.error;
- } catch (Database.GeneralException e) {
- status = Id.return_value.error;
- }
-
- return status;
- }
-
- public static boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ArmoredOutputStream aos = new ArmoredOutputStream(bos);
- try {
- aos.write(keyring.getEncoded());
- aos.close();
-
- String armouredKey = bos.toString("UTF-8");
- server.add(armouredKey);
-
- return true;
- } catch (IOException e) {
- return false;
- } catch (AddKeyException e) {
- // TODO: tell the user?
- return false;
- } finally {
- try {
- bos.close();
- } catch (IOException e) {
- }
- }
- }
-
- public static Bundle importKeyRings(Activity context, int type, InputData data,
- ProgressDialogUpdater progress) throws GeneralException, FileNotFoundException,
- PGPException, IOException {
- Bundle returnData = new Bundle();
-
- if (type == Id.type.secret_key) {
- if (progress != null)
- progress.setProgress(R.string.progress_importingSecretKeys, 0, 100);
- } else {
- if (progress != null)
- progress.setProgress(R.string.progress_importingPublicKeys, 0, 100);
- }
-
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
- }
-
- PositionAwareInputStream progressIn = new PositionAwareInputStream(data.getInputStream());
- // need to have access to the bufferedInput, so we can reuse it for the possible
- // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
- // armour blocks
- BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
- int newKeys = 0;
- int oldKeys = 0;
- int badKeys = 0;
- try {
- PGPKeyRing keyring = decodeKeyRing(bufferedInput);
- while (keyring != null) {
- int status = Integer.MIN_VALUE; // out of bounds value
-
- // if this key is what we expect it to be, save it
- if ((type == Id.type.secret_key && keyring instanceof PGPSecretKeyRing)
- || (type == Id.type.public_key && keyring instanceof PGPPublicKeyRing)) {
- status = storeKeyRingInCache(keyring);
- }
-
- if (status == Id.return_value.error) {
- throw new GeneralException(context.getString(R.string.error_savingKeys));
- }
-
- // update the counts to display to the user at the end
- if (status == Id.return_value.updated) {
- ++oldKeys;
- } else if (status == Id.return_value.ok) {
- ++newKeys;
- } else if (status == Id.return_value.bad) {
- ++badKeys;
- }
-
- if (progress != null) {
- progress.setProgress((int) (100 * progressIn.position() / data.getSize()), 100);
- }
- // TODO: needed?
- // obj = objectFactory.nextObject();
-
- keyring = decodeKeyRing(bufferedInput);
- }
- } catch (EOFException e) {
- // nothing to do, we are done
- }
-
- returnData.putInt("added", newKeys);
- returnData.putInt("updated", oldKeys);
- returnData.putInt("bad", badKeys);
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
-
- return returnData;
- }
-
- public static Bundle exportKeyRings(Activity context, Vector keyRingIds,
- OutputStream outStream, ProgressDialogUpdater progress) throws GeneralException,
- FileNotFoundException, PGPException, IOException {
- Bundle returnData = new Bundle();
-
- if (keyRingIds.size() == 1) {
- if (progress != null)
- progress.setProgress(R.string.progress_exportingKey, 0, 100);
- } else {
- if (progress != null)
- progress.setProgress(R.string.progress_exportingKeys, 0, 100);
- }
-
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(context.getString(R.string.error_externalStorageNotReady));
- }
- ArmoredOutputStream out = new ArmoredOutputStream(outStream);
-
- int numKeys = 0;
- for (int i = 0; i < keyRingIds.size(); ++i) {
- if (progress != null)
- progress.setProgress(i * 100 / keyRingIds.size(), 100);
- Object obj = mDatabase.getKeyRing(keyRingIds.get(i));
- PGPPublicKeyRing publicKeyRing;
- PGPSecretKeyRing secretKeyRing;
-
- if (obj instanceof PGPSecretKeyRing) {
- secretKeyRing = (PGPSecretKeyRing) obj;
- secretKeyRing.encode(out);
- } else if (obj instanceof PGPPublicKeyRing) {
- publicKeyRing = (PGPPublicKeyRing) obj;
- publicKeyRing.encode(out);
- } else {
- continue;
- }
- ++numKeys;
- }
- out.close();
- returnData.putInt("exported", numKeys);
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
-
- return returnData;
- }
-
- public static Date getCreationDate(PGPPublicKey key) {
- return key.getCreationTime();
- }
-
- public static Date getCreationDate(PGPSecretKey key) {
- return key.getPublicKey().getCreationTime();
- }
-
- @SuppressWarnings("unchecked")
- public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
- public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
- public static Vector getEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector encryptKeys = new Vector();
-
- for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) {
- if (isEncryptionKey(key)) {
- encryptKeys.add(key);
- }
- }
-
- return encryptKeys;
- }
-
- @SuppressWarnings("unchecked")
- public static Vector getSigningKeys(PGPSecretKeyRing keyRing) {
- Vector signingKeys = new Vector();
-
- for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) {
- if (isSigningKey(key)) {
- signingKeys.add(key);
- }
- }
-
- return signingKeys;
- }
-
- public static Vector getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
- Vector usableKeys = new Vector();
- Vector encryptKeys = getEncryptKeys(keyRing);
- PGPPublicKey masterKey = null;
- for (int i = 0; i < encryptKeys.size(); ++i) {
- PGPPublicKey key = encryptKeys.get(i);
- if (!isExpired(key)) {
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- public static boolean isExpired(PGPPublicKey key) {
- Date creationDate = getCreationDate(key);
- Date expiryDate = getExpiryDate(key);
- Date now = new Date();
- if (now.compareTo(creationDate) >= 0
- && (expiryDate == null || now.compareTo(expiryDate) <= 0)) {
- return false;
- }
- return true;
- }
-
- public static boolean isExpired(PGPSecretKey key) {
- return isExpired(key.getPublicKey());
- }
-
- public static Vector getUsableSigningKeys(PGPSecretKeyRing keyRing) {
- Vector usableKeys = new Vector();
- Vector signingKeys = getSigningKeys(keyRing);
- PGPSecretKey masterKey = null;
- for (int i = 0; i < signingKeys.size(); ++i) {
- PGPSecretKey key = signingKeys.get(i);
- if (key.isMasterKey()) {
- masterKey = key;
- } else {
- usableKeys.add(key);
- }
- }
- if (masterKey != null) {
- usableKeys.add(masterKey);
- }
- return usableKeys;
- }
-
- public static Date getExpiryDate(PGPPublicKey key) {
- Date creationDate = getCreationDate(key);
- if (key.getValidDays() == 0) {
- // no expiry
- return null;
- }
- Calendar calendar = GregorianCalendar.getInstance();
- calendar.setTime(creationDate);
- calendar.add(Calendar.DATE, key.getValidDays());
- Date expiryDate = calendar.getTime();
-
- return expiryDate;
- }
-
- public static Date getExpiryDate(PGPSecretKey key) {
- return getExpiryDate(key.getPublicKey());
- }
-
- public static PGPPublicKey getEncryptPublicKey(long masterKeyId) {
- PGPPublicKeyRing keyRing = getPublicKeyRing(masterKeyId);
- if (keyRing == null) {
- return null;
- }
- Vector encryptKeys = getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- return null;
- }
- return encryptKeys.get(0);
- }
-
- public static PGPSecretKey getSigningKey(long masterKeyId) {
- PGPSecretKeyRing keyRing = getSecretKeyRing(masterKeyId);
- if (keyRing == null) {
- return null;
- }
- Vector signingKeys = getUsableSigningKeys(keyRing);
- if (signingKeys.size() == 0) {
- return null;
- }
- return signingKeys.get(0);
- }
-
- @SuppressWarnings("unchecked")
- public static String getMainUserId(PGPPublicKey key) {
- for (String userId : new IterableIterator(key.getUserIDs())) {
- return userId;
- }
- return null;
- }
-
- @SuppressWarnings("unchecked")
- public static String getMainUserId(PGPSecretKey key) {
- for (String userId : new IterableIterator(key.getUserIDs())) {
- return userId;
- }
- return null;
- }
-
- public static String getMainUserIdSafe(Context context, PGPPublicKey key) {
- String userId = getMainUserId(key);
- if (userId == null) {
- userId = context.getResources().getString(R.string.unknownUserId);
- }
- return userId;
- }
-
- public static String getMainUserIdSafe(Context context, PGPSecretKey key) {
- String userId = getMainUserId(key);
- if (userId == null) {
- userId = context.getResources().getString(R.string.unknownUserId);
- }
- return userId;
- }
-
- @SuppressWarnings("unchecked")
- public static boolean isEncryptionKey(PGPPublicKey key) {
- if (!key.isEncryptionKey()) {
- return false;
- }
-
- if (key.getVersion() <= 3) {
- // this must be true now
- return key.isEncryptionKey();
- }
-
- // special cases
- if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
- return true;
- }
-
- if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
- return true;
- }
-
- for (PGPSignature sig : new IterableIterator(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
-
- 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;
- }
-
- public static boolean isEncryptionKey(PGPSecretKey key) {
- return isEncryptionKey(key.getPublicKey());
- }
-
- @SuppressWarnings("unchecked")
- public static boolean isSigningKey(PGPPublicKey key) {
- if (key.getVersion() <= 3) {
- return true;
- }
-
- // special case
- if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
- return true;
- }
-
- for (PGPSignature sig : new IterableIterator(key.getSignatures())) {
- if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
- continue;
- }
- PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
-
- 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;
- }
- }
-
- return false;
- }
-
- public static boolean isSigningKey(PGPSecretKey key) {
- return isSigningKey(key.getPublicKey());
- }
-
- public static String getAlgorithmInfo(PGPPublicKey key) {
- return getAlgorithmInfo(key.getAlgorithm(), key.getBitStrength());
- }
-
- public static String getAlgorithmInfo(PGPSecretKey key) {
- return getAlgorithmInfo(key.getPublicKey());
- }
-
- public static String getAlgorithmInfo(int algorithm, int keySize) {
- String algorithmStr = null;
-
- switch (algorithm) {
- case PGPPublicKey.RSA_ENCRYPT:
- case PGPPublicKey.RSA_GENERAL:
- case PGPPublicKey.RSA_SIGN: {
- algorithmStr = "RSA";
- break;
- }
-
- case PGPPublicKey.DSA: {
- algorithmStr = "DSA";
- break;
- }
-
- case PGPPublicKey.ELGAMAL_ENCRYPT:
- case PGPPublicKey.ELGAMAL_GENERAL: {
- algorithmStr = "ElGamal";
- break;
- }
-
- default: {
- algorithmStr = "???";
- break;
- }
- }
- return algorithmStr + ", " + keySize + "bit";
- }
-
- public static String convertToHex(byte[] fp) {
- String fingerPrint = "";
- for (int i = 0; i < fp.length; ++i) {
- if (i != 0 && i % 10 == 0) {
- fingerPrint += " ";
- } else if (i != 0 && i % 2 == 0) {
- fingerPrint += " ";
- }
- String chunk = Integer.toHexString((fp[i] + 256) % 256).toUpperCase();
- while (chunk.length() < 2) {
- chunk = "0" + chunk;
- }
- fingerPrint += chunk;
- }
-
- return fingerPrint;
-
- }
-
- public static String getFingerPrint(long keyId) {
- PGPPublicKey key = Apg.getPublicKey(keyId);
- if (key == null) {
- PGPSecretKey secretKey = Apg.getSecretKey(keyId);
- if (secretKey == null) {
- return "";
- }
- key = secretKey.getPublicKey();
- }
-
- return convertToHex(key.getFingerprint());
- }
-
- public static String getSmallFingerPrint(long keyId) {
- String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase();
- while (fingerPrint.length() < 8) {
- fingerPrint = "0" + fingerPrint;
- }
- return fingerPrint;
- }
-
- public static String keyToHex(long keyId) {
- return getSmallFingerPrint(keyId >> 32) + getSmallFingerPrint(keyId);
- }
-
- public static long keyFromHex(String data) {
- int len = data.length();
- String s2 = data.substring(len - 8);
- String s1 = data.substring(0, len - 8);
- return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
- }
-
- public static void deleteKey(int keyRingId) {
- mDatabase.deleteKeyRing(keyRingId);
- }
-
- public static PGPKeyRing getKeyRing(int keyRingId) {
- return (PGPKeyRing) mDatabase.getKeyRing(keyRingId);
- }
-
- public static PGPSecretKeyRing getSecretKeyRing(long keyId) {
- byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_secret, keyId);
- if (data == null) {
- return null;
- }
- try {
- return new PGPSecretKeyRing(data);
- } catch (IOException e) {
- // no good way to handle this, return null
- // TODO: some info?
- } catch (PGPException e) {
- // no good way to handle this, return null
- // TODO: some info?
- }
- return null;
- }
-
- public static PGPPublicKeyRing getPublicKeyRing(long keyId) {
- byte[] data = mDatabase.getKeyRingDataFromKeyId(Id.database.type_public, keyId);
- if (data == null) {
- return null;
- }
- try {
- return new PGPPublicKeyRing(data);
- } catch (IOException e) {
- // no good way to handle this, return null
- // TODO: some info?
- }
- return null;
- }
-
- public static PGPSecretKey getSecretKey(long keyId) {
- PGPSecretKeyRing keyRing = getSecretKeyRing(keyId);
- if (keyRing == null) {
- return null;
- }
- return keyRing.getSecretKey(keyId);
- }
-
- public static PGPPublicKey getPublicKey(long keyId) {
- PGPPublicKeyRing keyRing = getPublicKeyRing(keyId);
- if (keyRing == null) {
- return null;
- }
-
- return keyRing.getPublicKey(keyId);
- }
-
- public static Vector getKeyRingIds(int type) {
- SQLiteDatabase db = mDatabase.db();
- Vector keyIds = new Vector();
- Cursor c = db.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID }, KeyRings.TYPE
- + " = ?", new String[] { "" + type }, null, null, null);
- if (c != null && c.moveToFirst()) {
- do {
- keyIds.add(c.getInt(0));
- } while (c.moveToNext());
- }
-
- if (c != null) {
- c.close();
- }
-
- return keyIds;
- }
-
- public static String getMainUserId(long keyId, int type) {
- SQLiteDatabase db = mDatabase.db();
- Cursor c = db.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON ("
- + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
- + Keys.KEY_RING_ID + ") " + " INNER JOIN " + Keys.TABLE_NAME + " AS masterKey ON ("
- + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + "masterKey."
- + Keys.KEY_RING_ID + " AND " + "masterKey." + Keys.IS_MASTER_KEY + " = '1') "
- + " INNER JOIN " + UserIds.TABLE_NAME + " ON (" + UserIds.TABLE_NAME + "."
- + UserIds.KEY_ID + " = " + "masterKey." + Keys._ID + " AND " + UserIds.TABLE_NAME
- + "." + UserIds.RANK + " = '0')", new String[] { UserIds.USER_ID }, Keys.TABLE_NAME
- + "." + Keys.KEY_ID + " = ? AND " + KeyRings.TABLE_NAME + "." + KeyRings.TYPE
- + " = ?", new String[] { "" + keyId, "" + type, }, null, null, null);
- String userId = "";
- if (c != null && c.moveToFirst()) {
- do {
- userId = c.getString(0);
- } while (c.moveToNext());
- }
-
- if (c != null) {
- c.close();
- }
-
- return userId;
- }
-
- public static void encrypt(Context context, InputData data, OutputStream outStream,
- boolean armored, long encryptionKeyIds[], long signatureKeyId,
- String signaturePassPhrase, ProgressDialogUpdater progress, int symmetricAlgorithm,
- int hashAlgorithm, int compression, boolean forceV3Signature, String passPhrase)
- throws IOException, GeneralException, PGPException, NoSuchProviderException,
- NoSuchAlgorithmException, SignatureException {
- Security.addProvider(new BouncyCastleProvider());
-
- 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", getFullVersion(context));
- out = armorOut;
- } else {
- out = outStream;
- }
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (encryptionKeyIds.length == 0 && passPhrase == null) {
- throw new GeneralException(
- context.getString(R.string.error_noEncryptionKeysOrPassPhrase));
- }
-
- if (signatureKeyId != 0) {
- signingKeyRing = getSecretKeyRing(signatureKeyId);
- signingKey = getSigningKey(signatureKeyId);
- if (signingKey == null) {
- throw new GeneralException(context.getString(R.string.error_signatureFailed));
- }
-
- if (signaturePassPhrase == null) {
- throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
- }
- if (progress != null)
- progress.setProgress(R.string.progress_extractingSignatureKey, 0, 100);
- signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
- new BouncyCastleProvider());
- if (signaturePrivateKey == null) {
- throw new GeneralException(
- context.getString(R.string.error_couldNotExtractPrivateKey));
- }
- }
- if (progress != null)
- progress.setProgress(R.string.progress_preparingStreams, 5, 100);
-
- // encrypt and compress input file content
- PGPEncryptedDataGenerator cPk = 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) {
- cPk.addMethod(key);
- }
- }
- encryptOut = cPk.open(out, new byte[1 << 16]);
-
- PGPSignatureGenerator signatureGenerator = null;
- PGPV3SignatureGenerator signatureV3Generator = null;
-
- if (signatureKeyId != 0) {
- if (progress != null)
- progress.setProgress(R.string.progress_preparingSignature, 10, 100);
- if (forceV3Signature) {
- signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
- .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
- signatureV3Generator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(signingKey.getPublicKey()
- .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
- signatureGenerator.initSign(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey);
-
- String userId = getMainUserId(getMasterKey(signingKeyRing));
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
- }
- }
-
- PGPCompressedDataGenerator compressGen = null;
- BCPGOutputStream bcpgOut = null;
- if (compression == Id.choice.compression.none) {
- bcpgOut = new BCPGOutputStream(encryptOut);
- } else {
- compressGen = new PGPCompressedDataGenerator(compression);
- bcpgOut = new BCPGOutputStream(compressGen.open(encryptOut));
- }
- if (signatureKeyId != 0) {
- if (forceV3Signature) {
- signatureV3Generator.generateOnePassVersion(false).encode(bcpgOut);
- } else {
- signatureGenerator.generateOnePassVersion(false).encode(bcpgOut);
- }
- }
-
- PGPLiteralDataGenerator literalGen = new PGPLiteralDataGenerator();
- // file name not needed, so empty string
- OutputStream pOut = literalGen.open(bcpgOut, PGPLiteralData.BINARY, "", new Date(),
- new byte[1 << 16]);
- if (progress != null)
- progress.setProgress(R.string.progress_encrypting, 20, 100);
-
- long done = 0;
- int n = 0;
- byte[] buffer = new byte[1 << 16];
- InputStream in = data.getInputStream();
- while ((n = in.read(buffer)) > 0) {
- pOut.write(buffer, 0, n);
- if (signatureKeyId != 0) {
- if (forceV3Signature) {
- signatureV3Generator.update(buffer, 0, n);
- } else {
- signatureGenerator.update(buffer, 0, n);
- }
- }
- done += n;
- if (data.getSize() != 0) {
- if (progress != null)
- progress.setProgress((int) (20 + (95 - 20) * done / data.getSize()), 100);
- }
- }
-
- literalGen.close();
-
- if (signatureKeyId != 0) {
- if (progress != null)
- progress.setProgress(R.string.progress_generatingSignature, 95, 100);
- if (forceV3Signature) {
- signatureV3Generator.generate().encode(pOut);
- } else {
- signatureGenerator.generate().encode(pOut);
- }
- }
- if (compressGen != null) {
- compressGen.close();
- }
- encryptOut.close();
- if (armored) {
- armorOut.close();
- }
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
- }
-
- public static void signText(Context context, InputData data, OutputStream outStream,
- long signatureKeyId, String signaturePassPhrase, int hashAlgorithm,
- boolean forceV3Signature, ProgressDialogUpdater progress) throws GeneralException,
- PGPException, IOException, NoSuchAlgorithmException, SignatureException {
- Security.addProvider(new BouncyCastleProvider());
-
- ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream);
- armorOut.setHeader("Version", getFullVersion(context));
-
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (signatureKeyId == 0) {
- throw new GeneralException(context.getString(R.string.error_noSignatureKey));
- }
-
- signingKeyRing = getSecretKeyRing(signatureKeyId);
- signingKey = getSigningKey(signatureKeyId);
- if (signingKey == null) {
- throw new GeneralException(context.getString(R.string.error_signatureFailed));
- }
-
- if (signaturePassPhrase == null) {
- throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
- }
- signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
- new BouncyCastleProvider());
- if (signaturePrivateKey == null) {
- throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey));
- }
- if (progress != null)
- progress.setProgress(R.string.progress_preparingStreams, 0, 100);
-
- if (progress != null)
- progress.setProgress(R.string.progress_preparingSignature, 30, 100);
-
- PGPSignatureGenerator signatureGenerator = null;
- PGPV3SignatureGenerator signatureV3Generator = null;
-
- if (forceV3Signature) {
- signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
- .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
- signatureV3Generator
- .initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(
- signingKey.getPublicKey().getAlgorithm(), hashAlgorithm,
- new BouncyCastleProvider());
- signatureGenerator.initSign(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- String userId = getMainUserId(getMasterKey(signingKeyRing));
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
- }
-
- if (progress != null)
- progress.setProgress(R.string.progress_signing, 40, 100);
-
- armorOut.beginClearText(hashAlgorithm);
-
- InputStream inStream = data.getInputStream();
- final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
-
- final byte[] newline = "\r\n".getBytes("UTF-8");
-
- if (forceV3Signature) {
- processLine(reader.readLine(), armorOut, signatureV3Generator);
- } else {
- processLine(reader.readLine(), armorOut, signatureGenerator);
- }
-
- while (true) {
- final String line = reader.readLine();
-
- if (line == null) {
- armorOut.write(newline);
- break;
- }
-
- armorOut.write(newline);
- if (forceV3Signature) {
- signatureV3Generator.update(newline);
- processLine(line, armorOut, signatureV3Generator);
- } else {
- signatureGenerator.update(newline);
- processLine(line, armorOut, signatureGenerator);
- }
- }
-
- armorOut.endClearText();
-
- BCPGOutputStream bOut = new BCPGOutputStream(armorOut);
- if (forceV3Signature) {
- signatureV3Generator.generate().encode(bOut);
- } else {
- signatureGenerator.generate().encode(bOut);
- }
- armorOut.close();
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
- }
-
- public static void generateSignature(Context context, InputData data, OutputStream outStream,
- boolean armored, boolean binary, long signatureKeyId, String signaturePassPhrase,
- int hashAlgorithm, boolean forceV3Signature, ProgressDialogUpdater progress)
- throws GeneralException, PGPException, IOException, NoSuchAlgorithmException,
- SignatureException {
- Security.addProvider(new BouncyCastleProvider());
-
- ArmoredOutputStream armorOut = null;
- OutputStream out = null;
- if (armored) {
- armorOut = new ArmoredOutputStream(outStream);
- armorOut.setHeader("Version", getFullVersion(context));
- out = armorOut;
- } else {
- out = outStream;
- }
-
- PGPSecretKey signingKey = null;
- PGPSecretKeyRing signingKeyRing = null;
- PGPPrivateKey signaturePrivateKey = null;
-
- if (signatureKeyId == 0) {
- throw new GeneralException(context.getString(R.string.error_noSignatureKey));
- }
-
- signingKeyRing = getSecretKeyRing(signatureKeyId);
- signingKey = getSigningKey(signatureKeyId);
- if (signingKey == null) {
- throw new GeneralException(context.getString(R.string.error_signatureFailed));
- }
-
- if (signaturePassPhrase == null) {
- throw new GeneralException(context.getString(R.string.error_noSignaturePassPhrase));
- }
- signaturePrivateKey = signingKey.extractPrivateKey(signaturePassPhrase.toCharArray(),
- new BouncyCastleProvider());
- if (signaturePrivateKey == null) {
- throw new GeneralException(context.getString(R.string.error_couldNotExtractPrivateKey));
- }
- if (progress != null)
- progress.setProgress(R.string.progress_preparingStreams, 0, 100);
-
- if (progress != null)
- progress.setProgress(R.string.progress_preparingSignature, 30, 100);
-
- PGPSignatureGenerator signatureGenerator = null;
- PGPV3SignatureGenerator signatureV3Generator = null;
-
- int type = PGPSignature.CANONICAL_TEXT_DOCUMENT;
- if (binary) {
- type = PGPSignature.BINARY_DOCUMENT;
- }
-
- if (forceV3Signature) {
- signatureV3Generator = new PGPV3SignatureGenerator(signingKey.getPublicKey()
- .getAlgorithm(), hashAlgorithm, new BouncyCastleProvider());
- signatureV3Generator.initSign(type, signaturePrivateKey);
- } else {
- signatureGenerator = new PGPSignatureGenerator(
- signingKey.getPublicKey().getAlgorithm(), hashAlgorithm,
- new BouncyCastleProvider());
- signatureGenerator.initSign(type, signaturePrivateKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- String userId = getMainUserId(getMasterKey(signingKeyRing));
- spGen.setSignerUserID(false, userId);
- signatureGenerator.setHashedSubpackets(spGen.generate());
- }
-
- if (progress != null)
- progress.setProgress(R.string.progress_signing, 40, 100);
-
- InputStream inStream = data.getInputStream();
- if (binary) {
- byte[] buffer = new byte[1 << 16];
- int n = 0;
- while ((n = inStream.read(buffer)) > 0) {
- if (forceV3Signature) {
- signatureV3Generator.update(buffer, 0, n);
- } else {
- signatureGenerator.update(buffer, 0, n);
- }
- }
- } else {
- final BufferedReader reader = new BufferedReader(new InputStreamReader(inStream));
- final byte[] newline = "\r\n".getBytes("UTF-8");
-
- while (true) {
- final String line = reader.readLine();
-
- if (line == null) {
- break;
- }
-
- if (forceV3Signature) {
- processLine(line, null, signatureV3Generator);
- signatureV3Generator.update(newline);
- } else {
- processLine(line, null, signatureGenerator);
- signatureGenerator.update(newline);
- }
- }
- }
-
- BCPGOutputStream bOut = new BCPGOutputStream(out);
- if (forceV3Signature) {
- signatureV3Generator.generate().encode(bOut);
- } else {
- signatureGenerator.generate().encode(bOut);
- }
- out.close();
- outStream.close();
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
- }
-
- public static long getDecryptionKeyId(Context context, InputData data) throws GeneralException,
- NoAsymmetricEncryptionException, IOException {
- InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
- 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));
- }
-
- // TODO: currently we always only look at the first known key
- // find the secret key
- PGPSecretKey secretKey = null;
- 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 = getSecretKey(pbe.getKeyID());
- if (secretKey != null) {
- break;
- }
- }
- }
-
- if (!gotAsymmetricEncryption) {
- throw new NoAsymmetricEncryptionException();
- }
-
- if (secretKey == null) {
- return Id.key.none;
- }
-
- return secretKey.getKeyID();
- }
-
- public static boolean hasSymmetricEncryption(Context context, InputData data)
- throws GeneralException, IOException {
- InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
- 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, InputData data, OutputStream outStream,
- String passPhrase, ProgressDialogUpdater progress, boolean assumeSymmetric)
- throws IOException, GeneralException, PGPException, SignatureException {
- if (passPhrase == null) {
- passPhrase = "";
- }
- Bundle returnData = new Bundle();
- InputStream in = PGPUtil.getDecoderStream(data.getInputStream());
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
- long signatureKeyId = 0;
-
- int currentProgress = 0;
- if (progress != null)
- progress.setProgress(R.string.progress_readingData, currentProgress, 100);
-
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new GeneralException(context.getString(R.string.error_invalidData));
- }
-
- 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 (pbe == null) {
- throw new GeneralException(
- context.getString(R.string.error_noSymmetricEncryptionPacket));
- }
-
- if (progress != null)
- progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
- clear = pbe.getDataStream(passPhrase.toCharArray(), new BouncyCastleProvider());
- encryptedData = pbe;
- currentProgress += 5;
- } else {
- if (progress != null)
- 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 = getSecretKey(encData.getKeyID());
- if (secretKey != null) {
- pbe = encData;
- break;
- }
- }
- }
-
- if (secretKey == null) {
- throw new GeneralException(context.getString(R.string.error_noSecretKeyFound));
- }
-
- currentProgress += 5;
- if (progress != null)
- 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));
- }
- if (privateKey == null) {
- throw new GeneralException(
- context.getString(R.string.error_couldNotExtractPrivateKey));
- }
- currentProgress += 5;
- if (progress != null)
- progress.setProgress(R.string.progress_preparingStreams, currentProgress, 100);
- clear = pbe.getDataStream(privateKey, new BouncyCastleProvider());
- encryptedData = pbe;
- currentProgress += 5;
- }
-
- PGPObjectFactory plainFact = new PGPObjectFactory(clear);
- Object dataChunk = plainFact.nextObject();
- PGPOnePassSignature signature = null;
- PGPPublicKey signatureKey = null;
- int signatureIndex = -1;
-
- if (dataChunk instanceof PGPCompressedData) {
- if (progress != null)
- 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) {
- if (progress != null)
- progress.setProgress(R.string.progress_processingSignature, currentProgress, 100);
- returnData.putBoolean(EXTRA_SIGNATURE, true);
- PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
- for (int i = 0; i < sigList.size(); ++i) {
- signature = sigList.get(i);
- signatureKey = getPublicKey(signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
- signature = null;
- } else {
- signatureIndex = i;
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
- if (sigKeyRing != null) {
- userId = getMainUserId(getMasterKey(sigKeyRing));
- }
- returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
- break;
- }
- }
-
- returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
-
- if (signature != null) {
- signature.initVerify(signatureKey, new BouncyCastleProvider());
- } else {
- returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
- }
-
- dataChunk = plainFact.nextObject();
- currentProgress += 10;
- }
-
- if (dataChunk instanceof PGPSignatureList) {
- dataChunk = plainFact.nextObject();
- }
-
- if (dataChunk instanceof PGPLiteralData) {
- if (progress != null)
- progress.setProgress(R.string.progress_decrypting, currentProgress, 100);
- PGPLiteralData literalData = (PGPLiteralData) dataChunk;
- OutputStream out = outStream;
-
- byte[] buffer = new byte[1 << 16];
- InputStream dataIn = literalData.getInputStream();
-
- 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 = data.getStreamPosition();
- while ((n = dataIn.read(buffer)) > 0) {
- out.write(buffer, 0, n);
- done += n;
- if (signature != null) {
- try {
- signature.update(buffer, 0, n);
- } catch (SignatureException e) {
- returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, 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 (data.getSize() - startPos == 0) {
- currentProgress = endProgress;
- } else {
- currentProgress = (int) (startProgress + (endProgress - startProgress)
- * (data.getStreamPosition() - startPos) / (data.getSize() - startPos));
- }
- if (progress != null)
- progress.setProgress(currentProgress, 100);
- }
-
- if (signature != null) {
- if (progress != null)
- progress.setProgress(R.string.progress_verifyingSignature, 90, 100);
- PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
- PGPSignature messageSignature = signatureList.get(signatureIndex);
- if (signature.verify(messageSignature)) {
- returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, true);
- } else {
- returnData.putBoolean(EXTRA_SIGNATURE_SUCCESS, false);
- }
- }
- }
-
- // TODO: add integrity somewhere
- if (encryptedData.isIntegrityProtected()) {
- if (progress != null)
- progress.setProgress(R.string.progress_verifyingIntegrity, 95, 100);
- if (encryptedData.verify()) {
- // passed
- } else {
- // failed
- }
- } else {
- // no integrity check
- }
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- public static Bundle verifyText(BaseActivity context, InputData data, OutputStream outStream,
- ProgressDialogUpdater progress) throws IOException, GeneralException, PGPException,
- SignatureException {
- Bundle returnData = new Bundle();
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream());
-
- if (progress != null)
- 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(EXTRA_SIGNATURE, true);
-
- if (progress != null)
- 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 = getPublicKey(signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
- Bundle pauseData = new Bundle();
- pauseData.putInt(Constants.extras.STATUS, Id.message.unknown_signature_key);
- pauseData.putLong(Constants.extras.KEY_ID, signatureKeyId);
- Message msg = new Message();
- msg.setData(pauseData);
- context.sendMessage(msg);
- // pause here
- context.getRunningThread().pause();
- // see whether the key was found in the meantime
- signatureKey = getPublicKey(signature.getKeyID());
- }
-
- if (signatureKey == null) {
- signature = null;
- } else {
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing sigKeyRing = getPublicKeyRing(signatureKeyId);
- if (sigKeyRing != null) {
- userId = getMainUserId(getMasterKey(sigKeyRing));
- }
- returnData.putString(EXTRA_SIGNATURE_USER_ID, userId);
- break;
- }
- }
-
- returnData.putLong(EXTRA_SIGNATURE_KEY_ID, signatureKeyId);
-
- if (signature == null) {
- returnData.putBoolean(EXTRA_SIGNATURE_UNKNOWN, true);
- if (progress != null)
- 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(EXTRA_SIGNATURE_SUCCESS, signature.verify());
-
- if (progress != null)
- progress.setProgress(R.string.progress_done, 100, 100);
- return returnData;
- }
-
- public static int getStreamContent(Context context, InputStream inStream) throws IOException {
- InputStream in = PGPUtil.getDecoderStream(inStream);
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- Object object = pgpF.nextObject();
- while (object != null) {
- if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) {
- return Id.content.keys;
- } else if (object instanceof PGPEncryptedDataList) {
- return Id.content.encrypted_data;
- }
- object = pgpF.nextObject();
- }
-
- return Id.content.unknown;
- }
-
- private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
- final PGPSignatureGenerator pSignatureGenerator) throws IOException, SignatureException {
-
- if (pLine == null) {
- return;
- }
-
- final char[] chars = pLine.toCharArray();
- int len = chars.length;
-
- while (len > 0) {
- if (!Character.isWhitespace(chars[len - 1])) {
- break;
- }
- len--;
- }
-
- final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
-
- if (pArmoredOutput != null) {
- pArmoredOutput.write(data);
- }
- pSignatureGenerator.update(data);
- }
-
- private static void processLine(final String pLine, final ArmoredOutputStream pArmoredOutput,
- final PGPV3SignatureGenerator pSignatureGenerator) throws IOException,
- SignatureException {
-
- if (pLine == null) {
- return;
- }
-
- final char[] chars = pLine.toCharArray();
- int len = chars.length;
-
- while (len > 0) {
- if (!Character.isWhitespace(chars[len - 1])) {
- break;
- }
- len--;
- }
-
- final byte[] data = pLine.substring(0, len).getBytes("UTF-8");
-
- if (pArmoredOutput != null) {
- pArmoredOutput.write(data);
- }
- pSignatureGenerator.update(data);
- }
-
- // taken from ClearSignedFileProcessor in BC
- 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 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 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;
- }
-
- public static boolean isReleaseVersion(Context context) {
- try {
- PackageInfo pi = context.getPackageManager().getPackageInfo(mApgPackageName, 0);
- if (pi.versionCode % 100 == 99) {
- return true;
- } else {
- return false;
- }
- } catch (NameNotFoundException e) {
- // unpossible!
- return false;
- }
- }
-
- public static String getVersion(Context context) {
- if (VERSION != null) {
- return VERSION;
- }
- try {
- PackageInfo pi = context.getPackageManager().getPackageInfo(mApgPackageName, 0);
- VERSION = pi.versionName;
- return VERSION;
- } catch (NameNotFoundException e) {
- // unpossible!
- return "0.0.0";
- }
- }
-
- public static String getFullVersion(Context context) {
- return "APG v" + getVersion(context);
- }
-
- public static String generateRandomString(int length) {
- SecureRandom random = new SecureRandom();
- /*
- * try { random = SecureRandom.getInstance("SHA1PRNG", new BouncyCastleProvider()); } catch
- * (NoSuchAlgorithmException e) { // TODO: need to handle this case somehow return null; }
- */
- byte bytes[] = new byte[length];
- random.nextBytes(bytes);
- String result = "";
- for (int i = 0; i < length; ++i) {
- int v = (bytes[i] + 256) % 64;
- if (v < 10) {
- result += (char) ('0' + v);
- } else if (v < 36) {
- result += (char) ('A' + v - 10);
- } else if (v < 62) {
- result += (char) ('a' + v - 36);
- } else if (v == 62) {
- result += '_';
- } else if (v == 63) {
- result += '.';
- }
- }
- return result;
- }
-
- static long getLengthOfStream(InputStream in) throws IOException {
- long size = 0;
- long n = 0;
- byte dummy[] = new byte[0x10000];
- while ((n = in.read(dummy)) > 0) {
- size += n;
- }
- return size;
- }
-
- public static void deleteFileSecurely(Context context, File file, ProgressDialogUpdater progress)
- throws FileNotFoundException, IOException {
- long length = file.length();
- SecureRandom random = new SecureRandom();
- RandomAccessFile raf = new RandomAccessFile(file, "rws");
- raf.seek(0);
- raf.getFilePointer();
- byte[] data = new byte[1 << 16];
- int pos = 0;
- String msg = context.getString(R.string.progress_deletingSecurely, file.getName());
- while (pos < length) {
- if (progress != null)
- progress.setProgress(msg, (int) (100 * pos / length), 100);
- random.nextBytes(data);
- raf.write(data);
- pos += data.length;
- }
- raf.close();
- file.delete();
- }
-}
diff --git a/src/org/apg/ApgService.java b/src/org/apg/ApgService.java
deleted file mode 100644
index 82e85371d..000000000
--- a/src/org/apg/ApgService.java
+++ /dev/null
@@ -1,643 +0,0 @@
-package org.apg;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-
-import org.apg.provider.KeyRings;
-import org.apg.provider.Keys;
-import org.apg.provider.UserIds;
-import org.apg.IApgService;
-
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-
-public class ApgService extends Service {
- private final static String TAG = "ApgService";
- public static final boolean LOCAL_LOGV = true;
- public static final boolean LOCAL_LOGD = true;
-
- @Override
- public IBinder onBind(Intent intent) {
- if (LOCAL_LOGD)
- Log.d(TAG, "bound");
- return mBinder;
- }
-
- /** error status */
- private static enum error {
- ARGUMENTS_MISSING, APG_FAILURE, NO_MATCHING_SECRET_KEY, PRIVATE_KEY_PASSPHRASE_WRONG, PRIVATE_KEY_PASSPHRASE_MISSING;
-
- public int shiftedOrdinal() {
- return ordinal() + 100;
- }
- }
-
- private static enum call {
- encrypt_with_passphrase, encrypt_with_public_key, decrypt, get_keys
- }
-
- /** all arguments that can be passed by calling application */
- public static enum arg {
- MESSAGE, // message to encrypt or to decrypt
- SYMMETRIC_PASSPHRASE, // key for symmetric en/decryption
- PUBLIC_KEYS, // public keys for encryption
- ENCRYPTION_ALGORYTHM, // encryption algorithm
- HASH_ALGORYTHM, // hash algorithm
- ARMORED_OUTPUT, // whether to armor output
- FORCE_V3_SIGNATURE, // whether to force v3 signature
- COMPRESSION, // what compression to use for encrypted output
- SIGNATURE_KEY, // key for signing
- PRIVATE_KEY_PASSPHRASE, // passphrase for encrypted private key
- KEY_TYPE, // type of key (private or public)
- BLOB, // blob passed
- }
-
- /** all things that might be returned */
- private static enum ret {
- ERRORS, // string array list with errors
- WARNINGS, // string array list with warnings
- ERROR, // numeric error
- RESULT, // en-/decrypted
- FINGERPRINTS, // fingerprints of keys
- USER_IDS, // user ids
- }
-
- /** required arguments for each AIDL function */
- private static final HashMap> FUNCTIONS_REQUIRED_ARGS = new HashMap>();
- static {
- HashSet args = new HashSet();
- args.add(arg.SYMMETRIC_PASSPHRASE);
- FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_passphrase.name(), args);
-
- args = new HashSet();
- args.add(arg.PUBLIC_KEYS);
- FUNCTIONS_REQUIRED_ARGS.put(call.encrypt_with_public_key.name(), args);
-
- args = new HashSet();
- FUNCTIONS_REQUIRED_ARGS.put(call.decrypt.name(), args);
-
- args = new HashSet();
- args.add(arg.KEY_TYPE);
- FUNCTIONS_REQUIRED_ARGS.put(call.get_keys.name(), args);
- }
-
- /** optional arguments for each AIDL function */
- private static final HashMap> FUNCTIONS_OPTIONAL_ARGS = new HashMap>();
- static {
- HashSet args = new HashSet();
- args.add(arg.ENCRYPTION_ALGORYTHM);
- args.add(arg.HASH_ALGORYTHM);
- args.add(arg.ARMORED_OUTPUT);
- args.add(arg.FORCE_V3_SIGNATURE);
- args.add(arg.COMPRESSION);
- args.add(arg.PRIVATE_KEY_PASSPHRASE);
- args.add(arg.SIGNATURE_KEY);
- args.add(arg.BLOB);
- args.add(arg.MESSAGE);
- FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_passphrase.name(), args);
- FUNCTIONS_OPTIONAL_ARGS.put(call.encrypt_with_public_key.name(), args);
-
- args = new HashSet();
- args.add(arg.SYMMETRIC_PASSPHRASE);
- args.add(arg.PUBLIC_KEYS);
- args.add(arg.PRIVATE_KEY_PASSPHRASE);
- args.add(arg.MESSAGE);
- args.add(arg.BLOB);
- FUNCTIONS_OPTIONAL_ARGS.put(call.decrypt.name(), args);
- }
-
- /** a map from ApgService parameters to function calls to get the default */
- private static final HashMap FUNCTIONS_DEFAULTS = new HashMap();
- static {
- FUNCTIONS_DEFAULTS.put(arg.ENCRYPTION_ALGORYTHM, "getDefaultEncryptionAlgorithm");
- FUNCTIONS_DEFAULTS.put(arg.HASH_ALGORYTHM, "getDefaultHashAlgorithm");
- FUNCTIONS_DEFAULTS.put(arg.ARMORED_OUTPUT, "getDefaultAsciiArmour");
- FUNCTIONS_DEFAULTS.put(arg.FORCE_V3_SIGNATURE, "getForceV3Signatures");
- FUNCTIONS_DEFAULTS.put(arg.COMPRESSION, "getDefaultMessageCompression");
- }
-
- /** a map of the default function names to their method */
- private static final HashMap FUNCTIONS_DEFAULTS_METHODS = new HashMap();
- static {
- try {
- FUNCTIONS_DEFAULTS_METHODS.put("getDefaultEncryptionAlgorithm",
- Preferences.class.getMethod("getDefaultEncryptionAlgorithm"));
- FUNCTIONS_DEFAULTS_METHODS.put("getDefaultHashAlgorithm",
- Preferences.class.getMethod("getDefaultHashAlgorithm"));
- FUNCTIONS_DEFAULTS_METHODS.put("getDefaultAsciiArmour",
- Preferences.class.getMethod("getDefaultAsciiArmour"));
- FUNCTIONS_DEFAULTS_METHODS.put("getForceV3Signatures",
- Preferences.class.getMethod("getForceV3Signatures"));
- FUNCTIONS_DEFAULTS_METHODS.put("getDefaultMessageCompression",
- Preferences.class.getMethod("getDefaultMessageCompression"));
- } catch (Exception e) {
- Log.e(TAG, "Function method exception: " + e.getMessage());
- }
- }
-
- private static void writeToOutputStream(InputStream is, OutputStream os) throws IOException {
- byte[] buffer = new byte[8];
- int len = 0;
- while ((len = is.read(buffer)) != -1) {
- os.write(buffer, 0, len);
- }
- }
-
- private static Cursor getKeyEntries(HashMap pParams) {
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "("
- + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
- + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY
- + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + "("
- + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "."
- + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
-
- String orderBy = pParams.containsKey("order_by") ? (String) pParams.get("order_by")
- : UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
-
- String typeVal[] = null;
- String typeWhere = null;
- if (pParams.containsKey("key_type")) {
- typeWhere = KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?";
- typeVal = new String[] { "" + pParams.get("key_type") };
- }
- return qb.query(Apg.getDatabase().db(), (String[]) pParams.get("columns"), typeWhere,
- typeVal, null, null, orderBy);
- }
-
- /**
- * maps a fingerprint or user id of a key to a master key in database
- *
- * @param search_key
- * fingerprint or user id to search for
- * @return master key if found, or 0
- */
- private static long getMasterKey(String pSearchKey, Bundle pReturn) {
- if (pSearchKey == null || pSearchKey.length() != 8) {
- return 0;
- }
- ArrayList keyList = new ArrayList();
- keyList.add(pSearchKey);
- long[] keys = getMasterKey(keyList, pReturn);
- if (keys.length > 0) {
- return keys[0];
- } else {
- return 0;
- }
- }
-
- /**
- * maps fingerprints or user ids of keys to master keys in database
- *
- * @param search_keys
- * a list of keys (fingerprints or user ids) to look for in database
- * @return an array of master keys
- */
- private static long[] getMasterKey(ArrayList pSearchKeys, Bundle pReturn) {
-
- HashMap qParams = new HashMap();
- qParams.put("columns", new String[] { KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
- UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1
- });
- qParams.put("key_type", Id.database.type_public);
-
- Cursor mCursor = getKeyEntries(qParams);
-
- if (LOCAL_LOGV)
- Log.v(TAG, "going through installed user keys");
- ArrayList masterKeys = new ArrayList();
- while (mCursor.moveToNext()) {
- long curMkey = mCursor.getLong(0);
- String curUser = mCursor.getString(1);
-
- String curFprint = Apg.getSmallFingerPrint(curMkey);
- if (LOCAL_LOGV)
- Log.v(TAG, "current user: " + curUser + " (" + curFprint + ")");
- if (pSearchKeys.contains(curFprint) || pSearchKeys.contains(curUser)) {
- if (LOCAL_LOGV)
- Log.v(TAG, "master key found for: " + curFprint);
- masterKeys.add(curMkey);
- pSearchKeys.remove(curFprint);
- } else {
- if (LOCAL_LOGV)
- Log.v(TAG, "Installed key " + curFprint
- + " is not in the list of public keys to encrypt with");
- }
- }
- mCursor.close();
-
- long[] masterKeyLongs = new long[masterKeys.size()];
- int i = 0;
- for (Long key : masterKeys) {
- masterKeyLongs[i++] = key;
- }
-
- if (i == 0) {
- Log.w(TAG, "Found not one public key");
- pReturn.getStringArrayList(ret.WARNINGS.name()).add(
- "Searched for public key(s) but found not one");
- }
-
- for (String key : pSearchKeys) {
- Log.w(TAG, "Searched for key " + key + " but cannot find it in APG");
- pReturn.getStringArrayList(ret.WARNINGS.name()).add(
- "Searched for key " + key + " but cannot find it in APG");
- }
-
- return masterKeyLongs;
- }
-
- /**
- * Add default arguments if missing
- *
- * @param args
- * the bundle to add default parameters to if missing
- */
- private void addDefaultArguments(String pCall, Bundle pArgs) {
- // check whether there are optional elements defined for that call
- if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pCall)) {
- Preferences preferences = Preferences.getPreferences(getBaseContext(), true);
-
- Iterator iter = FUNCTIONS_DEFAULTS.keySet().iterator();
- while (iter.hasNext()) {
- arg currentArg = iter.next();
- String currentKey = currentArg.name();
- if (!pArgs.containsKey(currentKey)
- && FUNCTIONS_OPTIONAL_ARGS.get(pCall).contains(currentArg)) {
- String currentFunctionName = FUNCTIONS_DEFAULTS.get(currentArg);
- try {
- Class> returnType = FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
- .getReturnType();
- if (returnType == String.class) {
- pArgs.putString(currentKey,
- (String) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
- .invoke(preferences));
- } else if (returnType == boolean.class) {
- pArgs.putBoolean(currentKey,
- (Boolean) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
- .invoke(preferences));
- } else if (returnType == int.class) {
- pArgs.putInt(currentKey,
- (Integer) FUNCTIONS_DEFAULTS_METHODS.get(currentFunctionName)
- .invoke(preferences));
- } else {
- Log.e(TAG, "Unknown return type " + returnType.toString()
- + " for default option");
- }
- } catch (Exception e) {
- Log.e(TAG, "Exception in add_default_arguments " + e.getMessage());
- }
- }
- }
- }
- }
-
- /**
- * updates a Bundle with default return values
- *
- * @param pReturn
- * the Bundle to update
- */
- private void addDefaultReturns(Bundle pReturn) {
- ArrayList errors = new ArrayList();
- ArrayList warnings = new ArrayList();
-
- pReturn.putStringArrayList(ret.ERRORS.name(), errors);
- pReturn.putStringArrayList(ret.WARNINGS.name(), warnings);
- }
-
- /**
- * checks for required arguments and adds them to the error if missing
- *
- * @param function
- * the functions required arguments to check for
- * @param pArgs
- * the Bundle of arguments to check
- * @param pReturn
- * the bundle to write errors to
- */
- private void checkForRequiredArgs(String pFunction, Bundle pArgs, Bundle pReturn) {
- if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) {
- Iterator iter = FUNCTIONS_REQUIRED_ARGS.get(pFunction).iterator();
- while (iter.hasNext()) {
- String curArg = iter.next().name();
- if (!pArgs.containsKey(curArg)) {
- pReturn.getStringArrayList(ret.ERRORS.name())
- .add("Argument missing: " + curArg);
- }
- }
- }
-
- if (pFunction.equals(call.encrypt_with_passphrase.name())
- || pFunction.equals(call.encrypt_with_public_key.name())
- || pFunction.equals(call.decrypt.name())) {
- // check that either MESSAGE or BLOB are there
- if (!pArgs.containsKey(arg.MESSAGE.name()) && !pArgs.containsKey(arg.BLOB.name())) {
- pReturn.getStringArrayList(ret.ERRORS.name()).add(
- "Arguments missing: Neither MESSAGE nor BLOG found");
- }
-
- }
- }
-
- /**
- * checks for unknown arguments and add them to warning if found
- *
- * @param function
- * the functions name to check against
- * @param pArgs
- * the Bundle of arguments to check
- * @param pReturn
- * the bundle to write warnings to
- */
- private void checkForUnknownArgs(String pFunction, Bundle pArgs, Bundle pReturn) {
-
- HashSet allArgs = new HashSet();
- if (FUNCTIONS_REQUIRED_ARGS.containsKey(pFunction)) {
- allArgs.addAll(FUNCTIONS_REQUIRED_ARGS.get(pFunction));
- }
- if (FUNCTIONS_OPTIONAL_ARGS.containsKey(pFunction)) {
- allArgs.addAll(FUNCTIONS_OPTIONAL_ARGS.get(pFunction));
- }
-
- ArrayList unknownArgs = new ArrayList();
- Iterator iter = pArgs.keySet().iterator();
- while (iter.hasNext()) {
- String curKey = iter.next();
- try {
- arg curArg = arg.valueOf(curKey);
- if (!allArgs.contains(curArg)) {
- pReturn.getStringArrayList(ret.WARNINGS.name()).add(
- "Unknown argument: " + curKey);
- unknownArgs.add(curKey);
- }
- } catch (Exception e) {
- pReturn.getStringArrayList(ret.WARNINGS.name()).add("Unknown argument: " + curKey);
- unknownArgs.add(curKey);
- }
- }
-
- // remove unknown arguments so our bundle has just what we need
- for (String arg : unknownArgs) {
- pArgs.remove(arg);
- }
- }
-
- private boolean prepareArgs(String pCall, Bundle pArgs, Bundle pReturn) {
- Apg.initialize(getBaseContext());
-
- /* add default return values for all functions */
- addDefaultReturns(pReturn);
-
- /* add default arguments if missing */
- addDefaultArguments(pCall, pArgs);
- if (LOCAL_LOGV)
- Log.v(TAG, "add_default_arguments");
-
- /* check for required arguments */
- checkForRequiredArgs(pCall, pArgs, pReturn);
- if (LOCAL_LOGV)
- Log.v(TAG, "check_required_args");
-
- /* check for unknown arguments and add to warning if found */
- checkForUnknownArgs(pCall, pArgs, pReturn);
- if (LOCAL_LOGV)
- Log.v(TAG, "check_unknown_args");
-
- /* return if errors happened */
- if (pReturn.getStringArrayList(ret.ERRORS.name()).size() != 0) {
- if (LOCAL_LOGV)
- Log.v(TAG, "Errors after preparing, not executing " + pCall);
- pReturn.putInt(ret.ERROR.name(), error.ARGUMENTS_MISSING.shiftedOrdinal());
- return false;
- }
- if (LOCAL_LOGV)
- Log.v(TAG, "error return");
-
- return true;
- }
-
- private boolean encrypt(Bundle pArgs, Bundle pReturn) {
- boolean isBlob = pArgs.containsKey(arg.BLOB.name());
-
- long pubMasterKeys[] = {};
- if (pArgs.containsKey(arg.PUBLIC_KEYS.name())) {
- ArrayList list = pArgs.getStringArrayList(arg.PUBLIC_KEYS.name());
- ArrayList pubKeys = new ArrayList();
- if (LOCAL_LOGV)
- Log.v(TAG, "Long size: " + list.size());
- Iterator iter = list.iterator();
- while (iter.hasNext()) {
- pubKeys.add(iter.next());
- }
- pubMasterKeys = getMasterKey(pubKeys, pReturn);
- }
-
- InputStream inStream = null;
- if (isBlob) {
- ContentResolver cr = getContentResolver();
- try {
- inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
- } catch (Exception e) {
- Log.e(TAG, "... exception on opening blob", e);
- }
- } else {
- inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
- }
- InputData in = new InputData(inStream, 0); // XXX Size second param?
-
- OutputStream out = new ByteArrayOutputStream();
- if (LOCAL_LOGV)
- Log.v(TAG, "About to encrypt");
- try {
- Apg.encrypt(getBaseContext(), // context
- in, // input stream
- out, // output stream
- pArgs.getBoolean(arg.ARMORED_OUTPUT.name()), // ARMORED_OUTPUT
- pubMasterKeys, // encryption keys
- getMasterKey(pArgs.getString(arg.SIGNATURE_KEY.name()), pReturn), // signature
- // key
- pArgs.getString(arg.PRIVATE_KEY_PASSPHRASE.name()), // signature passphrase
- null, // progress
- pArgs.getInt(arg.ENCRYPTION_ALGORYTHM.name()), // encryption
- pArgs.getInt(arg.HASH_ALGORYTHM.name()), // hash
- pArgs.getInt(arg.COMPRESSION.name()), // compression
- pArgs.getBoolean(arg.FORCE_V3_SIGNATURE.name()), // mPreferences.getForceV3Signatures(),
- pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) // passPhrase
- );
- } catch (Exception e) {
- Log.e(TAG, "Exception in encrypt");
- String msg = e.getMessage();
- if (msg.equals(getBaseContext().getString(R.string.error_noSignaturePassPhrase))) {
- pReturn.getStringArrayList(ret.ERRORS.name()).add(
- "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name() + " missing): "
- + msg);
- pReturn.putInt(ret.ERROR.name(),
- error.PRIVATE_KEY_PASSPHRASE_MISSING.shiftedOrdinal());
- } else if (msg.equals(getBaseContext().getString(
- R.string.error_couldNotExtractPrivateKey))) {
- pReturn.getStringArrayList(ret.ERRORS.name()).add(
- "Cannot encrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
- + " probably wrong): " + msg);
- pReturn.putInt(ret.ERROR.name(),
- error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
- } else {
- pReturn.getStringArrayList(ret.ERRORS.name()).add(
- "Internal failure (" + e.getClass() + ") in APG when encrypting: "
- + e.getMessage());
- pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
- }
- return false;
- }
- if (LOCAL_LOGV)
- Log.v(TAG, "Encrypted");
- if (isBlob) {
- ContentResolver cr = getContentResolver();
- try {
- OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
- .name())));
- writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()), outStream);
- outStream.close();
- } catch (Exception e) {
- Log.e(TAG, "... exception on writing blob", e);
- }
- } else {
- pReturn.putString(ret.RESULT.name(), out.toString());
- }
- return true;
- }
-
- private final IApgService.Stub mBinder = new IApgService.Stub() {
-
- public boolean getKeys(Bundle pArgs, Bundle pReturn) {
-
- prepareArgs("get_keys", pArgs, pReturn);
-
- HashMap qParams = new HashMap();
- qParams.put("columns", new String[] {
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 0
- UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 1
- });
-
- qParams.put("key_type", pArgs.getInt(arg.KEY_TYPE.name()));
-
- Cursor cursor = getKeyEntries(qParams);
- ArrayList fPrints = new ArrayList();
- ArrayList ids = new ArrayList();
- while (cursor.moveToNext()) {
- if (LOCAL_LOGV)
- Log.v(TAG, "adding key " + Apg.getSmallFingerPrint(cursor.getLong(0)));
- fPrints.add(Apg.getSmallFingerPrint(cursor.getLong(0)));
- ids.add(cursor.getString(1));
- }
- cursor.close();
-
- pReturn.putStringArrayList(ret.FINGERPRINTS.name(), fPrints);
- pReturn.putStringArrayList(ret.USER_IDS.name(), ids);
- return true;
- }
-
- public boolean encryptWithPublicKey(Bundle pArgs, Bundle pReturn) {
- if (!prepareArgs("encrypt_with_public_key", pArgs, pReturn)) {
- return false;
- }
-
- return encrypt(pArgs, pReturn);
- }
-
- public boolean encryptWithPassphrase(Bundle pArgs, Bundle pReturn) {
- if (!prepareArgs("encrypt_with_passphrase", pArgs, pReturn)) {
- return false;
- }
-
- return encrypt(pArgs, pReturn);
-
- }
-
- public boolean decrypt(Bundle pArgs, Bundle pReturn) {
- if (!prepareArgs("decrypt", pArgs, pReturn)) {
- return false;
- }
-
- boolean isBlob = pArgs.containsKey(arg.BLOB.name());
-
- String passphrase = pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null ? pArgs
- .getString(arg.SYMMETRIC_PASSPHRASE.name()) : pArgs
- .getString(arg.PRIVATE_KEY_PASSPHRASE.name());
-
- InputStream inStream = null;
- if (isBlob) {
- ContentResolver cr = getContentResolver();
- try {
- inStream = cr.openInputStream(Uri.parse(pArgs.getString(arg.BLOB.name())));
- } catch (Exception e) {
- Log.e(TAG, "... exception on opening blob", e);
- }
- } else {
- inStream = new ByteArrayInputStream(pArgs.getString(arg.MESSAGE.name()).getBytes());
- }
-
- InputData in = new InputData(inStream, 0); // XXX what size in second parameter?
- OutputStream out = new ByteArrayOutputStream();
- if (LOCAL_LOGV)
- Log.v(TAG, "About to decrypt");
- try {
- Apg.decrypt(getBaseContext(), in, out, passphrase, null, // progress
- pArgs.getString(arg.SYMMETRIC_PASSPHRASE.name()) != null // symmetric
- );
- } catch (Exception e) {
- Log.e(TAG, "Exception in decrypt");
- String msg = e.getMessage();
- if (msg.equals(getBaseContext().getString(R.string.error_noSecretKeyFound))) {
- pReturn.getStringArrayList(ret.ERRORS.name()).add("Cannot decrypt: " + msg);
- pReturn.putInt(ret.ERROR.name(), error.NO_MATCHING_SECRET_KEY.shiftedOrdinal());
- } else if (msg.equals(getBaseContext().getString(R.string.error_wrongPassPhrase))) {
- pReturn.getStringArrayList(ret.ERRORS.name()).add(
- "Cannot decrypt (" + arg.PRIVATE_KEY_PASSPHRASE.name()
- + " wrong/missing): " + msg);
- pReturn.putInt(ret.ERROR.name(),
- error.PRIVATE_KEY_PASSPHRASE_WRONG.shiftedOrdinal());
- } else {
- pReturn.getStringArrayList(ret.ERRORS.name()).add(
- "Internal failure (" + e.getClass() + ") in APG when decrypting: "
- + msg);
- pReturn.putInt(ret.ERROR.name(), error.APG_FAILURE.shiftedOrdinal());
- }
- return false;
- }
- if (LOCAL_LOGV)
- Log.v(TAG, "... decrypted");
-
- if (isBlob) {
- ContentResolver cr = getContentResolver();
- try {
- OutputStream outStream = cr.openOutputStream(Uri.parse(pArgs.getString(arg.BLOB
- .name())));
- writeToOutputStream(new ByteArrayInputStream(out.toString().getBytes()),
- outStream);
- outStream.close();
- } catch (Exception e) {
- Log.e(TAG, "... exception on writing blob", e);
- }
- } else {
- pReturn.putString(ret.RESULT.name(), out.toString());
- }
- return true;
- }
-
- };
-}
diff --git a/src/org/apg/AskForSecretKeyPassPhrase.java b/src/org/apg/AskForSecretKeyPassPhrase.java
deleted file mode 100644
index ebed75667..000000000
--- a/src/org/apg/AskForSecretKeyPassPhrase.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg;
-
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.apg.R;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.EditText;
-import android.widget.Toast;
-
-public class AskForSecretKeyPassPhrase {
- public static interface PassPhraseCallbackInterface {
- void passPhraseCallback(long keyId, String passPhrase);
- }
-
- public static Dialog createDialog(Activity context, long secretKeyId,
- PassPhraseCallbackInterface callback) {
- AlertDialog.Builder alert = new AlertDialog.Builder(context);
-
- alert.setTitle(R.string.title_authentication);
-
- final PGPSecretKey secretKey;
- final Activity activity = context;
-
- if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) {
- secretKey = null;
- alert.setMessage(context.getString(R.string.passPhraseForSymmetricEncryption));
- } else {
- secretKey = Apg.getMasterKey(Apg.getSecretKeyRing(secretKeyId));
- if (secretKey == null) {
- alert.setTitle(R.string.title_keyNotFound);
- alert.setMessage(context.getString(R.string.keyNotFound, secretKeyId));
- alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- activity.removeDialog(Id.dialog.pass_phrase);
- }
- });
- alert.setCancelable(false);
- return alert.create();
- }
- String userId = Apg.getMainUserIdSafe(context, secretKey);
- alert.setMessage(context.getString(R.string.passPhraseFor, userId));
- }
-
- 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;
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- activity.removeDialog(Id.dialog.pass_phrase);
- String passPhrase = "" + input.getText();
- long keyId;
- if (secretKey != null) {
- try {
- PGPPrivateKey testKey = secretKey.extractPrivateKey(
- passPhrase.toCharArray(), new BouncyCastleProvider());
- if (testKey == null) {
- Toast.makeText(activity, R.string.error_couldNotExtractPrivateKey,
- Toast.LENGTH_SHORT).show();
- return;
- }
- } catch (PGPException e) {
- Toast.makeText(activity, R.string.wrongPassPhrase, Toast.LENGTH_SHORT)
- .show();
- return;
- }
- keyId = secretKey.getKeyID();
- } else {
- keyId = Id.key.symmetric;
- }
- cb.passPhraseCallback(keyId, passPhrase);
- }
- });
-
- alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- activity.removeDialog(Id.dialog.pass_phrase);
- }
- });
-
- return alert.create();
- }
-}
diff --git a/src/org/apg/CachedPassPhrase.java b/src/org/apg/CachedPassPhrase.java
deleted file mode 100644
index 2d67a300d..000000000
--- a/src/org/apg/CachedPassPhrase.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.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;
- }
-
- @Override
- public int hashCode() {
- int hc1 = (int) (this.timestamp & 0xffffffff);
- int hc2 = (this.passPhrase == null ? 0 : this.passPhrase.hashCode());
- return (hc1 + hc2) * hc2 + hc1;
- }
-
- @Override
- 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;
- }
-
- @Override
- public String toString() {
- return "(" + timestamp + ", *******)";
- }
-}
diff --git a/src/org/apg/Constants.java b/src/org/apg/Constants.java
deleted file mode 100644
index 9669d4b0d..000000000
--- a/src/org/apg/Constants.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg;
-
-import android.os.Environment;
-
-public final class Constants {
-
- public static final String TAG = "APG";
-
- 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_HELP = "seenHelp";
- public static final String HAS_SEEN_CHANGE_LOG = "seenChangeLogDialog";
- 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";
- public static final String LANGUAGE = "language";
- public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";
- public static final String KEY_SERVERS = "keyServers";
- }
-
- public static final class defaults {
- public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu";
- }
-
- public static final class extras {
- public static final String PROGRESS = "progress";
- public static final String PROGRESS_MAX = "max";
- public static final String STATUS = "status";
- public static final String MESSAGE = "message";
- public static final String KEY_ID = "keyId";
- }
-}
diff --git a/src/org/apg/DataDestination.java b/src/org/apg/DataDestination.java
deleted file mode 100644
index 509670e69..000000000
--- a/src/org/apg/DataDestination.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package org.apg;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import org.apg.Apg.GeneralException;
-import org.apg.R;
-
-import android.content.Context;
-import android.os.Environment;
-
-public class DataDestination {
- private String mStreamFilename;
- private String mFilename;
- private int mMode = Id.mode.undefined;
-
- public DataDestination() {
-
- }
-
- public void setMode(int mode) {
- mMode = mode;
- }
-
- public void setFilename(String filename) {
- mFilename = filename;
- }
-
- public String getStreamFilename() {
- return mStreamFilename;
- }
-
- public OutputStream getOutputStream(Context context) throws Apg.GeneralException,
- FileNotFoundException, IOException {
- OutputStream out = null;
- mStreamFilename = null;
-
- switch (mMode) {
- case Id.mode.stream: {
- try {
- while (true) {
- mStreamFilename = Apg.generateRandomString(32);
- if (mStreamFilename == null) {
- throw new Apg.GeneralException("couldn't generate random file name");
- }
- context.openFileInput(mStreamFilename).close();
- }
- } catch (FileNotFoundException e) {
- // found a name that isn't used yet
- }
- out = context.openFileOutput(mStreamFilename, Context.MODE_PRIVATE);
- break;
- }
-
- case Id.mode.byte_array: {
- out = new ByteArrayOutputStream();
- break;
- }
-
- case Id.mode.file: {
- if (mFilename.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(
- context.getString(R.string.error_externalStorageNotReady));
- }
- }
- out = new FileOutputStream(mFilename);
- break;
- }
-
- default: {
- break;
- }
- }
-
- return out;
- }
-}
diff --git a/src/org/apg/DataSource.java b/src/org/apg/DataSource.java
deleted file mode 100644
index 340afa9f8..000000000
--- a/src/org/apg/DataSource.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package org.apg;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apg.Apg.GeneralException;
-import org.apg.R;
-
-import android.content.Context;
-import android.net.Uri;
-import android.os.Environment;
-
-public class DataSource {
- private Uri mContentUri = null;
- private String mText = null;
- private byte[] mData = null;
-
- public DataSource() {
-
- }
-
- public void setUri(Uri uri) {
- mContentUri = uri;
- mText = null;
- mData = null;
- }
-
- public void setUri(String uri) {
- if (uri.startsWith("/")) {
- setUri(Uri.parse("file://" + uri));
- } else {
- setUri(Uri.parse(uri));
- }
- }
-
- public void setText(String text) {
- mText = text;
- mData = null;
- mContentUri = null;
- }
-
- public void setData(byte[] data) {
- mData = data;
- mText = null;
- mContentUri = null;
- }
-
- public boolean isText() {
- return mText != null;
- }
-
- public boolean isBinary() {
- return mData != null || mContentUri != null;
- }
-
- public InputData getInputData(Context context, boolean withSize) throws GeneralException,
- FileNotFoundException, IOException {
- InputStream in = null;
- long size = 0;
-
- if (mContentUri != null) {
- if (mContentUri.getScheme().equals("file")) {
- // get the rest after "file://"
- String path = Uri.decode(mContentUri.toString().substring(7));
- if (path.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) {
- if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- throw new GeneralException(
- context.getString(R.string.error_externalStorageNotReady));
- }
- }
- in = new FileInputStream(path);
- File file = new File(path);
- if (withSize) {
- size = file.length();
- }
- } else {
- in = context.getContentResolver().openInputStream(mContentUri);
- if (withSize) {
- InputStream tmp = context.getContentResolver().openInputStream(mContentUri);
- size = Apg.getLengthOfStream(tmp);
- tmp.close();
- }
- }
- } else if (mText != null || mData != null) {
- byte[] bytes = null;
- if (mData != null) {
- bytes = mData;
- } else {
- bytes = mText.getBytes();
- }
- in = new ByteArrayInputStream(bytes);
- if (withSize) {
- size = bytes.length;
- }
- }
-
- return new InputData(in, size);
- }
-
-}
diff --git a/src/org/apg/FileDialog.java b/src/org/apg/FileDialog.java
deleted file mode 100644
index 6ad498b90..000000000
--- a/src/org/apg/FileDialog.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg;
-
-import org.apg.R;
-
-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.CheckBox;
-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 CheckBox mCheckBox;
- 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, boolean checkbox);
- }
-
- public static AlertDialog build(Activity activity, String title, String message,
- String defaultFile, OnClickListener onClickListener, String fileManagerTitle,
- String fileManagerButton, String checkboxText, int requestCode) {
- // TODO: fileManagerTitle and fileManagerButton are deprecated, no use for them right now,
- // but maybe the Intent now used will someday support them again, so leaving them in
- LayoutInflater inflater = (LayoutInflater) activity
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- AlertDialog.Builder alert = new AlertDialog.Builder(activity);
-
- alert.setTitle(title);
- alert.setMessage(message);
-
- 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() {
- public void onClick(View v) {
- openFile();
- }
- });
- mFileManagerTitle = fileManagerTitle;
- mFileManagerButton = fileManagerButton;
- mRequestCode = requestCode;
- mCheckBox = (CheckBox) view.findViewById(R.id.checkbox);
- if (checkboxText == null) {
- mCheckBox.setEnabled(false);
- mCheckBox.setVisibility(View.GONE);
- } else {
- mCheckBox.setEnabled(true);
- mCheckBox.setVisibility(View.VISIBLE);
- mCheckBox.setText(checkboxText);
- }
-
- alert.setView(view);
-
- final OnClickListener clickListener = onClickListener;
-
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- boolean checked = false;
- if (mCheckBox.isEnabled()) {
- checked = mCheckBox.isChecked();
- }
- clickListener.onOkClick(mFilename.getText().toString(), checked);
- }
- });
-
- 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(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
-
- intent.setData(Uri.parse("file://" + filename));
- intent.setType("*/*");
-
- try {
- mActivity.startActivityForResult(intent, mRequestCode);
- } catch (ActivityNotFoundException e) {
- // No compatible file manager was found.
- Toast.makeText(mActivity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show();
- }
- }
-}
diff --git a/src/org/apg/HkpKeyServer.java b/src/org/apg/HkpKeyServer.java
deleted file mode 100644
index 294a60cb2..000000000
--- a/src/org/apg/HkpKeyServer.java
+++ /dev/null
@@ -1,242 +0,0 @@
-package org.apg;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.HttpURLConnection;
-import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.Vector;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.message.BasicNameValuePair;
-import org.apache.http.util.EntityUtils;
-
-import android.text.Html;
-
-public class HkpKeyServer extends KeyServer {
- private static class HttpError extends Exception {
- private static final long serialVersionUID = 1718783705229428893L;
- private int mCode;
- private String mData;
-
- public HttpError(int code, String data) {
- super("" + code + ": " + data);
- mCode = code;
- mData = data;
- }
-
- public int getCode() {
- return mCode;
- }
-
- public String getData() {
- return mData;
- }
- }
-
- private String mHost;
- private short mPort = 11371;
-
- // example:
- // pub 2048R/9F5C9090 2009-08-17 Jörg Runge
- // <joerg@joergrunge.de>
- public static Pattern PUB_KEY_LINE = Pattern
- .compile(
- "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
- Pattern.CASE_INSENSITIVE);
- public static Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE
- | Pattern.CASE_INSENSITIVE);
-
- public HkpKeyServer(String host) {
- mHost = host;
- }
-
- public HkpKeyServer(String host, short port) {
- mHost = host;
- mPort = port;
- }
-
- static private String readAll(InputStream in, String encoding) throws IOException {
- ByteArrayOutputStream raw = new ByteArrayOutputStream();
-
- byte buffer[] = new byte[1 << 16];
- int n = 0;
- while ((n = in.read(buffer)) != -1) {
- raw.write(buffer, 0, n);
- }
-
- if (encoding == null) {
- encoding = "utf8";
- }
- return raw.toString(encoding);
- }
-
- // TODO: replace this with httpclient
- private String query(String request) throws QueryException, HttpError {
- InetAddress ips[];
- try {
- ips = InetAddress.getAllByName(mHost);
- } catch (UnknownHostException e) {
- throw new QueryException(e.toString());
- }
- for (int i = 0; i < ips.length; ++i) {
- try {
- String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request;
- URL realUrl = new URL(url);
- HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
- conn.setConnectTimeout(5000);
- conn.setReadTimeout(25000);
- conn.connect();
- int response = conn.getResponseCode();
- if (response >= 200 && response < 300) {
- return readAll(conn.getInputStream(), conn.getContentEncoding());
- } else {
- String data = readAll(conn.getErrorStream(), conn.getContentEncoding());
- throw new HttpError(response, data);
- }
- } catch (MalformedURLException e) {
- // nothing to do, try next IP
- } catch (IOException e) {
- // nothing to do, try next IP
- }
- }
-
- throw new QueryException("querying server(s) for '" + mHost + "' failed");
- }
-
- // TODO: replace this with httpclient
- @Override
- public List search(String query) throws QueryException, TooManyResponses,
- InsufficientQuery {
- Vector results = new Vector();
-
- if (query.length() < 3) {
- throw new InsufficientQuery();
- }
-
- String encodedQuery;
- try {
- encodedQuery = URLEncoder.encode(query, "utf8");
- } catch (UnsupportedEncodingException e) {
- return null;
- }
- String request = "/pks/lookup?op=index&search=" + encodedQuery;
-
- String data = null;
- try {
- data = query(request);
- } catch (HttpError e) {
- if (e.getCode() == 404) {
- return results;
- } else {
- if (e.getData().toLowerCase().contains("no keys found")) {
- return results;
- } else if (e.getData().toLowerCase().contains("too many")) {
- throw new TooManyResponses();
- } else if (e.getData().toLowerCase().contains("insufficient")) {
- throw new InsufficientQuery();
- }
- }
- throw new QueryException("querying server(s) for '" + mHost + "' failed");
- }
-
- Matcher matcher = PUB_KEY_LINE.matcher(data);
- while (matcher.find()) {
- KeyInfo info = new KeyInfo();
- info.size = Integer.parseInt(matcher.group(1));
- info.algorithm = matcher.group(2);
- info.keyId = Apg.keyFromHex(matcher.group(3));
- info.fingerPrint = Apg.getSmallFingerPrint(info.keyId);
- String chunks[] = matcher.group(4).split("-");
- info.date = new GregorianCalendar(Integer.parseInt(chunks[0]),
- Integer.parseInt(chunks[1]), Integer.parseInt(chunks[2])).getTime();
- info.userIds = new Vector();
- if (matcher.group(5).startsWith("*** KEY")) {
- info.revoked = matcher.group(5);
- } else {
- String tmp = matcher.group(5).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
- }
- if (matcher.group(6).length() > 0) {
- Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6));
- while (matcher2.find()) {
- String tmp = matcher2.group(1).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
- }
- }
- results.add(info);
- }
-
- return results;
- }
-
- @Override
- public String get(long keyId) throws QueryException {
- HttpClient client = new DefaultHttpClient();
- try {
- HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
- + "/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId));
-
- HttpResponse response = client.execute(get);
- if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
- throw new QueryException("not found");
- }
-
- HttpEntity entity = response.getEntity();
- InputStream is = entity.getContent();
- String data = readAll(is, EntityUtils.getContentCharSet(entity));
- Matcher matcher = Apg.PGP_PUBLIC_KEY.matcher(data);
- if (matcher.find()) {
- return matcher.group(1);
- }
- } catch (IOException e) {
- // nothing to do, better luck on the next keyserver
- } finally {
- client.getConnectionManager().shutdown();
- }
-
- return null;
- }
-
- @Override
- void add(String armouredText) throws AddKeyException {
- HttpClient client = new DefaultHttpClient();
- try {
- HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add");
-
- List nameValuePairs = new ArrayList(2);
- nameValuePairs.add(new BasicNameValuePair("keytext", armouredText));
- post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
-
- HttpResponse response = client.execute(post);
- if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
- throw new AddKeyException();
- }
- } catch (IOException e) {
- // nothing to do, better luck on the next keyserver
- } finally {
- client.getConnectionManager().shutdown();
- }
- }
-}
diff --git a/src/org/apg/IApgService.aidl b/src/org/apg/IApgService.aidl
deleted file mode 100644
index 8101bd2a4..000000000
--- a/src/org/apg/IApgService.aidl
+++ /dev/null
@@ -1,125 +0,0 @@
-package org.apg;
-
-interface IApgService {
-
- /* All functions fill the returnVals Bundle with the following keys:
- *
- * ArrayList "WARNINGS" = Warnings, if any
- * ArrayList "ERRORS" = Human readable error descriptions, if any
- * int "ERROR" = Numeric representation of error, if any
- * starting with 100:
- * 100: Required argument missing
- * 101: Generic failure of APG
- * 102: No matching private key found
- * 103: Private key's passphrase wrong
- * 104: Private key's passphrase missing
- */
-
- /* ********************************************************
- * Encryption
- * ********************************************************/
-
- /* All encryption function's arguments
- *
- * Bundle params' keys:
- * (optional/required)
- * TYPE "STRING KEY" = EXPLANATION / VALUES
- *
- * (required)
- * String "MESSAGE" = Message to encrypt
- * OR
- * String "BLOB" = ContentUri to a file handle
- * with binary data to encrypt
- * (Attention: file will be overwritten
- * with encrypted content!)
- *
- * (optional)
- * int "ENCRYPTION_ALGORYTHM" = Encryption Algorithm
- * 7: AES-128, 8: AES-192, 9: AES-256,
- * 4: Blowfish, 10: Twofish, 3: CAST5,
- * 6: DES, 2: Triple DES, 1: IDEA
- * (optional)
- * int "HASH_ALGORYTHM" = Hash Algorithm
- * 1: MD5, 3: RIPEMD-160, 2: SHA-1,
- * 11: SHA-224, 8: SHA-256, 9: SHA-384,
- * 10: SHA-512
- * (optional)
- * Boolean "ARMORED_OUTPUT" = Armor output
- *
- * (optional)
- * Boolean "FORCE_V3_SIGNATURE" = Force V3 Signatures
- *
- * (optional)
- * int "COMPRESSION" = Compression to use
- * 0x21070001: none, 1: Zip, 2: Zlib,
- * 3: BZip2
- * (optional)
- * String "SIGNATURE_KEY" = Key to sign with
- *
- * (optional)
- * String "PRIVATE_KEY_PASSPHRASE" = Passphrase for signing key
- *
- * Bundle returnVals (in addition to the ERRORS/WARNINGS above):
- * If "MESSAGE" was set:
- * String "RESULT" = Encrypted message
- */
-
- /* Additional argument for function below:
- * (required)
- * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase to use
- */
- boolean encryptWithPassphrase(in Bundle params, out Bundle returnVals);
-
- /* Additional argument:
- * (required)
- * ArrayList "PUBLIC_KEYS" = Public keys (8char fingerprint "123ABC12" OR
- * complete id "Alice Meyer ")
- */
- boolean encryptWithPublicKey(in Bundle params, out Bundle returnVals);
-
- /* ********************************************************
- * Decryption
- * ********************************************************/
-
- /* Bundle params:
- * (required)
- * String "MESSAGE" = Message to dencrypt
- * OR
- * String "BLOB" = ContentUri to a file handle
- * with binary data to dencrypt
- * (Attention: file will be overwritten
- * with dencrypted content!)
- *
- * (optional)
- * String "SYMMETRIC_PASSPHRASE" = Symmetric passphrase for decryption
- *
- * (optional)
- * String "PRIVATE_KEY_PASSPHRASE" = Private keys's passphrase on asymmetric encryption
- *
- * Bundle return_vals:
- * If "MESSAGE" was set:
- * String "RESULT" = Decrypted message
- */
- boolean decrypt(in Bundle params, out Bundle returnVals);
-
- /* ********************************************************
- * Get key information
- * ********************************************************/
-
- /* Get info about all available keys
- *
- * Bundle params:
- * (required)
- * int "KEY_TYPE" = info about what type of keys to return
- * 0: public keys
- * 1: private keys
- *
- * Returns:
- * StringArrayList "FINGERPRINTS" = Short fingerprints of keys
- *
- * StringArrayList "USER_IDS" = User ids of corresponding fingerprints
- * (order is the same as in FINGERPRINTS)
- */
- boolean getKeys(in Bundle params, out Bundle returnVals);
-
-}
\ No newline at end of file
diff --git a/src/org/apg/Id.java b/src/org/apg/Id.java
deleted file mode 100644
index adcad0534..000000000
--- a/src/org/apg/Id.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg;
-
-import org.spongycastle.bcpg.CompressionAlgorithmTags;
-
-public final class Id {
-
- public static final String TAG = "APG";
-
- 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 int update = 0x21070004;
- public static final int exportToServer = 0x21070005;
- public static final int share = 0x21070006;
- public static final int signKey = 0x21070007;
-
- 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 int search = 0x21070009;
- public static final int help = 0x21070010;
- public static final int key_server = 0x21070011;
- public static final int scanQRCode = 0x21070012;
- }
- }
-
- 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 int delete_done = 0x21070009;
- public static final int query_done = 0x21070010;
- public static final int unknown_signature_key = 0x21070011;
- }
-
- 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 int key_server_preference = 0x21070005;
- public static final int look_up_key_id = 0x21070006;
- public static final int export_to_server = 0x21070007;
- public static final int import_from_qr_code = 0x21070008;
- public static final int sign_key = 0x21070009;
- }
-
- 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 int deleting = 0x21070013;
- public static final int help = 0x21070014;
- public static final int querying = 0x21070015;
- public static final int lookup_unknown_key = 0x21070016;
- public static final int signing = 0x21070017;
- }
-
- public static final class task {
- public static final int import_keys = 0x21070001;
- public static final int export_keys = 0x21070002;
- }
-
- public static final class database {
- public static final int type_public = 0;
- public static final int type_secret = 1;
- }
-
- public static final class type {
- public static final int public_key = 0x21070001;
- public static final int secret_key = 0x21070002;
- 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 action {
- public static final int encrypt = 0x21070001;
- public static final int decrypt = 0x21070002;
- public static final int import_public = 0x21070003;
- public static final int import_secret = 0x21070004;
- }
- }
-
- 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 int bad = -3;
- }
-
- 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 mode {
- public static final int undefined = 0x21070001;
- public static final int byte_array = 0x21070002;
- public static final int file = 0x21070003;
- public static final int stream = 0x21070004;
- }
-
- public static final class key {
- public static final int none = 0;
- public static final int symmetric = -1;
- }
-
- public static final class content {
- public static final int unknown = 0;
- public static final int encrypted_data = 1;
- public static final int keys = 2;
- }
-
- public static final class keyserver {
- public static final int search = 0x21070001;
- public static final int get = 0x21070002;
- public static final int add = 0x21070003;
- }
-}
diff --git a/src/org/apg/InputData.java b/src/org/apg/InputData.java
deleted file mode 100644
index ae5b51fe4..000000000
--- a/src/org/apg/InputData.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.apg;
-
-import java.io.InputStream;
-
-public class InputData {
- private PositionAwareInputStream mInputStream;
- private long mSize;
-
- public InputData(InputStream inputStream, long size) {
- mInputStream = new PositionAwareInputStream(inputStream);
- mSize = size;
- }
-
- public InputStream getInputStream() {
- return mInputStream;
- }
-
- public long getSize() {
- return mSize;
- }
-
- public long getStreamPosition() {
- return mInputStream.position();
- }
-}
diff --git a/src/org/apg/KeyServer.java b/src/org/apg/KeyServer.java
deleted file mode 100644
index 32158454f..000000000
--- a/src/org/apg/KeyServer.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package org.apg;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.List;
-import java.util.Vector;
-
-public abstract class KeyServer {
- static public class QueryException extends Exception {
- private static final long serialVersionUID = 2703768928624654512L;
-
- public QueryException(String message) {
- super(message);
- }
- }
-
- static public class TooManyResponses extends Exception {
- private static final long serialVersionUID = 2703768928624654513L;
- }
-
- static public class InsufficientQuery extends Exception {
- private static final long serialVersionUID = 2703768928624654514L;
- }
-
- static public class AddKeyException extends Exception {
- private static final long serialVersionUID = -507574859137295530L;
- }
-
- static public class KeyInfo implements Serializable {
- private static final long serialVersionUID = -7797972113284992662L;
- public Vector userIds;
- public String revoked;
- public Date date;
- public String fingerPrint;
- public long keyId;
- public int size;
- public String algorithm;
- }
-
- abstract List search(String query) throws QueryException, TooManyResponses,
- InsufficientQuery;
-
- abstract String get(long keyId) throws QueryException;
-
- abstract void add(String armouredText) throws AddKeyException;
-}
diff --git a/src/org/apg/PausableThread.java b/src/org/apg/PausableThread.java
deleted file mode 100644
index 87258d36c..000000000
--- a/src/org/apg/PausableThread.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.apg;
-
-public class PausableThread extends Thread {
- private boolean mPaused = false;
-
- public PausableThread(Runnable runnable) {
- super(runnable);
- }
-
- public void pause() {
- synchronized (this) {
- mPaused = true;
- while (mPaused) {
- try {
- wait();
- } catch (InterruptedException e) {
- // ignore
- }
- }
- }
- }
-
- public void unpause() {
- synchronized (this) {
- mPaused = false;
- notify();
- }
- }
-
- public boolean isPaused() {
- synchronized (this) {
- return mPaused;
- }
- }
-}
diff --git a/src/org/apg/PositionAwareInputStream.java b/src/org/apg/PositionAwareInputStream.java
deleted file mode 100644
index c59459670..000000000
--- a/src/org/apg/PositionAwareInputStream.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package org.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/apg/Preferences.java b/src/org/apg/Preferences.java
deleted file mode 100644
index f4800e064..000000000
--- a/src/org/apg/Preferences.java
+++ /dev/null
@@ -1,170 +0,0 @@
-package org.apg;
-
-import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.openpgp.PGPEncryptedData;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-import java.util.Vector;
-
-public class Preferences {
- private static Preferences mPreferences;
- private SharedPreferences mSharedPreferences;
-
- public static synchronized Preferences getPreferences(Context context) {
- return getPreferences(context, false);
- }
-
- public static synchronized Preferences getPreferences(Context context, boolean force_new) {
- if (mPreferences == null || force_new) {
- mPreferences = new Preferences(context);
- }
- return mPreferences;
- }
-
- private Preferences(Context context) {
- mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_PRIVATE);
- }
-
- public String getLanguage() {
- return mSharedPreferences.getString(Constants.pref.LANGUAGE, "");
- }
-
- public void setLanguage(String value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putString(Constants.pref.LANGUAGE, value);
- editor.commit();
- }
-
- public int getPassPhraseCacheTtl() {
- int ttl = mSharedPreferences.getInt(Constants.pref.PASS_PHRASE_CACHE_TTL, 180);
- // fix the value if it was set to "never" in previous versions, which currently is not
- // supported
- if (ttl == 0) {
- ttl = 180;
- }
- return ttl;
- }
-
- public void setPassPhraseCacheTtl(int value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.PASS_PHRASE_CACHE_TTL, value);
- editor.commit();
- }
-
- public int getDefaultEncryptionAlgorithm() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM,
- PGPEncryptedData.AES_256);
- }
-
- public void setDefaultEncryptionAlgorithm(int value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM, value);
- editor.commit();
- }
-
- public int getDefaultHashAlgorithm() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_HASH_ALGORITHM,
- HashAlgorithmTags.SHA256);
- }
-
- public void setDefaultHashAlgorithm(int value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_HASH_ALGORITHM, value);
- editor.commit();
- }
-
- public int getDefaultMessageCompression() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION,
- Id.choice.compression.zlib);
- }
-
- public void setDefaultMessageCompression(int value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_MESSAGE_COMPRESSION, value);
- editor.commit();
- }
-
- public int getDefaultFileCompression() {
- return mSharedPreferences.getInt(Constants.pref.DEFAULT_FILE_COMPRESSION,
- Id.choice.compression.none);
- }
-
- public void setDefaultFileCompression(int value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.pref.DEFAULT_FILE_COMPRESSION, value);
- editor.commit();
- }
-
- public boolean getDefaultAsciiArmour() {
- return mSharedPreferences.getBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, false);
- }
-
- public void setDefaultAsciiArmour(boolean value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.pref.DEFAULT_ASCII_ARMOUR, value);
- editor.commit();
- }
-
- public boolean getForceV3Signatures() {
- return mSharedPreferences.getBoolean(Constants.pref.FORCE_V3_SIGNATURES, false);
- }
-
- public void setForceV3Signatures(boolean value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.pref.FORCE_V3_SIGNATURES, value);
- editor.commit();
- }
-
- public boolean hasSeenChangeLog(String version) {
- return mSharedPreferences.getBoolean(Constants.pref.HAS_SEEN_CHANGE_LOG + version, false);
- }
-
- public void setHasSeenChangeLog(String version, boolean value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.pref.HAS_SEEN_CHANGE_LOG + version, value);
- editor.commit();
- }
-
- public boolean hasSeenHelp() {
- return mSharedPreferences.getBoolean(Constants.pref.HAS_SEEN_HELP, false);
- }
-
- public void setHasSeenHelp(boolean value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.pref.HAS_SEEN_HELP, value);
- editor.commit();
- }
-
- public String[] getKeyServers() {
- String rawData = mSharedPreferences.getString(Constants.pref.KEY_SERVERS,
- Constants.defaults.KEY_SERVERS);
- Vector servers = new Vector();
- String chunks[] = rawData.split(",");
- for (int i = 0; i < chunks.length; ++i) {
- String tmp = chunks[i].trim();
- if (tmp.length() > 0) {
- servers.add(tmp);
- }
- }
- return servers.toArray(chunks);
- }
-
- public void setKeyServers(String[] value) {
- SharedPreferences.Editor editor = mSharedPreferences.edit();
- String rawData = "";
- for (int i = 0; i < value.length; ++i) {
- String tmp = value[i].trim();
- if (tmp.length() == 0) {
- continue;
- }
- if (!"".equals(rawData)) {
- rawData += ",";
- }
- rawData += tmp;
- }
- editor.putString(Constants.pref.KEY_SERVERS, rawData);
- editor.commit();
- }
-}
diff --git a/src/org/apg/Primes.java b/src/org/apg/Primes.java
deleted file mode 100644
index 1fd6482b2..000000000
--- a/src/org/apg/Primes.java
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.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/apg/ProgressDialogUpdater.java b/src/org/apg/ProgressDialogUpdater.java
deleted file mode 100644
index 043cca906..000000000
--- a/src/org/apg/ProgressDialogUpdater.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.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/apg/Service.java b/src/org/apg/Service.java
deleted file mode 100644
index 24c4daa11..000000000
--- a/src/org/apg/Service.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.apg;
-
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-
-public class Service extends android.app.Service {
- private final IBinder mBinder = new LocalBinder();
-
- public static final String EXTRA_TTL = "ttl";
-
- private int mPassPhraseCacheTtl = 15;
- private Handler mCacheHandler = new Handler();
- private Runnable mCacheTask = new Runnable() {
- public void run() {
- // check every ttl/2 seconds, which shouldn't be heavy on the device (even if ttl = 15),
- // and makes sure the longest a pass phrase survives in the cache is 1.5 * ttl
- int delay = mPassPhraseCacheTtl * 1000 / 2;
- // also make sure the delay is not longer than one minute
- if (delay > 60000) {
- delay = 60000;
- }
-
- delay = Apg.cleanUpCache(mPassPhraseCacheTtl, delay);
- // don't check too often, even if we were close
- if (delay < 5000) {
- delay = 5000;
- }
-
- mCacheHandler.postDelayed(this, delay);
- }
- };
-
- static private boolean mIsRunning = false;
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- mIsRunning = true;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- mIsRunning = false;
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
- super.onStart(intent, startId);
-
- if (intent != null) {
- mPassPhraseCacheTtl = intent.getIntExtra(EXTRA_TTL, 15);
- }
- if (mPassPhraseCacheTtl < 15) {
- mPassPhraseCacheTtl = 15;
- }
- mCacheHandler.removeCallbacks(mCacheTask);
- mCacheHandler.postDelayed(mCacheTask, 1000);
- }
-
- static public boolean isRunning() {
- return mIsRunning;
- }
-
- public class LocalBinder extends Binder {
- Service getService() {
- return Service.this;
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
-}
diff --git a/src/org/apg/provider/Accounts.java b/src/org/apg/provider/Accounts.java
deleted file mode 100644
index b95f079df..000000000
--- a/src/org/apg/provider/Accounts.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.provider;
-
-import android.provider.BaseColumns;
-
-public class Accounts implements BaseColumns {
- public static final String TABLE_NAME = "accounts";
-
- public static final String _ID_type = "INTEGER PRIMARY KEY";
- public static final String NAME = "c_name";
- public static final String NAME_type = "TEXT";
-}
diff --git a/src/org/apg/provider/ApgServiceBlobDatabase.java b/src/org/apg/provider/ApgServiceBlobDatabase.java
deleted file mode 100644
index 70b9cd64f..000000000
--- a/src/org/apg/provider/ApgServiceBlobDatabase.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package org.apg.provider;
-
-import org.apg.ApgService;
-
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.net.Uri;
-import android.util.Log;
-
-public class ApgServiceBlobDatabase extends SQLiteOpenHelper {
-
- private static final String TAG = "ApgServiceBlobDatabase";
-
- private static final int VERSION = 1;
- private static final String NAME = "apg_service_blob_data";
- private static final String TABLE = "data";
-
- public ApgServiceBlobDatabase(Context context) {
- super(context, NAME, null, VERSION);
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "constructor called");
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "onCreate() called");
- db.execSQL("create table " + TABLE + " ( _id integer primary key autoincrement," +
- "key text not null)");
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "onUpgrade() called");
- // no upgrade necessary yet
- }
-
- public Uri insert(ContentValues vals) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "insert() called");
- SQLiteDatabase db = this.getWritableDatabase();
- long newId = db.insert(TABLE, null, vals);
- return ContentUris.withAppendedId(ApgServiceBlobProvider.CONTENT_URI, newId);
- }
-
- public Cursor query(String id, String key) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "query() called");
- SQLiteDatabase db = this.getReadableDatabase();
- return db.query(TABLE, new String[] {"_id"},
- "_id = ? and key = ?", new String[] {id, key},
- null, null, null);
- }
-}
diff --git a/src/org/apg/provider/ApgServiceBlobProvider.java b/src/org/apg/provider/ApgServiceBlobProvider.java
deleted file mode 100644
index f2d5377a4..000000000
--- a/src/org/apg/provider/ApgServiceBlobProvider.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package org.apg.provider;
-
-import org.apg.ApgService;
-import org.apg.Constants;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.List;
-import java.util.UUID;
-
-public class ApgServiceBlobProvider extends ContentProvider {
-
- private static final String TAG = "ApgServiceBlobProvider";
-
- public static final Uri CONTENT_URI = Uri.parse("content://org.thialfihar.android.apg.provider.apgserviceblobprovider");
-
- private static final String COLUMN_KEY = "key";
-
- private static final String STORE_PATH = Constants.path.APP_DIR+"/ApgServiceBlobs";
-
- private ApgServiceBlobDatabase mDb = null;
-
- public ApgServiceBlobProvider() {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "Constructor called");
- File dir = new File(STORE_PATH);
- dir.mkdirs();
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "Constructor finished");
- }
-
- @Override
- public int delete(Uri arg0, String arg1, String[] arg2) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "delete() called");
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public String getType(Uri arg0) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "getType() called");
- // not needed for now
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues ignored) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "insert() called");
- // ContentValues are actually ignored, because we want to store a blob with no more information
- // but have to create an record with the password generated here first
-
- ContentValues vals = new ContentValues();
-
- // Insert a random key in the database. This has to provided by the caller when updating or
- // getting the blob
- String password = UUID.randomUUID().toString();
- vals.put(COLUMN_KEY, password);
-
- Uri insertedUri = mDb.insert(vals);
- return Uri.withAppendedPath(insertedUri, password);
- }
-
- @Override
- public boolean onCreate() {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "onCreate() called");
- mDb = new ApgServiceBlobDatabase(getContext());
- // TODO Auto-generated method stub
- return true;
- }
-
- @Override
- public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3, String arg4) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "query() called");
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "update() called");
- // TODO Auto-generated method stub
- return 0;
- }
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, FileNotFoundException {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "openFile() called");
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "... with uri: "+uri.toString());
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "... with mode: "+mode);
-
- List segments = uri.getPathSegments();
- if(segments.size() < 2) {
- throw new SecurityException("Password not found in URI");
- }
- String id = segments.get(0);
- String key = segments.get(1);
-
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "... got id: "+id);
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "... and key: "+key);
-
- // get the data
- Cursor result = mDb.query(id, key);
-
- if(result.getCount() == 0) {
- // either the key is wrong or no id exists
- throw new FileNotFoundException("No file found with that ID and/or password");
- }
-
- File targetFile = new File(STORE_PATH, id);
- if(mode.equals("w")) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "... will try to open file w");
- if( !targetFile.exists() ) {
- try {
- targetFile.createNewFile();
- } catch (IOException e) {
- Log.e(TAG, "... got IEOException on creating new file", e);
- throw new FileNotFoundException("Could not create file to write to");
- }
- }
- return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE );
- } else if(mode.equals("r")) {
- if(ApgService.LOCAL_LOGD) Log.d(TAG, "... will try to open file r");
- if( !targetFile.exists() ) {
- throw new FileNotFoundException("Error: Could not find the file requested");
- }
- return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
- }
-
- return null;
- }
-
-}
diff --git a/src/org/apg/provider/DataProvider.java b/src/org/apg/provider/DataProvider.java
deleted file mode 100644
index a97851e58..000000000
--- a/src/org/apg/provider/DataProvider.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.provider;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.HashMap;
-
-import org.apg.Id;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.UriMatcher;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-
-public class DataProvider extends ContentProvider {
- public static final String AUTHORITY = "org.thialfihar.android.apg.provider";
-
- private static final int PUBLIC_KEY_RING = 101;
- private static final int PUBLIC_KEY_RING_ID = 102;
- private static final int PUBLIC_KEY_RING_BY_KEY_ID = 103;
- private static final int PUBLIC_KEY_RING_BY_EMAILS = 104;
- private static final int PUBLIC_KEY_RING_KEY = 111;
- private static final int PUBLIC_KEY_RING_KEY_RANK = 112;
- private static final int PUBLIC_KEY_RING_USER_ID = 121;
- private static final int PUBLIC_KEY_RING_USER_ID_RANK = 122;
-
- private static final int SECRET_KEY_RING = 201;
- private static final int SECRET_KEY_RING_ID = 202;
- private static final int SECRET_KEY_RING_BY_KEY_ID = 203;
- private static final int SECRET_KEY_RING_BY_EMAILS = 204;
- private static final int SECRET_KEY_RING_KEY = 211;
- private static final int SECRET_KEY_RING_KEY_RANK = 212;
- private static final int SECRET_KEY_RING_USER_ID = 221;
- private static final int SECRET_KEY_RING_USER_ID_RANK = 222;
-
- private static final int DATA_STREAM = 301;
-
- private static final String PUBLIC_KEY_RING_CONTENT_DIR_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key_ring";
- private static final String PUBLIC_KEY_RING_CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.public.key_ring";
-
- private static final String PUBLIC_KEY_CONTENT_DIR_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.public.key";
- private static final String PUBLIC_KEY_CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.public.key";
-
- private static final String SECRET_KEY_RING_CONTENT_DIR_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key_ring";
- private static final String SECRET_KEY_RING_CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key_ring";
-
- private static final String SECRET_KEY_CONTENT_DIR_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.secret.key";
- private static final String SECRET_KEY_CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.secret.key";
-
- private static final String USER_ID_CONTENT_DIR_TYPE =
- "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
- private static final String USER_ID_CONTENT_ITEM_TYPE =
- "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
-
- public static final String _ID = "_id";
- public static final String MASTER_KEY_ID = "master_key_id";
- public static final String KEY_ID = "key_id";
- public static final String USER_ID = "user_id";
-
- private static final UriMatcher mUriMatcher;
-
- private Database mDb;
-
- static {
- mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- mUriMatcher.addURI(AUTHORITY, "key_rings/public/key_id/*", PUBLIC_KEY_RING_BY_KEY_ID);
- mUriMatcher.addURI(AUTHORITY, "key_rings/public/emails/*", PUBLIC_KEY_RING_BY_EMAILS);
-
- mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys", PUBLIC_KEY_RING_KEY);
- mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/keys/#", PUBLIC_KEY_RING_KEY_RANK);
-
- mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids", PUBLIC_KEY_RING_USER_ID);
- mUriMatcher.addURI(AUTHORITY, "key_rings/public/*/user_ids/#", PUBLIC_KEY_RING_USER_ID_RANK);
-
- mUriMatcher.addURI(AUTHORITY, "key_rings/public", PUBLIC_KEY_RING);
- mUriMatcher.addURI(AUTHORITY, "key_rings/public/*", PUBLIC_KEY_RING_ID);
-
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret/key_id/*", SECRET_KEY_RING_BY_KEY_ID);
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret/emails/*", SECRET_KEY_RING_BY_EMAILS);
-
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys", SECRET_KEY_RING_KEY);
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/keys/#", SECRET_KEY_RING_KEY_RANK);
-
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids", SECRET_KEY_RING_USER_ID);
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*/user_ids/#", SECRET_KEY_RING_USER_ID_RANK);
-
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret", SECRET_KEY_RING);
- mUriMatcher.addURI(AUTHORITY, "key_rings/secret/*", SECRET_KEY_RING_ID);
-
- mUriMatcher.addURI(AUTHORITY, "data/*", DATA_STREAM);
- }
-
- @Override
- public boolean onCreate() {
- mDb = new Database(getContext());
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection,
- String[] selectionArgs, String sortOrder) {
- // TODO: implement the others, then use them for the lists
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- HashMap projectionMap = new HashMap();
-
- int match = mUriMatcher.match(uri);
- int type;
- switch (match) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_KEY:
- case PUBLIC_KEY_RING_KEY_RANK:
- case PUBLIC_KEY_RING_USER_ID:
- case PUBLIC_KEY_RING_USER_ID_RANK:
- type = Id.database.type_public;
- break;
-
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_ID:
- case SECRET_KEY_RING_BY_KEY_ID:
- case SECRET_KEY_RING_BY_EMAILS:
- case SECRET_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY_RANK:
- case SECRET_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID_RANK:
- type = Id.database.type_secret;
- break;
-
- default: {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
-
- qb.appendWhere(KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = " + type);
-
- switch (match) {
- case PUBLIC_KEY_RING_ID:
- case SECRET_KEY_RING_ID: {
- qb.appendWhere(" AND " +
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
-
- // break omitted intentionally
- }
-
- case PUBLIC_KEY_RING:
- case SECRET_KEY_RING: {
- qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
- "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
- Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
- Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
- ") " +
- " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
- "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
- UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
- UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
-
- projectionMap.put(_ID,
- KeyRings.TABLE_NAME + "." + KeyRings._ID);
- projectionMap.put(MASTER_KEY_ID,
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
- projectionMap.put(USER_ID,
- UserIds.TABLE_NAME + "." + UserIds.USER_ID);
-
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
- }
-
- break;
- }
-
- case SECRET_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID: {
- qb.setTables(Keys.TABLE_NAME + " AS tmp INNER JOIN " +
- KeyRings.TABLE_NAME + " ON (" +
- KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
- "tmp." + Keys.KEY_RING_ID + ")" +
- " INNER JOIN " + Keys.TABLE_NAME + " ON " +
- "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
- Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
- Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
- ") " +
- " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
- "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
- UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
- UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
-
- projectionMap.put(_ID,
- KeyRings.TABLE_NAME + "." + KeyRings._ID);
- projectionMap.put(MASTER_KEY_ID,
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
- projectionMap.put(USER_ID,
- UserIds.TABLE_NAME + "." + UserIds.USER_ID);
-
- qb.appendWhere(" AND tmp." + Keys.KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(3));
-
- break;
- }
-
- case SECRET_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_EMAILS: {
- qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
- "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
- Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
- Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
- ") " +
- " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
- "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
- UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
- UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
-
- projectionMap.put(_ID,
- KeyRings.TABLE_NAME + "." + KeyRings._ID);
- projectionMap.put(MASTER_KEY_ID,
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID);
- projectionMap.put(USER_ID,
- UserIds.TABLE_NAME + "." + UserIds.USER_ID);
-
- String emails = uri.getPathSegments().get(3);
- String chunks[] = emails.split(" *, *");
- boolean gotCondition = false;
- String emailWhere = "";
- for (int i = 0; i < chunks.length; ++i) {
- if (chunks[i].length() == 0) {
- continue;
- }
- if (i != 0) {
- emailWhere += " OR ";
- }
- emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
- // match '*', so it has to be at the *end* of the user id
- emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
- gotCondition = true;
- }
-
- if (gotCondition) {
- qb.appendWhere(" AND EXISTS (SELECT tmp." + UserIds._ID +
- " FROM " + UserIds.TABLE_NAME +
- " AS tmp WHERE tmp." + UserIds.KEY_ID + " = " +
- Keys.TABLE_NAME + "." + Keys._ID +
- " AND (" + emailWhere + "))");
- }
-
- break;
- }
-
- default: {
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
-
- qb.setProjectionMap(projectionMap);
-
- // If no sort order is specified use the default
- String orderBy;
- if (TextUtils.isEmpty(sortOrder)) {
- orderBy = null;
- } else {
- orderBy = sortOrder;
- }
-
- //System.out.println(qb.buildQuery(projection, selection, selectionArgs, null, null, sortOrder, null).replace("WHERE", "WHERE\n"));
- Cursor c = qb.query(mDb.db(), projection, selection, selectionArgs, null, null, orderBy);
-
- // Tell the cursor what uri to watch, so it knows when its source data changes
- c.setNotificationUri(getContext().getContentResolver(), uri);
- return c;
- }
-
- @Override
- public String getType(Uri uri) {
- switch (mUriMatcher.match(uri)) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_BY_EMAILS:
- return PUBLIC_KEY_RING_CONTENT_DIR_TYPE;
-
- case PUBLIC_KEY_RING_ID:
- return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
-
- case PUBLIC_KEY_RING_BY_KEY_ID:
- return PUBLIC_KEY_RING_CONTENT_ITEM_TYPE;
-
- case PUBLIC_KEY_RING_KEY:
- return PUBLIC_KEY_CONTENT_DIR_TYPE;
-
- case PUBLIC_KEY_RING_KEY_RANK:
- return PUBLIC_KEY_CONTENT_ITEM_TYPE;
-
- case PUBLIC_KEY_RING_USER_ID:
- return USER_ID_CONTENT_DIR_TYPE;
-
- case PUBLIC_KEY_RING_USER_ID_RANK:
- return USER_ID_CONTENT_ITEM_TYPE;
-
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_BY_EMAILS:
- return SECRET_KEY_RING_CONTENT_DIR_TYPE;
-
- case SECRET_KEY_RING_ID:
- return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
-
- case SECRET_KEY_RING_BY_KEY_ID:
- return SECRET_KEY_RING_CONTENT_ITEM_TYPE;
-
- case SECRET_KEY_RING_KEY:
- return SECRET_KEY_CONTENT_DIR_TYPE;
-
- case SECRET_KEY_RING_KEY_RANK:
- return SECRET_KEY_CONTENT_ITEM_TYPE;
-
- case SECRET_KEY_RING_USER_ID:
- return USER_ID_CONTENT_DIR_TYPE;
-
- case SECRET_KEY_RING_USER_ID_RANK:
- return USER_ID_CONTENT_ITEM_TYPE;
-
- default:
- throw new IllegalArgumentException("Unknown URI " + uri);
- }
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues initialValues) {
- // not supported
- return null;
- }
-
- @Override
- public int delete(Uri uri, String where, String[] whereArgs) {
- // not supported
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
- // not supported
- return 0;
- }
-
- @Override
- public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- int match = mUriMatcher.match(uri);
- if (match != DATA_STREAM) {
- throw new FileNotFoundException();
- }
- String fileName = uri.getPathSegments().get(1);
- File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName);
- return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- }
-}
diff --git a/src/org/apg/provider/Database.java b/src/org/apg/provider/Database.java
deleted file mode 100644
index 8b1aaa9e7..000000000
--- a/src/org/apg/provider/Database.java
+++ /dev/null
@@ -1,605 +0,0 @@
-package org.apg.provider;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.util.IterableIterator;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Vector;
-
-public class Database extends SQLiteOpenHelper {
- public static class GeneralException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public GeneralException(String message) {
- super(message);
- }
- }
-
- private static final String DATABASE_NAME = "apg";
- private static final int DATABASE_VERSION = 2;
-
- public static final String AUTHORITY = "org.thialfihar.android.apg.database";
-
- public static HashMap sKeyRingsProjection;
- public static HashMap sKeysProjection;
- public static HashMap sUserIdsProjection;
-
- private SQLiteDatabase mDb = null;
- private int mStatus = 0;
-
- static {
- sKeyRingsProjection = new HashMap();
- sKeyRingsProjection.put(KeyRings._ID, KeyRings._ID);
- sKeyRingsProjection.put(KeyRings.MASTER_KEY_ID, KeyRings.MASTER_KEY_ID);
- sKeyRingsProjection.put(KeyRings.TYPE, KeyRings.TYPE);
- sKeyRingsProjection.put(KeyRings.WHO_ID, KeyRings.WHO_ID);
- sKeyRingsProjection.put(KeyRings.KEY_RING_DATA, KeyRings.KEY_RING_DATA);
-
- sKeysProjection = new HashMap();
- sKeysProjection.put(Keys._ID, Keys._ID);
- sKeysProjection.put(Keys.KEY_ID, Keys.KEY_ID);
- sKeysProjection.put(Keys.TYPE, Keys.TYPE);
- sKeysProjection.put(Keys.IS_MASTER_KEY, Keys.IS_MASTER_KEY);
- sKeysProjection.put(Keys.ALGORITHM, Keys.ALGORITHM);
- sKeysProjection.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
- sKeysProjection.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
- sKeysProjection.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
- sKeysProjection.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
- sKeysProjection.put(Keys.CREATION, Keys.CREATION);
- sKeysProjection.put(Keys.EXPIRY, Keys.EXPIRY);
- sKeysProjection.put(Keys.KEY_DATA, Keys.KEY_DATA);
- sKeysProjection.put(Keys.RANK, Keys.RANK);
-
- sUserIdsProjection = new HashMap();
- sUserIdsProjection.put(UserIds._ID, UserIds._ID);
- sUserIdsProjection.put(UserIds.KEY_ID, UserIds.KEY_ID);
- sUserIdsProjection.put(UserIds.USER_ID, UserIds.USER_ID);
- sUserIdsProjection.put(UserIds.RANK, UserIds.RANK);
- }
-
- public Database(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- // force upgrade to test things
- //onUpgrade(getWritableDatabase(), 1, 2);
- mDb = getWritableDatabase();
- }
-
- @Override
- protected void finalize() throws Throwable {
- mDb.close();
- super.finalize();
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
- KeyRings._ID + " " + KeyRings._ID_type + "," +
- KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
- KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
- KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
- KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
-
- db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
- Keys._ID + " " + Keys._ID_type + "," +
- Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
- Keys.TYPE + " " + Keys.TYPE_type + ", " +
- Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
- Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
- Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
- Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
- Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
- Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
- Keys.CREATION + " " + Keys.CREATION_type + ", " +
- Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
- Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
- Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
- Keys.RANK + " " + Keys.RANK_type + ");");
-
- db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
- UserIds._ID + " " + UserIds._ID_type + "," +
- UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
- UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
- UserIds.RANK + " " + UserIds.RANK_type + ");");
-
- db.execSQL("CREATE TABLE " + Accounts.TABLE_NAME + " (" +
- Accounts._ID + " " + Accounts._ID_type + "," +
- Accounts.NAME + " " + Accounts.NAME_type + ");");
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- mDb = db;
- for (int version = oldVersion; version < newVersion; ++version) {
- switch (version) {
- case 1: { // upgrade 1 to 2
- db.execSQL("DROP TABLE IF EXISTS " + KeyRings.TABLE_NAME + ";");
- db.execSQL("DROP TABLE IF EXISTS " + Keys.TABLE_NAME + ";");
- db.execSQL("DROP TABLE IF EXISTS " + UserIds.TABLE_NAME + ";");
-
- db.execSQL("CREATE TABLE " + KeyRings.TABLE_NAME + " (" +
- KeyRings._ID + " " + KeyRings._ID_type + "," +
- KeyRings.MASTER_KEY_ID + " " + KeyRings.MASTER_KEY_ID_type + ", " +
- KeyRings.TYPE + " " + KeyRings.TYPE_type + ", " +
- KeyRings.WHO_ID + " " + KeyRings.WHO_ID_type + ", " +
- KeyRings.KEY_RING_DATA + " " + KeyRings.KEY_RING_DATA_type + ");");
-
- db.execSQL("CREATE TABLE " + Keys.TABLE_NAME + " (" +
- Keys._ID + " " + Keys._ID_type + "," +
- Keys.KEY_ID + " " + Keys.KEY_ID_type + ", " +
- Keys.TYPE + " " + Keys.TYPE_type + ", " +
- Keys.IS_MASTER_KEY + " " + Keys.IS_MASTER_KEY_type + ", " +
- Keys.ALGORITHM + " " + Keys.ALGORITHM_type + ", " +
- Keys.KEY_SIZE + " " + Keys.KEY_SIZE_type + ", " +
- Keys.CAN_SIGN + " " + Keys.CAN_SIGN_type + ", " +
- Keys.CAN_ENCRYPT + " " + Keys.CAN_ENCRYPT_type + ", " +
- Keys.IS_REVOKED + " " + Keys.IS_REVOKED_type + ", " +
- Keys.CREATION + " " + Keys.CREATION_type + ", " +
- Keys.EXPIRY + " " + Keys.EXPIRY_type + ", " +
- Keys.KEY_RING_ID + " " + Keys.KEY_RING_ID_type + ", " +
- Keys.KEY_DATA + " " + Keys.KEY_DATA_type +
- Keys.RANK + " " + Keys.RANK_type + ");");
-
- db.execSQL("CREATE TABLE " + UserIds.TABLE_NAME + " (" +
- UserIds._ID + " " + UserIds._ID_type + "," +
- UserIds.KEY_ID + " " + UserIds.KEY_ID_type + "," +
- UserIds.USER_ID + " " + UserIds.USER_ID_type + "," +
- UserIds.RANK + " " + UserIds.RANK_type + ");");
-
- Cursor cursor = db.query("public_keys", new String[] { "c_key_data" },
- null, null, null, null, null);
- if (cursor != null && cursor.moveToFirst()) {
- do {
- byte[] data = cursor.getBlob(0);
- try {
- PGPPublicKeyRing keyRing = new PGPPublicKeyRing(data);
- saveKeyRing(keyRing);
- } catch (IOException e) {
- Log.e("apg.db.upgrade", "key import failed: " + e);
- } catch (GeneralException e) {
- Log.e("apg.db.upgrade", "key import failed: " + e);
- }
- } while (cursor.moveToNext());
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- cursor = db.query("secret_keys", new String[]{ "c_key_data" },
- null, null, null, null, null);
- if (cursor != null && cursor.moveToFirst()) {
- do {
- byte[] data = cursor.getBlob(0);
- try {
- PGPSecretKeyRing keyRing = new PGPSecretKeyRing(data);
- saveKeyRing(keyRing);
- } catch (IOException e) {
- Log.e("apg.db.upgrade", "key import failed: " + e);
- } catch (PGPException e) {
- Log.e("apg.db.upgrade", "key import failed: " + e);
- } catch (GeneralException e) {
- Log.e("apg.db.upgrade", "key import failed: " + e);
- }
- } while (cursor.moveToNext());
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- db.execSQL("DROP TABLE IF EXISTS public_keys;");
- db.execSQL("DROP TABLE IF EXISTS secret_keys;");
-
- break;
- }
-
- default: {
- break;
- }
- }
- }
- mDb = null;
- }
-
- public int saveKeyRing(PGPPublicKeyRing keyRing) throws IOException, GeneralException {
- mDb.beginTransaction();
- ContentValues values = new ContentValues();
- PGPPublicKey masterKey = keyRing.getPublicKey();
- long masterKeyId = masterKey.getKeyID();
-
- values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
- values.put(KeyRings.TYPE, Id.database.type_public);
- values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
-
- long rowId = insertOrUpdateKeyRing(values);
- int returnValue = mStatus;
-
- if (rowId == -1) {
- throw new GeneralException("saving public key ring " + masterKeyId + " failed");
- }
-
- Vector seenIds = new Vector();
- int rank = 0;
- for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) {
- seenIds.add(saveKey(rowId, key, rank));
- ++rank;
- }
-
- String seenIdsStr = "";
- for (Integer id : seenIds) {
- if (seenIdsStr.length() > 0) {
- seenIdsStr += ",";
- }
- seenIdsStr += id;
- }
- mDb.delete(Keys.TABLE_NAME,
- Keys.KEY_RING_ID + " = ? AND " +
- Keys._ID + " NOT IN (" + seenIdsStr + ")",
- new String[] { "" + rowId });
-
- mDb.setTransactionSuccessful();
- mDb.endTransaction();
- return returnValue;
- }
-
- public int saveKeyRing(PGPSecretKeyRing keyRing) throws IOException, GeneralException {
- mDb.beginTransaction();
- ContentValues values = new ContentValues();
- PGPSecretKey masterKey = keyRing.getSecretKey();
- long masterKeyId = masterKey.getKeyID();
-
- values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
- values.put(KeyRings.TYPE, Id.database.type_secret);
- values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
-
- long rowId = insertOrUpdateKeyRing(values);
- int returnValue = mStatus;
-
- if (rowId == -1) {
- throw new GeneralException("saving secret key ring " + masterKeyId + " failed");
- }
-
- Vector seenIds = new Vector();
- int rank = 0;
- for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) {
- seenIds.add(saveKey(rowId, key, rank));
- ++rank;
- }
-
- String seenIdsStr = "";
- for (Integer id : seenIds) {
- if (seenIdsStr.length() > 0) {
- seenIdsStr += ",";
- }
- seenIdsStr += id;
- }
- mDb.delete(Keys.TABLE_NAME,
- Keys.KEY_RING_ID + " = ? AND " +
- Keys._ID + " NOT IN (" + seenIdsStr + ")",
- new String[] { "" + rowId });
-
- mDb.setTransactionSuccessful();
- mDb.endTransaction();
- return returnValue;
- }
-
- private int saveKey(long keyRingId, PGPPublicKey key, int rank)
- throws IOException, GeneralException {
- ContentValues values = new ContentValues();
-
- values.put(Keys.KEY_ID, key.getKeyID());
- values.put(Keys.TYPE, Id.database.type_public);
- values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
- values.put(Keys.ALGORITHM, key.getAlgorithm());
- values.put(Keys.KEY_SIZE, key.getBitStrength());
- values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
- values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
- values.put(Keys.IS_REVOKED, key.isRevoked());
- values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
- Date expiryDate = Apg.getExpiryDate(key);
- if (expiryDate != null) {
- values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
- }
- values.put(Keys.KEY_RING_ID, keyRingId);
- values.put(Keys.KEY_DATA, key.getEncoded());
- values.put(Keys.RANK, rank);
-
- long rowId = insertOrUpdateKey(values);
-
- if (rowId == -1) {
- throw new GeneralException("saving public key " + key.getKeyID() + " failed");
- }
-
- Vector seenIds = new Vector();
- int userIdRank = 0;
- for (String userId : new IterableIterator(key.getUserIDs())) {
- seenIds.add(saveUserId(rowId, userId, userIdRank));
- ++userIdRank;
- }
-
- String seenIdsStr = "";
- for (Integer id : seenIds) {
- if (seenIdsStr.length() > 0) {
- seenIdsStr += ",";
- }
- seenIdsStr += id;
- }
- mDb.delete(UserIds.TABLE_NAME,
- UserIds.KEY_ID + " = ? AND " +
- UserIds._ID + " NOT IN (" + seenIdsStr + ")",
- new String[] { "" + rowId });
-
- return (int)rowId;
- }
-
- private int saveKey(long keyRingId, PGPSecretKey key, int rank)
- throws IOException, GeneralException {
- ContentValues values = new ContentValues();
-
- values.put(Keys.KEY_ID, key.getPublicKey().getKeyID());
- values.put(Keys.TYPE, Id.database.type_secret);
- values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
- values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
- values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
- values.put(Keys.CAN_SIGN, Apg.isSigningKey(key));
- values.put(Keys.CAN_ENCRYPT, Apg.isEncryptionKey(key));
- values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
- values.put(Keys.CREATION, Apg.getCreationDate(key).getTime() / 1000);
- Date expiryDate = Apg.getExpiryDate(key);
- if (expiryDate != null) {
- values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
- }
- values.put(Keys.KEY_RING_ID, keyRingId);
- values.put(Keys.KEY_DATA, key.getEncoded());
- values.put(Keys.RANK, rank);
-
- long rowId = insertOrUpdateKey(values);
-
- if (rowId == -1) {
- throw new GeneralException("saving secret key " + key.getPublicKey().getKeyID() + " failed");
- }
-
- Vector seenIds = new Vector();
- int userIdRank = 0;
- for (String userId : new IterableIterator(key.getUserIDs())) {
- seenIds.add(saveUserId(rowId, userId, userIdRank));
- ++userIdRank;
- }
-
- String seenIdsStr = "";
- for (Integer id : seenIds) {
- if (seenIdsStr.length() > 0) {
- seenIdsStr += ",";
- }
- seenIdsStr += id;
- }
- mDb.delete(UserIds.TABLE_NAME,
- UserIds.KEY_ID + " = ? AND " +
- UserIds._ID + " NOT IN (" + seenIdsStr + ")",
- new String[] { "" + rowId });
-
- return (int)rowId;
- }
-
- private int saveUserId(long keyId, String userId, int rank) throws GeneralException {
- ContentValues values = new ContentValues();
-
- values.put(UserIds.KEY_ID, keyId);
- values.put(UserIds.USER_ID, userId);
- values.put(UserIds.RANK, rank);
-
- long rowId = insertOrUpdateUserId(values);
-
- if (rowId == -1) {
- throw new GeneralException("saving user id " + userId + " failed");
- }
-
- return (int)rowId;
- }
-
- private long insertOrUpdateKeyRing(ContentValues values) {
- Cursor c = mDb.query(KeyRings.TABLE_NAME, new String[] { KeyRings._ID },
- KeyRings.MASTER_KEY_ID + " = ? AND " + KeyRings.TYPE + " = ?",
- new String[] {
- values.getAsString(KeyRings.MASTER_KEY_ID),
- values.getAsString(KeyRings.TYPE),
- },
- null, null, null);
- long rowId = -1;
- if (c != null && c.moveToFirst()) {
- rowId = c.getLong(0);
- mDb.update(KeyRings.TABLE_NAME, values,
- KeyRings._ID + " = ?", new String[] { "" + rowId });
- mStatus = Id.return_value.updated;
- } else {
- rowId = mDb.insert(KeyRings.TABLE_NAME, KeyRings.WHO_ID, values);
- mStatus = Id.return_value.ok;
- }
-
- if (c != null) {
- c.close();
- }
-
- return rowId;
- }
-
- private long insertOrUpdateKey(ContentValues values) {
- Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
- Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
- new String[] {
- values.getAsString(Keys.KEY_ID),
- values.getAsString(Keys.TYPE),
- },
- null, null, null);
- long rowId = -1;
- if (c != null && c.moveToFirst()) {
- rowId = c.getLong(0);
- mDb.update(Keys.TABLE_NAME, values,
- Keys._ID + " = ?", new String[] { "" + rowId });
- } else {
- rowId = mDb.insert(Keys.TABLE_NAME, Keys.KEY_DATA, values);
- }
-
- if (c != null) {
- c.close();
- }
-
- return rowId;
- }
-
- private long insertOrUpdateUserId(ContentValues values) {
- Cursor c = mDb.query(UserIds.TABLE_NAME, new String[] { UserIds._ID },
- UserIds.KEY_ID + " = ? AND " + UserIds.USER_ID + " = ?",
- new String[] {
- values.getAsString(UserIds.KEY_ID),
- values.getAsString(UserIds.USER_ID),
- },
- null, null, null);
- long rowId = -1;
- if (c != null && c.moveToFirst()) {
- rowId = c.getLong(0);
- mDb.update(UserIds.TABLE_NAME, values,
- UserIds._ID + " = ?", new String[] { "" + rowId });
- } else {
- rowId = mDb.insert(UserIds.TABLE_NAME, UserIds.USER_ID, values);
- }
-
- if (c != null) {
- c.close();
- }
-
- return rowId;
- }
-
- public Object getKeyRing(int keyRingId) {
- Cursor c = mDb.query(KeyRings.TABLE_NAME,
- new String[] { KeyRings.KEY_RING_DATA, KeyRings.TYPE },
- KeyRings._ID + " = ?",
- new String[] {
- "" + keyRingId,
- },
- null, null, null);
- byte[] data = null;
- Object keyRing = null;
- if (c != null && c.moveToFirst()) {
- data = c.getBlob(0);
- if (data != null) {
- try {
- if (c.getInt(1) == Id.database.type_public) {
- keyRing = new PGPPublicKeyRing(data);
- } else {
- keyRing = new PGPSecretKeyRing(data);
- }
- } catch (IOException e) {
- // can't load it, then
- } catch (PGPException e) {
- // can't load it, then
- }
- }
- }
-
- if (c != null) {
- c.close();
- }
-
- return keyRing;
- }
-
- public byte[] getKeyRingDataFromKeyId(int type, long keyId) {
- Cursor c = mDb.query(Keys.TABLE_NAME + " INNER JOIN " + KeyRings.TABLE_NAME + " ON (" +
- KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
- Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + ")",
- new String[] { KeyRings.TABLE_NAME + "." + KeyRings.KEY_RING_DATA },
- Keys.TABLE_NAME + "." + Keys.KEY_ID + " = ? AND " +
- KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
- new String[] {
- "" + keyId,
- "" + type,
- },
- null, null, null);
-
- byte[] data = null;
- if (c != null && c.moveToFirst()) {
- data = c.getBlob(0);
- }
-
- if (c != null) {
- c.close();
- }
-
- return data;
- }
-
- public byte[] getKeyDataFromKeyId(int type, long keyId) {
- Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys.KEY_DATA },
- Keys.KEY_ID + " = ? AND " + Keys.TYPE + " = ?",
- new String[] {
- "" + keyId,
- "" + type,
- },
- null, null, null);
- byte[] data = null;
- if (c != null && c.moveToFirst()) {
- data = c.getBlob(0);
- }
-
- if (c != null) {
- c.close();
- }
-
- return data;
- }
-
- public void deleteKeyRing(int keyRingId) {
- mDb.beginTransaction();
- mDb.delete(KeyRings.TABLE_NAME,
- KeyRings._ID + " = ?", new String[] { "" + keyRingId });
-
- Cursor c = mDb.query(Keys.TABLE_NAME, new String[] { Keys._ID },
- Keys.KEY_RING_ID + " = ?",
- new String[] {
- "" + keyRingId,
- },
- null, null, null);
- if (c != null && c.moveToFirst()) {
- do {
- int keyId = c.getInt(0);
- deleteKey(keyId);
- } while (c.moveToNext());
- }
-
- if (c != null) {
- c.close();
- }
-
- mDb.setTransactionSuccessful();
- mDb.endTransaction();
- }
-
- private void deleteKey(int keyId) {
- mDb.delete(Keys.TABLE_NAME,
- Keys._ID + " = ?", new String[] { "" + keyId });
-
- mDb.delete(UserIds.TABLE_NAME,
- UserIds.KEY_ID + " = ?", new String[] { "" + keyId });
- }
-
- public SQLiteDatabase db() {
- return mDb;
- }
-}
diff --git a/src/org/apg/provider/KeyRings.java b/src/org/apg/provider/KeyRings.java
deleted file mode 100644
index 304afd24f..000000000
--- a/src/org/apg/provider/KeyRings.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.provider;
-
-import android.provider.BaseColumns;
-
-public class KeyRings implements BaseColumns {
- public static final String TABLE_NAME = "key_rings";
-
- public static final String _ID_type = "INTEGER PRIMARY KEY";
- public static final String MASTER_KEY_ID = "c_master_key_id";
- public static final String MASTER_KEY_ID_type = "INT64";
- public static final String TYPE = "c_type";
- public static final String TYPE_type = "INTEGER";
- public static final String WHO_ID = "c_who_id";
- public static final String WHO_ID_type = "INTEGER";
- public static final String KEY_RING_DATA = "c_key_ring_data";
- public static final String KEY_RING_DATA_type = "BLOB";
-}
diff --git a/src/org/apg/provider/Keys.java b/src/org/apg/provider/Keys.java
deleted file mode 100644
index 63eaee54f..000000000
--- a/src/org/apg/provider/Keys.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.provider;
-
-import android.provider.BaseColumns;
-
-public class Keys implements BaseColumns {
- public static final String TABLE_NAME = "keys";
-
- public static final String _ID_type = "INTEGER PRIMARY KEY";
- public static final String KEY_ID = "c_key_id";
- public static final String KEY_ID_type = "INT64";
- public static final String TYPE = "c_type";
- public static final String TYPE_type = "INTEGER";
- public static final String IS_MASTER_KEY = "c_is_master_key";
- public static final String IS_MASTER_KEY_type = "INTEGER";
- public static final String ALGORITHM = "c_algorithm";
- public static final String ALGORITHM_type = "INTEGER";
- public static final String KEY_SIZE = "c_key_size";
- public static final String KEY_SIZE_type = "INTEGER";
- public static final String CAN_SIGN = "c_can_sign";
- public static final String CAN_SIGN_type = "INTEGER";
- public static final String CAN_ENCRYPT = "c_can_encrypt";
- public static final String CAN_ENCRYPT_type = "INTEGER";
- public static final String IS_REVOKED = "c_is_revoked";
- public static final String IS_REVOKED_type = "INTEGER";
- public static final String CREATION = "c_creation";
- public static final String CREATION_type = "INTEGER";
- public static final String EXPIRY = "c_expiry";
- public static final String EXPIRY_type = "INTEGER";
- public static final String KEY_RING_ID = "c_key_ring_id";
- public static final String KEY_RING_ID_type = "INTEGER";
- public static final String KEY_DATA = "c_key_data";
- public static final String KEY_DATA_type = "BLOB";
- public static final String RANK = "c_key_data";
- public static final String RANK_type = "INTEGER";
-}
diff --git a/src/org/apg/provider/UserIds.java b/src/org/apg/provider/UserIds.java
deleted file mode 100644
index e8ddc677d..000000000
--- a/src/org/apg/provider/UserIds.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.provider;
-
-import android.provider.BaseColumns;
-
-public class UserIds implements BaseColumns {
- public static final String TABLE_NAME = "user_ids";
-
- public static final String _ID_type = "INTEGER PRIMARY KEY";
- public static final String KEY_ID = "c_key_id";
- public static final String KEY_ID_type = "INTEGER";
- public static final String USER_ID = "c_user_id";
- public static final String USER_ID_type = "TEXT";
- public static final String RANK = "c_rank";
- public static final String RANK_type = "INTEGER";
-}
diff --git a/src/org/apg/ui/AboutActivity.java b/src/org/apg/ui/AboutActivity.java
deleted file mode 100644
index 308a1e06e..000000000
--- a/src/org/apg/ui/AboutActivity.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.apg.ui;
-
-import org.apg.Constants;
-import org.apg.R;
-
-import android.app.Activity;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.TextView;
-
-public class AboutActivity extends Activity {
- Activity mActivity;
-
- /**
- * Instantiate View for this Activity
- */
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.about_activity);
-
- mActivity = this;
-
- TextView versionText = (TextView) findViewById(R.id.about_version);
- versionText.setText(getString(R.string.about_version) + " " + getVersion());
- }
-
- /**
- * Get the current package version.
- *
- * @return The current version.
- */
- private String getVersion() {
- String result = "";
- try {
- PackageManager manager = mActivity.getPackageManager();
- PackageInfo info = manager.getPackageInfo(mActivity.getPackageName(), 0);
-
- result = String.format("%s (%s)", info.versionName, info.versionCode);
- } catch (NameNotFoundException e) {
- Log.w(Constants.TAG, "Unable to get application version: " + e.getMessage());
- result = "Unable to get application version.";
- }
-
- return result;
- }
-}
diff --git a/src/org/apg/ui/BaseActivity.java b/src/org/apg/ui/BaseActivity.java
deleted file mode 100644
index 9b5039a5d..000000000
--- a/src/org/apg/ui/BaseActivity.java
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Locale;
-
-import org.apg.Apg;
-import org.apg.AskForSecretKeyPassPhrase;
-import org.apg.Constants;
-import org.apg.Id;
-import org.apg.PausableThread;
-import org.apg.Preferences;
-import org.apg.ProgressDialogUpdater;
-import org.apg.Service;
-import org.apg.R;
-
-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.res.Configuration;
-import android.os.Bundle;
-import android.os.Environment;
-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 PausableThread mRunningThread = null;
- private Thread mDeletingThread = null;
-
- private long mSecretKeyId = 0;
- private String mDeleteFile = null;
-
- protected Preferences mPreferences;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- handlerCallback(msg);
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mPreferences = Preferences.getPreferences(this);
- setLanguage(this, mPreferences.getLanguage());
-
- Apg.initialize(this);
-
- if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
- File dir = new File(Constants.path.APP_DIR);
- if (!dir.exists() && !dir.mkdirs()) {
- // ignore this for now, it's not crucial
- // that the directory doesn't exist at this point
- }
- }
-
- startCacheService(this, mPreferences);
- }
-
- public static void startCacheService(Activity activity, Preferences preferences) {
- Intent intent = new Intent(activity, Service.class);
- intent.putExtra(Service.EXTRA_TTL, preferences.getPassPhraseCacheTtl());
- activity.startService(intent);
- }
-
- @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: {
- startActivity(new Intent(this, AboutActivity.class));
- return true;
- }
-
- case Id.menu.option.preferences: {
- startActivity(new Intent(this, PreferencesActivity.class));
- return true;
- }
-
- case Id.menu.option.search: {
- startSearch("", false, null, false);
- 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;
- }
-
- case Id.dialog.deleting: {
- mProgressDialog.setMessage(this.getString(R.string.progress_initializing));
- return mProgressDialog;
- }
-
- case Id.dialog.querying: {
- mProgressDialog.setMessage(this.getString(R.string.progress_querying));
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- case Id.dialog.signing: {
- mProgressDialog.setMessage(this.getString(R.string.progress_signing));
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- mProgressDialog.setCancelable(false);
- return mProgressDialog;
- }
-
- default: {
- break;
- }
- }
- mProgressDialog = null;
-
- switch (id) {
-
- 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);
- final File file = new File(getDeleteFile());
- showDialog(Id.dialog.deleting);
- mDeletingThread = new Thread(new Runnable() {
- public void run() {
- Bundle data = new Bundle();
- data.putInt(Constants.extras.STATUS, Id.message.delete_done);
- try {
- Apg.deleteFileSecurely(BaseActivity.this, file, BaseActivity.this);
- } catch (FileNotFoundException e) {
- data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
- R.string.error_fileNotFound, file));
- } catch (IOException e) {
- data.putString(Apg.EXTRA_ERROR, BaseActivity.this.getString(
- R.string.error_fileDeleteFailed, file));
- }
- Message msg = new Message();
- msg.setData(data);
- sendMessage(msg);
- }
- });
- mDeletingThread.start();
- }
- });
- 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(Apg.EXTRA_KEY_ID));
- } 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(Constants.extras.STATUS, Id.message.progress_update);
- data.putInt(Constants.extras.PROGRESS, progress);
- data.putInt(Constants.extras.PROGRESS_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(Constants.extras.STATUS, Id.message.progress_update);
- data.putString(Constants.extras.MESSAGE, message);
- data.putInt(Constants.extras.PROGRESS, progress);
- data.putInt(Constants.extras.PROGRESS_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(Constants.extras.STATUS);
- switch (type) {
- case Id.message.progress_update: {
- String message = data.getString(Constants.extras.MESSAGE);
- if (mProgressDialog != null) {
- if (message != null) {
- mProgressDialog.setMessage(message);
- }
- mProgressDialog.setMax(data.getInt(Constants.extras.PROGRESS_MAX));
- mProgressDialog.setProgress(data.getInt(Constants.extras.PROGRESS));
- }
- break;
- }
-
- case Id.message.delete_done: {
- mProgressDialog = null;
- deleteDoneCallback(msg);
- break;
- }
-
- case Id.message.import_done: // intentionally no break
- case Id.message.export_done: // intentionally no break
- case Id.message.query_done: // intentionally no break
- case Id.message.done: {
- mProgressDialog = null;
- doneCallback(msg);
- break;
- }
-
- default: {
- break;
- }
- }
- }
-
- public void doneCallback(Message msg) {
-
- }
-
- public void deleteDoneCallback(Message msg) {
- removeDialog(Id.dialog.deleting);
- mDeletingThread = null;
-
- Bundle data = msg.getData();
- String error = data.getString(Apg.EXTRA_ERROR);
- String message;
- if (error != null) {
- message = getString(R.string.errorMessage, error);
- } else {
- message = getString(R.string.fileDeleteSuccessful);
- }
-
- Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
- }
-
- public void passPhraseCallback(long keyId, String passPhrase) {
- Apg.setCachedPassPhrase(keyId, passPhrase);
- }
-
- public void sendMessage(Message msg) {
- mHandler.sendMessage(msg);
- }
-
- public PausableThread getRunningThread() {
- return mRunningThread;
- }
-
- public void startThread() {
- mRunningThread = new PausableThread(this);
- mRunningThread.start();
- }
-
- public void run() {
-
- }
-
- public void setSecretKeyId(long id) {
- mSecretKeyId = id;
- }
-
- public long getSecretKeyId() {
- return mSecretKeyId;
- }
-
- protected void setDeleteFile(String deleteFile) {
- mDeleteFile = deleteFile;
- }
-
- protected String getDeleteFile() {
- return mDeleteFile;
- }
-
- public static void setLanguage(Context context, String language) {
- Locale locale;
- if (language == null || language.equals("")) {
- locale = Locale.getDefault();
- } else {
- locale = new Locale(language);
- }
- Configuration config = new Configuration();
- config.locale = locale;
- context.getResources().updateConfiguration(config,
- context.getResources().getDisplayMetrics());
- }
-}
diff --git a/src/org/apg/ui/DecryptActivity.java b/src/org/apg/ui/DecryptActivity.java
deleted file mode 100644
index cb58dfb09..000000000
--- a/src/org/apg/ui/DecryptActivity.java
+++ /dev/null
@@ -1,807 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.DataDestination;
-import org.apg.DataSource;
-import org.apg.FileDialog;
-import org.apg.Id;
-import org.apg.InputData;
-import org.apg.PausableThread;
-import org.apg.provider.DataProvider;
-import org.apg.util.Compatibility;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.apg.R;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.ActivityNotFoundException;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Message;
-import android.util.Log;
-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;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileNotFoundException;
-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;
-
-public class DecryptActivity extends BaseActivity {
- private long mSignatureKeyId = 0;
-
- private Intent mIntent;
-
- private boolean mReturnResult = false;
- private String mReplyTo = null;
- private String mSubject = null;
- private boolean mSignedOnly = false;
- 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;
-
- private Uri mContentUri = null;
- private byte[] mData = null;
- private boolean mReturnBinary = false;
-
- private DataSource mDataSource = null;
- private DataDestination mDataDestination = null;
-
- private long mUnknownSignatureKeyId = 0;
-
- @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() {
- 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() {
- 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() {
- 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();
- }
-
- mIntent = getIntent();
- if (Intent.ACTION_VIEW.equals(mIntent.getAction())) {
- Uri uri = mIntent.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 = new String(byteOut.toByteArray());
- mMessage.setText(data);
- } catch (FileNotFoundException e) {
- // ignore, then
- } catch (IOException e) {
- // ignore, then
- }
- } else if (Apg.Intent.DECRYPT.equals(mIntent.getAction())) {
- Log.d(Constants.TAG, "Apg Intent DECRYPT startet");
- Bundle extras = mIntent.getExtras();
- if (extras == null) {
- Log.d(Constants.TAG, "extra bundle was null");
- extras = new Bundle();
- } else {
- Log.d(Constants.TAG, "got extras");
- }
-
- mData = extras.getByteArray(Apg.EXTRA_DATA);
- String textData = null;
- if (mData == null) {
- Log.d(Constants.TAG, "EXTRA_DATA was null");
- textData = extras.getString(Apg.EXTRA_TEXT);
- } else {
- Log.d(Constants.TAG, "Got data from EXTRA_DATA");
- }
- if (textData != null) {
- Log.d(Constants.TAG, "textData null, matching text ...");
- Matcher matcher = Apg.PGP_MESSAGE.matcher(textData);
- if (matcher.matches()) {
- Log.d(Constants.TAG, "PGP_MESSAGE matched");
- textData = matcher.group(1);
- // replace non breakable spaces
- textData = textData.replaceAll("\\xa0", " ");
- mMessage.setText(textData);
- } else {
- matcher = Apg.PGP_SIGNED_MESSAGE.matcher(textData);
- if (matcher.matches()) {
- Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
- textData = matcher.group(1);
- // replace non breakable spaces
- textData = textData.replaceAll("\\xa0", " ");
- mMessage.setText(textData);
- mDecryptButton.setText(R.string.btn_verify);
- } else {
- Log.d(Constants.TAG, "Nothing matched!");
- }
- }
- }
- mReplyTo = extras.getString(Apg.EXTRA_REPLY_TO);
- mSubject = extras.getString(Apg.EXTRA_SUBJECT);
- } else if (Apg.Intent.DECRYPT_FILE.equals(mIntent.getAction())) {
- mInputFilename = mIntent.getDataString();
- if ("file".equals(mIntent.getScheme())) {
- mInputFilename = Uri.decode(mInputFilename.substring(7));
- }
- mFilename.setText(mInputFilename);
- guessOutputFilename();
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceFile) {
- mSource.showNext();
- }
- } else if (Apg.Intent.DECRYPT_AND_RETURN.equals(mIntent.getAction())) {
- mContentUri = mIntent.getData();
- Bundle extras = mIntent.getExtras();
- if (extras == null) {
- extras = new Bundle();
- }
-
- mReturnBinary = extras.getBoolean(Apg.EXTRA_BINARY, false);
-
- if (mContentUri == null) {
- mData = extras.getByteArray(Apg.EXTRA_DATA);
- String data = extras.getString(Apg.EXTRA_TEXT);
- if (data != null) {
- Matcher matcher = Apg.PGP_MESSAGE.matcher(data);
- if (matcher.matches()) {
- data = matcher.group(1);
- // replace non breakable spaces
- data = data.replaceAll("\\xa0", " ");
- mMessage.setText(data);
- } else {
- matcher = Apg.PGP_SIGNED_MESSAGE.matcher(data);
- if (matcher.matches()) {
- data = matcher.group(1);
- // replace non breakable spaces
- data = data.replaceAll("\\xa0", " ");
- mMessage.setText(data);
- mDecryptButton.setText(R.string.btn_verify);
- }
- }
- }
- }
- mReturnResult = true;
- }
-
- if (mSource.getCurrentView().getId() == R.id.sourceMessage
- && mMessage.getText().length() == 0) {
-
- CharSequence clipboardText = Compatibility.getClipboardText(this);
-
- String data = "";
- if (clipboardText != null) {
- Matcher matcher = Apg.PGP_MESSAGE.matcher(clipboardText);
- if (!matcher.matches()) {
- matcher = Apg.PGP_SIGNED_MESSAGE.matcher(clipboardText);
- }
- if (matcher.matches()) {
- data = matcher.group(1);
- mMessage.setText(data);
- Toast.makeText(this, R.string.usingClipboardContent, Toast.LENGTH_SHORT).show();
- }
- }
- }
-
- mSignatureLayout.setVisibility(View.GONE);
- mSignatureLayout.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- if (mSignatureKeyId == 0) {
- return;
- }
- PGPPublicKeyRing key = Apg.getPublicKeyRing(mSignatureKeyId);
- if (key != null) {
- Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class);
- intent.setAction(Apg.Intent.LOOK_UP_KEY_ID);
- intent.putExtra(Apg.EXTRA_KEY_ID, mSignatureKeyId);
- startActivity(intent);
- }
- }
- });
-
- mDecryptButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- decryptClicked();
- }
- });
-
- mReplyButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- replyClicked();
- }
- });
- mReplyButton.setVisibility(View.INVISIBLE);
-
- if (mReturnResult) {
- mSourcePrevious.setClickable(false);
- mSourcePrevious.setEnabled(false);
- mSourcePrevious.setVisibility(View.INVISIBLE);
-
- mSourceNext.setClickable(false);
- mSourceNext.setEnabled(false);
- mSourceNext.setVisibility(View.INVISIBLE);
-
- mSourceLabel.setClickable(false);
- mSourceLabel.setEnabled(false);
- }
-
- updateSource();
-
- if (mSource.getCurrentView().getId() == R.id.sourceMessage
- && (mMessage.getText().length() > 0 || mData != null || mContentUri != null)) {
- mDecryptButton.performClick();
- }
- }
-
- private void openFile() {
- String filename = mFilename.getText().toString();
-
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
-
- intent.setData(Uri.parse("file://" + filename));
- intent.setType("*/*");
-
- try {
- startActivityForResult(intent, Id.request.filename);
- } catch (ActivityNotFoundException e) {
- // No compatible file manager was found.
- Toast.makeText(this, R.string.noFilemanagerInstalled, 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;
- }
-
- if (mInputFilename.startsWith("file")) {
- 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;
- fillDataSource();
- try {
- InputData in = mDataSource.getInputData(this, false);
- 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);
- in = mDataSource.getInputData(this, false);
- 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;
- } catch (Apg.GeneralException e) {
- error = "" + e;
- }
- 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(Apg.EXTRA_TEXT, data);
- intent.putExtra(Apg.EXTRA_SUBJECT, "Re: " + mSubject);
- intent.putExtra(Apg.EXTRA_SEND_TO, mReplyTo);
- intent.putExtra(Apg.EXTRA_SIGNATURE_KEY_ID, getSecretKeyId());
- intent.putExtra(Apg.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId });
- startActivity(intent);
- }
-
- 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();
- fillDataSource();
- fillDataDestination();
- try {
- InputData in = mDataSource.getInputData(this, true);
- OutputStream out = mDataDestination.getOutputStream(this);
-
- if (mSignedOnly) {
- data = Apg.verifyText(this, in, out, this);
- } else {
- data = Apg.decrypt(this, in, out, Apg.getCachedPassPhrase(getSecretKeyId()), this,
- mAssumeSymmetricEncryption);
- }
-
- out.close();
-
- if (mDataDestination.getStreamFilename() != null) {
- data.putString(Apg.EXTRA_RESULT_URI, "content://" + DataProvider.AUTHORITY
- + "/data/" + mDataDestination.getStreamFilename());
- } else if (mDecryptTarget == Id.target.message) {
- if (mReturnBinary) {
- data.putByteArray(Apg.EXTRA_DECRYPTED_DATA,
- ((ByteArrayOutputStream) out).toByteArray());
- } else {
- data.putString(Apg.EXTRA_DECRYPTED_MESSAGE, new String(
- ((ByteArrayOutputStream) out).toByteArray()));
- }
- }
- } catch (PGPException e) {
- error = "" + e;
- } catch (IOException e) {
- error = "" + e;
- } catch (SignatureException e) {
- error = "" + e;
- } catch (Apg.GeneralException e) {
- error = "" + e;
- }
-
- data.putInt(Constants.extras.STATUS, Id.message.done);
-
- if (error != null) {
- data.putString(Apg.EXTRA_ERROR, error);
- }
-
- msg.setData(data);
- sendMessage(msg);
- }
-
- @Override
- public void handlerCallback(Message msg) {
- Bundle data = msg.getData();
- if (data == null) {
- return;
- }
-
- if (data.getInt(Constants.extras.STATUS) == Id.message.unknown_signature_key) {
- mUnknownSignatureKeyId = data.getLong(Constants.extras.KEY_ID);
- showDialog(Id.dialog.lookup_unknown_key);
- return;
- }
-
- super.handlerCallback(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(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT)
- .show();
- return;
- }
-
- Toast.makeText(this, R.string.decryptionSuccessful, Toast.LENGTH_SHORT).show();
- if (mReturnResult) {
- Intent intent = new Intent();
- intent.putExtras(data);
- setResult(RESULT_OK, intent);
- finish();
- return;
- }
-
- switch (mDecryptTarget) {
- case Id.target.message: {
- String decryptedMessage = data.getString(Apg.EXTRA_DECRYPTED_MESSAGE);
- mMessage.setText(decryptedMessage);
- mMessage.setHorizontallyScrolling(false);
- 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(Apg.EXTRA_SIGNATURE)) {
- String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
- mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
- mUserIdRest.setText("id: " + Apg.getSmallFingerPrint(mSignatureKeyId));
- 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(Apg.EXTRA_SIGNATURE_SUCCESS)) {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
- } else if (data.getBoolean(Apg.EXTRA_SIGNATURE_UNKNOWN)) {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- Toast.makeText(this, R.string.unknownSignatureKeyTouchToLookUp, Toast.LENGTH_LONG)
- .show();
- } 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;
- }
-
- case Id.request.look_up_key_id: {
- PausableThread thread = getRunningThread();
- if (thread != null && thread.isPaused()) {
- thread.unpause();
- }
- 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() {
- public void onOkClick(String filename, boolean checked) {
- removeDialog(Id.dialog.output_filename);
- mOutputFilename = filename;
- decryptStart();
- }
-
- public void onCancelClick() {
- removeDialog(Id.dialog.output_filename);
- }
- }, getString(R.string.filemanager_titleSave),
- getString(R.string.filemanager_btnSave), null, Id.request.output_filename);
- }
-
- case Id.dialog.lookup_unknown_key: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setIcon(android.R.drawable.ic_dialog_alert);
- alert.setTitle(R.string.title_unknownSignatureKey);
- alert.setMessage(getString(R.string.lookupUnknownKey,
- Apg.getSmallFingerPrint(mUnknownSignatureKeyId)));
-
- alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.lookup_unknown_key);
- Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class);
- intent.setAction(Apg.Intent.LOOK_UP_KEY_ID);
- intent.putExtra(Apg.EXTRA_KEY_ID, mUnknownSignatureKeyId);
- startActivityForResult(intent, Id.request.look_up_key_id);
- }
- });
- alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.lookup_unknown_key);
- PausableThread thread = getRunningThread();
- if (thread != null && thread.isPaused()) {
- thread.unpause();
- }
- }
- });
- alert.setCancelable(true);
-
- return alert.create();
- }
-
- default: {
- break;
- }
- }
-
- return super.onCreateDialog(id);
- }
-
- protected void fillDataSource() {
- mDataSource = new DataSource();
- if (mContentUri != null) {
- mDataSource.setUri(mContentUri);
- } else if (mDecryptTarget == Id.target.file) {
- mDataSource.setUri(mInputFilename);
- } else {
- if (mData != null) {
- mDataSource.setData(mData);
- } else {
- mDataSource.setText(mMessage.getText().toString());
- }
- }
- }
-
- protected void fillDataDestination() {
- mDataDestination = new DataDestination();
- if (mContentUri != null) {
- mDataDestination.setMode(Id.mode.stream);
- } else if (mDecryptTarget == Id.target.file) {
- mDataDestination.setFilename(mOutputFilename);
- mDataDestination.setMode(Id.mode.file);
- } else {
- mDataDestination.setMode(Id.mode.byte_array);
- }
- }
-}
diff --git a/src/org/apg/ui/EditKeyActivity.java b/src/org/apg/ui/EditKeyActivity.java
deleted file mode 100644
index c3945d4ed..000000000
--- a/src/org/apg/ui/EditKeyActivity.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.Id;
-import org.apg.provider.Database;
-import org.apg.ui.widget.KeyEditor;
-import org.apg.ui.widget.SectionView;
-import org.apg.util.IterableIterator;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.apg.R;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SignatureException;
-import java.util.Vector;
-
-public class EditKeyActivity extends BaseActivity implements OnClickListener {
-
- private PGPSecretKeyRing mKeyRing = null;
-
- private SectionView mUserIds;
- private SectionView mKeys;
-
- private Button mSaveButton;
- private Button mDiscardButton;
-
- private String mCurrentPassPhrase = null;
- private String mNewPassPhrase = null;
-
- private Button mChangePassPhrase;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.edit_key);
-
- Vector userIds = new Vector();
- Vector keys = new Vector();
-
- Intent intent = getIntent();
- long keyId = 0;
- if (intent.getExtras() != null) {
- keyId = intent.getExtras().getLong(Apg.EXTRA_KEY_ID);
- }
-
- if (keyId != 0) {
- PGPSecretKey masterKey = null;
- mKeyRing = Apg.getSecretKeyRing(keyId);
- if (mKeyRing != null) {
- masterKey = Apg.getMasterKey(mKeyRing);
- for (PGPSecretKey key : new IterableIterator(mKeyRing.getSecretKeys())) {
- keys.add(key);
- }
- }
- if (masterKey != null) {
- for (String userId : new IterableIterator(masterKey.getUserIDs())) {
- userIds.add(userId);
- }
- }
- }
-
- mChangePassPhrase = (Button) findViewById(R.id.btn_change_pass_phrase);
- mChangePassPhrase.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- showDialog(Id.dialog.new_pass_phrase);
- }
- });
-
- mSaveButton = (Button) findViewById(R.id.btn_save);
- mDiscardButton = (Button) findViewById(R.id.btn_discard);
-
- mSaveButton.setOnClickListener(this);
- mDiscardButton.setOnClickListener(this);
-
- LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- LinearLayout container = (LinearLayout) findViewById(R.id.container);
- mUserIds = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
- 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(Id.type.key);
- mKeys.setKeys(keys);
- container.addView(mKeys);
-
- mCurrentPassPhrase = Apg.getEditPassPhrase();
- if (mCurrentPassPhrase == null) {
- mCurrentPassPhrase = "";
- }
-
- updatePassPhraseButtonText();
-
- Toast.makeText(this,
- getString(R.string.warningMessage, getString(R.string.keyEditingIsBeta)),
- Toast.LENGTH_LONG).show();
- }
-
- private long getMasterKeyId() {
- if (mKeys.getEditors().getChildCount() == 0) {
- return 0;
- }
- return ((KeyEditor) mKeys.getEditors().getChildAt(0)).getValue().getKeyID();
- }
-
- public boolean havePassPhrase() {
- return (!mCurrentPassPhrase.equals(""))
- || (mNewPassPhrase != null && !mNewPassPhrase.equals(""));
- }
-
- @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
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case Id.dialog.new_pass_phrase: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- if (havePassPhrase()) {
- alert.setTitle(R.string.title_changePassPhrase);
- } else {
- alert.setTitle(R.string.title_setPassPhrase);
- }
- 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(Id.dialog.new_pass_phrase);
-
- String passPhrase1 = "" + input1.getText();
- String passPhrase2 = "" + input2.getText();
- if (!passPhrase1.equals(passPhrase2)) {
- showDialog(Id.dialog.pass_phrases_do_not_match);
- return;
- }
-
- if (passPhrase1.equals("")) {
- showDialog(Id.dialog.no_pass_phrase);
- return;
- }
-
- mNewPassPhrase = passPhrase1;
- updatePassPhraseButtonText();
- }
- });
-
- alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.new_pass_phrase);
- }
- });
-
- return alert.create();
- }
-
- default: {
- return super.onCreateDialog(id);
- }
- }
- }
-
- public void onClick(View v) {
- if (v == mSaveButton) {
- // TODO: some warning
- saveClicked();
- } else if (v == mDiscardButton) {
- finish();
- }
- }
-
- private void saveClicked() {
- if (!havePassPhrase()) {
- Toast.makeText(this, R.string.setAPassPhrase, Toast.LENGTH_SHORT).show();
- return;
- }
- showDialog(Id.dialog.saving);
- startThread();
- }
-
- @Override
- public void run() {
- String error = null;
- Bundle data = new Bundle();
- Message msg = new Message();
-
- try {
- String oldPassPhrase = mCurrentPassPhrase;
- String newPassPhrase = mNewPassPhrase;
- if (newPassPhrase == null) {
- newPassPhrase = oldPassPhrase;
- }
- Apg.buildSecretKey(this, mUserIds, mKeys, oldPassPhrase, newPassPhrase, this);
- Apg.setCachedPassPhrase(getMasterKeyId(), newPassPhrase);
- } catch (NoSuchProviderException e) {
- error = "" + e;
- } catch (NoSuchAlgorithmException e) {
- error = "" + e;
- } catch (PGPException e) {
- error = "" + e;
- } catch (SignatureException e) {
- error = "" + e;
- } catch (Apg.GeneralException e) {
- error = "" + e;
- } catch (Database.GeneralException e) {
- error = "" + e;
- } catch (IOException e) {
- error = "" + e;
- }
-
- data.putInt(Constants.extras.STATUS, Id.message.done);
-
- if (error != null) {
- data.putString(Apg.EXTRA_ERROR, error);
- }
-
- msg.setData(data);
- sendMessage(msg);
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- Bundle data = msg.getData();
- removeDialog(Id.dialog.saving);
-
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(EditKeyActivity.this, getString(R.string.errorMessage, error),
- Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(EditKeyActivity.this, R.string.keySaved, Toast.LENGTH_SHORT).show();
- setResult(RESULT_OK);
- finish();
- }
- }
-
- private void updatePassPhraseButtonText() {
- mChangePassPhrase.setText(havePassPhrase() ? R.string.btn_changePassPhrase
- : R.string.btn_setPassPhrase);
- }
-}
diff --git a/src/org/apg/ui/EncryptActivity.java b/src/org/apg/ui/EncryptActivity.java
deleted file mode 100644
index e5892a4d5..000000000
--- a/src/org/apg/ui/EncryptActivity.java
+++ /dev/null
@@ -1,998 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.DataDestination;
-import org.apg.DataSource;
-import org.apg.FileDialog;
-import org.apg.Id;
-import org.apg.InputData;
-import org.apg.provider.DataProvider;
-import org.apg.util.Choice;
-import org.apg.util.Compatibility;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.apg.R;
-
-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.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;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SignatureException;
-import java.util.Vector;
-
-public class EncryptActivity extends BaseActivity {
- private Intent mIntent = null;
- private String mSubject = null;
- private String mSendTo = null;
-
- private long mEncryptionKeyIds[] = null;
-
- private boolean mReturnResult = false;
- private EditText mMessage = null;
- private Button mSelectKeysButton = null;
- private Button mEncryptButton = null;
- 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;
-
- private boolean mAsciiArmourDemand = false;
- private boolean mOverrideAsciiArmour = false;
- private Uri mContentUri = null;
- private byte[] mData = null;
-
- private DataSource mDataSource = null;
- private DataDestination mDataDestination = null;
-
- private boolean mGenerateSignature = false;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.encrypt);
-
- mGenerateSignature = false;
-
- 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() {
- 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() {
- 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() {
- 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() {
- 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() {
- 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) + " ("
- + getString(R.string.fast) + ")"),
- new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.fast) + ")"),
- new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.fast) + ")"),
- new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.very_slow)
- + ")"), };
- ArrayAdapter adapter = new ArrayAdapter(this,
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mFileCompression.setAdapter(adapter);
-
- int defaultFileCompression = mPreferences.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(mPreferences.getDefaultAsciiArmour());
- mAsciiArmour.setOnClickListener(new OnClickListener() {
- public void onClick(View view) {
- guessOutputFilename();
- }
- });
-
- mEncryptToClipboardButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- encryptToClipboardClicked();
- }
- });
-
- mEncryptButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- encryptClicked();
- }
- });
-
- mSelectKeysButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- selectPublicKeys();
- }
- });
-
- mSign.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- CheckBox checkBox = (CheckBox) v;
- if (checkBox.isChecked()) {
- selectSecretKey();
- } else {
- setSecretKeyId(Id.key.none);
- updateView();
- }
- }
- });
-
- mIntent = getIntent();
- if (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
- || Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())
- || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
- || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
- mContentUri = mIntent.getData();
- Bundle extras = mIntent.getExtras();
- if (extras == null) {
- extras = new Bundle();
- }
-
- if (Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
- || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
- mReturnResult = true;
- }
-
- if (Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
- mGenerateSignature = true;
- mOverrideAsciiArmour = true;
- mAsciiArmourDemand = false;
- }
-
- if (extras.containsKey(Apg.EXTRA_ASCII_ARMOUR)) {
- mAsciiArmourDemand = extras.getBoolean(Apg.EXTRA_ASCII_ARMOUR, true);
- mOverrideAsciiArmour = true;
- mAsciiArmour.setChecked(mAsciiArmourDemand);
- }
-
- mData = extras.getByteArray(Apg.EXTRA_DATA);
- String textData = null;
- if (mData == null) {
- textData = extras.getString(Apg.EXTRA_TEXT);
- }
- mSendTo = extras.getString(Apg.EXTRA_SEND_TO);
- mSubject = extras.getString(Apg.EXTRA_SUBJECT);
- long signatureKeyId = extras.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
- long encryptionKeyIds[] = extras.getLongArray(Apg.EXTRA_ENCRYPTION_KEY_IDS);
- if (signatureKeyId != 0) {
- PGPSecretKeyRing keyRing = Apg.getSecretKeyRing(signatureKeyId);
- PGPSecretKey masterKey = null;
- if (keyRing != null) {
- masterKey = Apg.getMasterKey(keyRing);
- if (masterKey != null) {
- Vector signKeys = Apg.getUsableSigningKeys(keyRing);
- if (signKeys.size() > 0) {
- setSecretKeyId(masterKey.getKeyID());
- }
- }
- }
- }
-
- if (encryptionKeyIds != null) {
- Vector goodIds = new Vector();
- for (int i = 0; i < encryptionKeyIds.length; ++i) {
- PGPPublicKeyRing keyRing = Apg.getPublicKeyRing(encryptionKeyIds[i]);
- PGPPublicKey masterKey = null;
- if (keyRing == null) {
- continue;
- }
- masterKey = Apg.getMasterKey(keyRing);
- if (masterKey == null) {
- continue;
- }
- Vector 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 (Apg.Intent.ENCRYPT.equals(mIntent.getAction())
- || Apg.Intent.ENCRYPT_AND_RETURN.equals(mIntent.getAction())
- || Apg.Intent.GENERATE_SIGNATURE.equals(mIntent.getAction())) {
- if (textData != null) {
- mMessage.setText(textData);
- }
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
- mSource.showNext();
- }
- } else if (Apg.Intent.ENCRYPT_FILE.equals(mIntent.getAction())) {
- if ("file".equals(mIntent.getScheme())) {
- mInputFilename = Uri.decode(mIntent.getDataString().replace("file://", ""));
- mFilename.setText(mInputFilename);
- guessOutputFilename();
- }
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceFile) {
- mSource.showNext();
- }
- }
- }
-
- updateView();
- updateSource();
- updateMode();
-
- if (mReturnResult) {
- mSourcePrevious.setClickable(false);
- mSourcePrevious.setEnabled(false);
- mSourcePrevious.setVisibility(View.INVISIBLE);
-
- mSourceNext.setClickable(false);
- mSourceNext.setEnabled(false);
- mSourceNext.setVisibility(View.INVISIBLE);
-
- mSourceLabel.setClickable(false);
- mSourceLabel.setEnabled(false);
- }
-
- updateButtons();
-
- if (mReturnResult
- && (mMessage.getText().length() > 0 || mData != null || mContentUri != null)
- && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || getSecretKeyId() != 0)) {
- encryptClicked();
- }
- }
-
- private void openFile() {
- String filename = mFilename.getText().toString();
-
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
-
- intent.setData(Uri.parse("file://" + filename));
- intent.setType("*/*");
-
- try {
- startActivityForResult(intent, Id.request.filename);
- } catch (ActivityNotFoundException e) {
- // No compatible file manager was found.
- Toast.makeText(this, R.string.noFilemanagerInstalled, 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);
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
- break;
- }
-
- default: {
- break;
- }
- }
- updateButtons();
- }
-
- private void updateButtons() {
- switch (mSource.getCurrentView().getId()) {
- case R.id.sourceFile: {
- mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
- mEncryptButton.setText(R.string.btn_encrypt);
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
- if (mReturnResult) {
- mEncryptToClipboardButton.setVisibility(View.INVISIBLE);
- } else {
- mEncryptToClipboardButton.setVisibility(View.VISIBLE);
- }
- if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
- if (mReturnResult) {
- mEncryptButton.setText(R.string.btn_encrypt);
- } else {
- mEncryptButton.setText(R.string.btn_encryptAndEmail);
- }
- mEncryptButton.setEnabled(true);
- mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
- mEncryptToClipboardButton.setEnabled(true);
- } else {
- if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- if (getSecretKeyId() == 0) {
- if (mReturnResult) {
- mEncryptButton.setText(R.string.btn_encrypt);
- } else {
- mEncryptButton.setText(R.string.btn_encryptAndEmail);
- }
- mEncryptButton.setEnabled(false);
- mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
- mEncryptToClipboardButton.setEnabled(false);
- } else {
- if (mReturnResult) {
- mEncryptButton.setText(R.string.btn_sign);
- } else {
- mEncryptButton.setText(R.string.btn_signAndEmail);
- }
- mEncryptButton.setEnabled(true);
- mEncryptToClipboardButton.setText(R.string.btn_signToClipboard);
- mEncryptToClipboardButton.setEnabled(true);
- }
- } else {
- if (mReturnResult) {
- mEncryptButton.setText(R.string.btn_encrypt);
- } else {
- mEncryptButton.setText(R.string.btn_encryptAndEmail);
- }
- mEncryptButton.setEnabled(true);
- mEncryptToClipboardButton.setText(R.string.btn_encryptToClipboard);
- 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;
- }
- }
- updateButtons();
- }
-
- 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;
- }
-
- if (!mInputFilename.startsWith("content")) {
- 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 {
- InputData in;
- OutputStream out;
- boolean useAsciiArmour = true;
- long encryptionKeyIds[] = null;
- long signatureKeyId = 0;
- int compressionId = 0;
- boolean signOnly = false;
-
- 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);
- }
-
- fillDataSource(signOnly && !mReturnResult);
- fillDataDestination();
-
- // streams
- in = mDataSource.getInputData(this, true);
- out = mDataDestination.getOutputStream(this);
-
- if (mEncryptTarget == Id.target.file) {
- useAsciiArmour = mAsciiArmour.isChecked();
- compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
- } else {
- useAsciiArmour = true;
- compressionId = mPreferences.getDefaultMessageCompression();
- }
-
- if (mOverrideAsciiArmour) {
- useAsciiArmour = mAsciiArmourDemand;
- }
-
- if (mGenerateSignature) {
- Apg.generateSignature(this, in, out, useAsciiArmour, mDataSource.isBinary(),
- getSecretKeyId(), Apg.getCachedPassPhrase(getSecretKeyId()),
- mPreferences.getDefaultHashAlgorithm(),
- mPreferences.getForceV3Signatures(), this);
- } else if (signOnly) {
- Apg.signText(this, in, out, getSecretKeyId(),
- Apg.getCachedPassPhrase(getSecretKeyId()),
- mPreferences.getDefaultHashAlgorithm(),
- mPreferences.getForceV3Signatures(), this);
- } else {
- Apg.encrypt(this, in, out, useAsciiArmour, encryptionKeyIds, signatureKeyId,
- Apg.getCachedPassPhrase(signatureKeyId), this,
- mPreferences.getDefaultEncryptionAlgorithm(),
- mPreferences.getDefaultHashAlgorithm(), compressionId,
- mPreferences.getForceV3Signatures(), passPhrase);
- }
-
- out.close();
- if (mEncryptTarget != Id.target.file) {
-
- if (out instanceof ByteArrayOutputStream) {
- if (useAsciiArmour) {
- String extraData = new String(((ByteArrayOutputStream) out).toByteArray());
- if (mGenerateSignature) {
- data.putString(Apg.EXTRA_SIGNATURE_TEXT, extraData);
- } else {
- data.putString(Apg.EXTRA_ENCRYPTED_MESSAGE, extraData);
- }
- } else {
- byte extraData[] = ((ByteArrayOutputStream) out).toByteArray();
- if (mGenerateSignature) {
- data.putByteArray(Apg.EXTRA_SIGNATURE_DATA, extraData);
- } else {
- data.putByteArray(Apg.EXTRA_ENCRYPTED_DATA, extraData);
- }
- }
- } else if (out instanceof FileOutputStream) {
- String fileName = mDataDestination.getStreamFilename();
- String uri = "content://" + DataProvider.AUTHORITY + "/data/" + fileName;
- data.putString(Apg.EXTRA_RESULT_URI, uri);
- } else {
- throw new Apg.GeneralException("No output-data found.");
- }
- }
- } catch (IOException e) {
- error = "" + e;
- } catch (PGPException e) {
- error = "" + e;
- } catch (NoSuchProviderException e) {
- error = "" + e;
- } catch (NoSuchAlgorithmException e) {
- error = "" + e;
- } catch (SignatureException e) {
- error = "" + e;
- } catch (Apg.GeneralException e) {
- error = "" + e;
- }
-
- data.putInt(Constants.extras.STATUS, Id.message.done);
-
- if (error != null) {
- data.putString(Apg.EXTRA_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);
- }
-
- updateButtons();
- }
-
- private void selectPublicKeys() {
- Intent intent = new Intent(this, SelectPublicKeyListActivity.class);
- Vector keyIds = new Vector();
- if (getSecretKeyId() != 0) {
- keyIds.add(getSecretKeyId());
- }
- if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
- for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
- keyIds.add(mEncryptionKeyIds[i]);
- }
- }
- long[] initialKeyIds = null;
- if (keyIds.size() > 0) {
- initialKeyIds = new long[keyIds.size()];
- for (int i = 0; i < keyIds.size(); ++i) {
- initialKeyIds[i] = keyIds.get(i);
- }
- }
- intent.putExtra(Apg.EXTRA_SELECTION, initialKeyIds);
- 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(Apg.EXTRA_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(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT)
- .show();
- return;
- }
- switch (mEncryptTarget) {
- case Id.target.clipboard: {
- String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
- Compatibility.copyToClipboard(this, message);
- Toast.makeText(this, R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT)
- .show();
- break;
- }
-
- case Id.target.email: {
- if (mReturnResult) {
- Intent intent = new Intent();
- intent.putExtras(data);
- setResult(RESULT_OK, intent);
- finish();
- return;
- }
-
- String message = data.getString(Apg.EXTRA_ENCRYPTED_MESSAGE);
- Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
- emailIntent.setType("text/plain; charset=utf-8");
- emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
- if (mSubject != null) {
- emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, mSubject);
- }
- if (mSendTo != null) {
- emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { mSendTo });
- }
- EncryptActivity.this.startActivity(Intent.createChooser(emailIntent,
- getString(R.string.title_sendEmail)));
- break;
- }
-
- 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() {
- public void onOkClick(String filename, boolean checked) {
- removeDialog(Id.dialog.output_filename);
- mOutputFilename = filename;
- encryptStart();
- }
-
- public void onCancelClick() {
- removeDialog(Id.dialog.output_filename);
- }
- }, getString(R.string.filemanager_titleSave),
- getString(R.string.filemanager_btnSave), null, Id.request.output_filename);
- }
-
- default: {
- break;
- }
- }
-
- return super.onCreateDialog(id);
- }
-
- protected void fillDataSource(boolean fixContent) {
- mDataSource = new DataSource();
- if (mContentUri != null) {
- mDataSource.setUri(mContentUri);
- } else if (mEncryptTarget == Id.target.file) {
- mDataSource.setUri(mInputFilename);
- } else {
- if (mData != null) {
- mDataSource.setData(mData);
- } else {
- String message = mMessage.getText().toString();
- if (fixContent) {
- // 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");
- }
- mDataSource.setText(message);
- }
- }
- }
-
- protected void fillDataDestination() {
- mDataDestination = new DataDestination();
- if (mContentUri != null) {
- mDataDestination.setMode(Id.mode.stream);
- } else if (mEncryptTarget == Id.target.file) {
- mDataDestination.setFilename(mOutputFilename);
- mDataDestination.setMode(Id.mode.file);
- } else {
- mDataDestination.setMode(Id.mode.byte_array);
- }
- }
-}
diff --git a/src/org/apg/ui/GeneralActivity.java b/src/org/apg/ui/GeneralActivity.java
deleted file mode 100644
index d70694630..000000000
--- a/src/org/apg/ui/GeneralActivity.java
+++ /dev/null
@@ -1,177 +0,0 @@
-package org.apg.ui;
-
-import java.io.ByteArrayInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Vector;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.util.Choice;
-import org.apg.R;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.Toast;
-
-public class GeneralActivity extends BaseActivity {
- private Intent mIntent;
- private ArrayAdapter mAdapter;
- private ListView mList;
- private Button mCancelButton;
- private String mDataString;
- private Uri mDataUri;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.general);
-
- mIntent = getIntent();
-
- InputStream inStream = null;
- {
- String data = mIntent.getStringExtra(Intent.EXTRA_TEXT);
- if (data != null) {
- mDataString = data;
- inStream = new ByteArrayInputStream(data.getBytes());
- }
- }
-
- if (inStream == null) {
- Uri data = mIntent.getData();
- if (data != null) {
- mDataUri = data;
- try {
- inStream = getContentResolver().openInputStream(data);
- } catch (FileNotFoundException e) {
- // didn't work
- Toast.makeText(this, "failed to open stream", Toast.LENGTH_SHORT).show();
- }
- }
- }
-
- if (inStream == null) {
- Toast.makeText(this, "no data found", Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
-
- int contentType = Id.content.unknown;
- try {
- contentType = Apg.getStreamContent(this, inStream);
- inStream.close();
- } catch (IOException e) {
- // just means that there's no PGP data in there
- }
-
- mList = (ListView) findViewById(R.id.options);
- Vector choices = new Vector();
-
- if (contentType == Id.content.keys) {
- choices.add(new Choice(Id.choice.action.import_public,
- getString(R.string.action_importPublic)));
- choices.add(new Choice(Id.choice.action.import_secret,
- getString(R.string.action_importSecret)));
- }
-
- if (contentType == Id.content.encrypted_data) {
- choices.add(new Choice(Id.choice.action.decrypt, getString(R.string.action_decrypt)));
- }
-
- if (contentType == Id.content.unknown) {
- choices.add(new Choice(Id.choice.action.encrypt, getString(R.string.action_encrypt)));
- }
-
- mAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, choices);
- mList.setAdapter(mAdapter);
-
- mList.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView> arg0, View arg1, int arg2, long arg3) {
- clicked(mAdapter.getItem(arg2).getId());
- }
- });
-
- mCancelButton = (Button) findViewById(R.id.btn_cancel);
- mCancelButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- GeneralActivity.this.finish();
- }
- });
-
- if (choices.size() == 1) {
- clicked(choices.get(0).getId());
- }
- }
-
- private void clicked(int id) {
- Intent intent = new Intent();
- switch (id) {
- case Id.choice.action.encrypt: {
- intent.setClass(this, EncryptActivity.class);
- if (mDataString != null) {
- intent.setAction(Apg.Intent.ENCRYPT);
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setAction(Apg.Intent.ENCRYPT_FILE);
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
-
- break;
- }
-
- case Id.choice.action.decrypt: {
- intent.setClass(this, DecryptActivity.class);
- if (mDataString != null) {
- intent.setAction(Apg.Intent.DECRYPT);
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setAction(Apg.Intent.DECRYPT_FILE);
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
-
- break;
- }
-
- case Id.choice.action.import_public: {
- intent.setClass(this, PublicKeyListActivity.class);
- intent.setAction(Apg.Intent.IMPORT);
- if (mDataString != null) {
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
- break;
- }
-
- case Id.choice.action.import_secret: {
- intent.setClass(this, SecretKeyListActivity.class);
- intent.setAction(Apg.Intent.IMPORT);
- if (mDataString != null) {
- intent.putExtra(Apg.EXTRA_TEXT, mDataString);
- } else if (mDataUri != null) {
- intent.setDataAndType(mDataUri, mIntent.getType());
- }
- break;
- }
-
- default: {
- // shouldn't happen
- return;
- }
- }
-
- startActivity(intent);
- finish();
- }
-}
diff --git a/src/org/apg/ui/ImportFromQRCodeActivity.java b/src/org/apg/ui/ImportFromQRCodeActivity.java
deleted file mode 100644
index 593c841df..000000000
--- a/src/org/apg/ui/ImportFromQRCodeActivity.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package org.apg.ui;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.HkpKeyServer;
-import org.apg.Id;
-import org.apg.KeyServer.QueryException;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.apg.R;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.google.zxing.integration.android.IntentIntegrator;
-import com.google.zxing.integration.android.IntentResult;
-
-public class ImportFromQRCodeActivity extends BaseActivity {
- private static final String TAG = "ImportFromQRCodeActivity";
-
- private final Bundle status = new Bundle();
- private final Message msg = new Message();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- new IntentIntegrator(this).initiateScan();
- }
-
- private void importAndSign(final long keyId, final String expectedFingerprint) {
- if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
-
- Thread t = new Thread() {
- @Override
- public void run() {
- try {
- // TODO: display some sort of spinner here while the user waits
-
- HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]); // TODO: there should be only 1
- String encodedKey = server.get(keyId);
-
- PGPKeyRing keyring = Apg.decodeKeyRing(new ByteArrayInputStream(encodedKey.getBytes()));
- if (keyring != null && keyring instanceof PGPPublicKeyRing) {
- PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
-
- // make sure the fingerprints match before we cache this thing
- String actualFingerprint = Apg.convertToHex(publicKeyRing.getPublicKey().getFingerprint());
- if (expectedFingerprint.equals(actualFingerprint)) {
- // store the signed key in our local cache
- int retval = Apg.storeKeyRingInCache(publicKeyRing);
- if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
- status.putString(Apg.EXTRA_ERROR, "Failed to store signed key in local cache");
- } else {
- Intent intent = new Intent(ImportFromQRCodeActivity.this, SignKeyActivity.class);
- intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
- startActivityForResult(intent, Id.request.sign_key);
- }
- } else {
- status.putString(Apg.EXTRA_ERROR, "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
- }
- }
- } catch (QueryException e) {
- Log.e(TAG, "Failed to query KeyServer", e);
- status.putString(Apg.EXTRA_ERROR, "Failed to query KeyServer");
- status.putInt(Constants.extras.STATUS, Id.message.done);
- } catch (IOException e) {
- Log.e(TAG, "Failed to query KeyServer", e);
- status.putString(Apg.EXTRA_ERROR, "Failed to query KeyServer");
- status.putInt(Constants.extras.STATUS, Id.message.done);
- }
- }
- };
-
- t.setName("KeyExchange Download Thread");
- t.setDaemon(true);
- t.start();
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case IntentIntegrator.REQUEST_CODE: {
- boolean debug = true; // TODO: remove this!!!
- IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
- if (debug || (scanResult != null && scanResult.getFormatName() != null)) {
- String[] bits = debug ? new String[] { "5993515643896327656", "0816 F68A 6816 68FB 01BF 2CA5 532D 3EB9 1E2F EDE8" } : scanResult.getContents().split(",");
- if (bits.length != 2) {
- return; // dont know how to handle this. Not a valid code
- }
-
- long keyId = Long.parseLong(bits[0]);
- String expectedFingerprint = bits[1];
-
- importAndSign(keyId, expectedFingerprint);
-
- break;
- }
- }
-
- case Id.request.sign_key: {
- // signals the end of processing. Signature was either applied, or it wasnt
- status.putInt(Constants.extras.STATUS, Id.message.done);
-
- msg.setData(status);
- sendMessage(msg);
-
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- Bundle data = msg.getData();
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
- return;
- }
-
- Toast.makeText(this, R.string.keySignSuccess, Toast.LENGTH_SHORT).show(); // TODO
- finish();
- }
-}
diff --git a/src/org/apg/ui/KeyListActivity.java b/src/org/apg/ui/KeyListActivity.java
deleted file mode 100644
index 6c76f02bc..000000000
--- a/src/org/apg/ui/KeyListActivity.java
+++ /dev/null
@@ -1,768 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.FileDialog;
-import org.apg.Id;
-import org.apg.InputData;
-import org.apg.provider.KeyRings;
-import org.apg.provider.Keys;
-import org.apg.provider.UserIds;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.apg.R;
-
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.SearchManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Message;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.BaseExpandableListAdapter;
-import android.widget.Button;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.io.ByteArrayInputStream;
-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.util.Vector;
-
-public class KeyListActivity extends BaseActivity {
- protected ExpandableListView mList;
- protected KeyListAdapter mListAdapter;
- protected View mFilterLayout;
- protected Button mClearFilterButton;
- protected TextView mFilterInfo;
-
- protected int mSelectedItem = -1;
- protected int mTask = 0;
-
- protected String mImportFilename = Constants.path.APP_DIR + "/";
- protected String mExportFilename = Constants.path.APP_DIR + "/";
-
- protected String mImportData;
- protected boolean mDeleteAfterImport = false;
-
- protected int mKeyType = Id.type.public_key;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.key_list);
-
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- mList = (ExpandableListView) findViewById(R.id.list);
- registerForContextMenu(mList);
-
- mFilterLayout = findViewById(R.id.layout_filter);
- mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
- mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
-
- mClearFilterButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- handleIntent(new Intent());
- }
- });
-
- handleIntent(getIntent());
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- protected void handleIntent(Intent intent) {
- String searchString = null;
- if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- searchString = intent.getStringExtra(SearchManager.QUERY);
- if (searchString != null && searchString.trim().length() == 0) {
- searchString = null;
- }
- }
-
- if (searchString == null) {
- mFilterLayout.setVisibility(View.GONE);
- } else {
- mFilterLayout.setVisibility(View.VISIBLE);
- mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- }
-
- if (mListAdapter != null) {
- mListAdapter.cleanup();
- }
- mListAdapter = new KeyListAdapter(this, searchString);
- mList.setAdapter(mListAdapter);
-
- if (Apg.Intent.IMPORT.equals(intent.getAction())) {
- if ("file".equals(intent.getScheme()) && intent.getDataString() != null) {
- mImportFilename = Uri.decode(intent.getDataString().replace("file://", ""));
- } else {
- mImportData = intent.getStringExtra(Apg.EXTRA_TEXT);
- }
- importKeys();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case Id.menu.option.import_keys: {
- showDialog(Id.dialog.import_keys);
- return true;
- }
-
- case Id.menu.option.export_keys: {
- showDialog(Id.dialog.export_keys);
- return true;
- }
-
- default: {
- return super.onOptionsItemSelected(item);
- }
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
- int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
-
- if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- return super.onContextItemSelected(menuItem);
- }
-
- switch (menuItem.getItemId()) {
- case Id.menu.export: {
- mSelectedItem = groupPosition;
- showDialog(Id.dialog.export_key);
- return true;
- }
-
- case Id.menu.delete: {
- mSelectedItem = groupPosition;
- showDialog(Id.dialog.delete_key);
- return true;
- }
-
- default: {
- return super.onContextItemSelected(menuItem);
- }
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- boolean singleKeyExport = false;
-
- switch (id) {
- case Id.dialog.delete_key: {
- final int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
- mSelectedItem = -1;
- // TODO: better way to do this?
- String userId = "";
- Object keyRing = Apg.getKeyRing(keyRingId);
- if (keyRing != null) {
- if (keyRing instanceof PGPPublicKeyRing) {
- userId = Apg.getMainUserIdSafe(this,
- Apg.getMasterKey((PGPPublicKeyRing) keyRing));
- } else {
- userId = Apg.getMainUserIdSafe(this,
- Apg.getMasterKey((PGPSecretKeyRing) keyRing));
- }
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.warning);
- builder.setMessage(getString(
- mKeyType == Id.type.public_key ? R.string.keyDeletionConfirmation
- : R.string.secretKeyDeletionConfirmation, userId));
- builder.setIcon(android.R.drawable.ic_dialog_alert);
- builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- deleteKey(keyRingId);
- removeDialog(Id.dialog.delete_key);
- }
- });
- builder.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- removeDialog(Id.dialog.delete_key);
- }
- });
- return builder.create();
- }
-
- case Id.dialog.import_keys: {
- return FileDialog.build(this, getString(R.string.title_importKeys),
- getString(R.string.specifyFileToImportFrom), mImportFilename,
- new FileDialog.OnClickListener() {
- public void onOkClick(String filename, boolean checked) {
- removeDialog(Id.dialog.import_keys);
- mDeleteAfterImport = checked;
- mImportFilename = filename;
- importKeys();
- }
-
- public void onCancelClick() {
- removeDialog(Id.dialog.import_keys);
- }
- }, getString(R.string.filemanager_titleOpen),
- getString(R.string.filemanager_btnOpen),
- getString(R.string.label_deleteAfterImport), Id.request.filename);
- }
-
- case Id.dialog.export_key: {
- singleKeyExport = true;
- // break intentionally omitted, to use the Id.dialog.export_keys dialog
- }
-
- case Id.dialog.export_keys: {
- String title = (singleKeyExport ? getString(R.string.title_exportKey)
- : getString(R.string.title_exportKeys));
-
- final int thisDialogId = (singleKeyExport ? Id.dialog.export_key
- : Id.dialog.export_keys);
-
- return FileDialog.build(this, title,
- getString(mKeyType == Id.type.public_key ? R.string.specifyFileToExportTo
- : R.string.specifyFileToExportSecretKeysTo), mExportFilename,
- new FileDialog.OnClickListener() {
- public void onOkClick(String filename, boolean checked) {
- removeDialog(thisDialogId);
- mExportFilename = filename;
- exportKeys();
- }
-
- public void onCancelClick() {
- removeDialog(thisDialogId);
- }
- }, getString(R.string.filemanager_titleSave),
- getString(R.string.filemanager_btnSave), null, Id.request.filename);
- }
-
- default: {
- return super.onCreateDialog(id);
- }
- }
- }
-
- public void importKeys() {
- showDialog(Id.dialog.importing);
- mTask = Id.task.import_keys;
- startThread();
- }
-
- public void exportKeys() {
- showDialog(Id.dialog.exporting);
- mTask = Id.task.export_keys;
- startThread();
- }
-
- @Override
- public void run() {
- String error = null;
- Bundle data = new Bundle();
- Message msg = new Message();
-
- try {
- InputStream importInputStream = null;
- OutputStream exportOutputStream = null;
- long size = 0;
- if (mTask == Id.task.import_keys) {
- if (mImportData != null) {
- byte[] bytes = mImportData.getBytes();
- size = bytes.length;
- importInputStream = new ByteArrayInputStream(bytes);
- } else {
- File file = new File(mImportFilename);
- size = file.length();
- importInputStream = new FileInputStream(file);
- }
- } else {
- exportOutputStream = new FileOutputStream(mExportFilename);
- }
-
- if (mTask == Id.task.import_keys) {
- data = Apg.importKeyRings(this, mKeyType, new InputData(importInputStream, size),
- this);
- } else {
- Vector keyRingIds = new Vector();
- if (mSelectedItem == -1) {
- keyRingIds = Apg
- .getKeyRingIds(mKeyType == Id.type.public_key ? Id.database.type_public
- : Id.database.type_secret);
- } else {
- int keyRingId = mListAdapter.getKeyRingId(mSelectedItem);
- keyRingIds.add(keyRingId);
- mSelectedItem = -1;
- }
- data = Apg.exportKeyRings(this, keyRingIds, exportOutputStream, this);
- }
- } catch (FileNotFoundException e) {
- error = getString(R.string.error_fileNotFound);
- } catch (IOException e) {
- error = "" + e;
- } catch (PGPException e) {
- error = "" + e;
- } catch (Apg.GeneralException e) {
- error = "" + e;
- }
-
- mImportData = null;
-
- if (mTask == Id.task.import_keys) {
- data.putInt(Constants.extras.STATUS, Id.message.import_done);
- } else {
- data.putInt(Constants.extras.STATUS, Id.message.export_done);
- }
-
- if (error != null) {
- data.putString(Apg.EXTRA_ERROR, error);
- }
-
- msg.setData(data);
- sendMessage(msg);
- }
-
- protected void deleteKey(int keyRingId) {
- Apg.deleteKey(keyRingId);
- refreshList();
- }
-
- protected void refreshList() {
- mListAdapter.rebuild(true);
- mListAdapter.notifyDataSetChanged();
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- Bundle data = msg.getData();
- if (data != null) {
- int type = data.getInt(Constants.extras.STATUS);
- switch (type) {
- case Id.message.import_done: {
- removeDialog(Id.dialog.importing);
-
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(KeyListActivity.this, getString(R.string.errorMessage, error),
- Toast.LENGTH_SHORT).show();
- } else {
- int added = data.getInt("added");
- int updated = data.getInt("updated");
- int bad = data.getInt("bad");
- String message;
- if (added > 0 && updated > 0) {
- message = getString(R.string.keysAddedAndUpdated, added, updated);
- } else if (added > 0) {
- message = getString(R.string.keysAdded, added);
- } else if (updated > 0) {
- message = getString(R.string.keysUpdated, updated);
- } else {
- message = getString(R.string.noKeysAddedOrUpdated);
- }
- Toast.makeText(KeyListActivity.this, message, Toast.LENGTH_SHORT).show();
- if (bad > 0) {
- 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.badKeysEncountered, bad));
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- }
- });
- alert.setCancelable(true);
- alert.create().show();
- } else if (mDeleteAfterImport) {
- // everything went well, so now delete, if that was turned on
- setDeleteFile(mImportFilename);
- showDialog(Id.dialog.delete_file);
- }
- }
- refreshList();
- break;
- }
-
- case Id.message.export_done: {
- removeDialog(Id.dialog.exporting);
-
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(KeyListActivity.this, getString(R.string.errorMessage, error),
- Toast.LENGTH_SHORT).show();
- } else {
- int exported = data.getInt("exported");
- String message;
- if (exported == 1) {
- message = getString(R.string.keyExported);
- } else if (exported > 0) {
- message = getString(R.string.keysExported, exported);
- } else {
- message = getString(R.string.noKeysExported);
- }
- Toast.makeText(KeyListActivity.this, message, Toast.LENGTH_SHORT).show();
- }
- break;
- }
-
- default: {
- break;
- }
- }
- }
- }
-
- protected class KeyListAdapter extends BaseExpandableListAdapter {
- private LayoutInflater mInflater;
- private Vector> mChildren;
- private SQLiteDatabase mDatabase;
- private Cursor mCursor;
- private String mSearchString;
-
- private class KeyChild {
- public static final int KEY = 0;
- public static final int USER_ID = 1;
- public static final int FINGER_PRINT = 2;
-
- public int type;
- public String userId;
- public long keyId;
- public boolean isMasterKey;
- public int algorithm;
- public int keySize;
- public boolean canSign;
- public boolean canEncrypt;
- public String fingerPrint;
-
- public KeyChild(long keyId, boolean isMasterKey, int algorithm, int keySize,
- boolean canSign, boolean canEncrypt) {
- this.type = KEY;
- this.keyId = keyId;
- this.isMasterKey = isMasterKey;
- this.algorithm = algorithm;
- this.keySize = keySize;
- this.canSign = canSign;
- this.canEncrypt = canEncrypt;
- }
-
- public KeyChild(String userId) {
- type = USER_ID;
- this.userId = userId;
- }
-
- public KeyChild(String fingerPrint, boolean isFingerPrint) {
- type = FINGER_PRINT;
- this.fingerPrint = fingerPrint;
- }
- }
-
- public KeyListAdapter(Context context, String searchString) {
- mSearchString = searchString;
-
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mDatabase = Apg.getDatabase().db();
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " + "("
- + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " + Keys.TABLE_NAME + "."
- + Keys.KEY_RING_ID + " AND " + Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY
- + " = '1'" + ") " + " INNER JOIN " + UserIds.TABLE_NAME + " ON " + "("
- + Keys.TABLE_NAME + "." + Keys._ID + " = " + UserIds.TABLE_NAME + "."
- + UserIds.KEY_ID + " AND " + UserIds.TABLE_NAME + "." + UserIds.RANK
- + " = '0')");
-
- if (searchString != null && searchString.trim().length() > 0) {
- String[] chunks = searchString.trim().split(" +");
- qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " + UserIds.TABLE_NAME
- + " AS tmp WHERE " + "tmp." + UserIds.KEY_ID + " = " + Keys.TABLE_NAME
- + "." + Keys._ID);
- for (int i = 0; i < chunks.length; ++i) {
- qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
- qb.appendWhereEscapeString("%" + chunks[i] + "%");
- }
- qb.appendWhere(")");
- }
-
- mCursor = qb.query(mDatabase, new String[] { KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
- UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
- }, KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?", new String[] { ""
- + (mKeyType == Id.type.public_key ? Id.database.type_public
- : Id.database.type_secret) }, null, null, UserIds.TABLE_NAME + "."
- + UserIds.USER_ID + " ASC");
-
- // content provider way for reference, might have to go back to it sometime:
- /*
- * Uri contentUri = null; if (mKeyType == Id.type.secret_key) { contentUri =
- * Apg.CONTENT_URI_SECRET_KEY_RINGS; } else { contentUri =
- * Apg.CONTENT_URI_PUBLIC_KEY_RINGS; } mCursor = getContentResolver().query( contentUri,
- * new String[] { DataProvider._ID, // 0 DataProvider.MASTER_KEY_ID, // 1
- * DataProvider.USER_ID, // 2 }, null, null, null);
- */
-
- startManagingCursor(mCursor);
- rebuild(false);
- }
-
- public void cleanup() {
- if (mCursor != null) {
- stopManagingCursor(mCursor);
- mCursor.close();
- }
- }
-
- public void rebuild(boolean requery) {
- if (requery) {
- mCursor.requery();
- }
- mChildren = new Vector>();
- for (int i = 0; i < mCursor.getCount(); ++i) {
- mChildren.add(null);
- }
- }
-
- protected Vector getChildrenOfGroup(int groupPosition) {
- Vector children = mChildren.get(groupPosition);
- if (children != null) {
- return children;
- }
-
- mCursor.moveToPosition(groupPosition);
- children = new Vector();
- Cursor c = mDatabase.query(Keys.TABLE_NAME, new String[] { Keys._ID, // 0
- Keys.KEY_ID, // 1
- Keys.IS_MASTER_KEY, // 2
- Keys.ALGORITHM, // 3
- Keys.KEY_SIZE, // 4
- Keys.CAN_SIGN, // 5
- Keys.CAN_ENCRYPT, // 6
- }, Keys.KEY_RING_ID + " = ?", new String[] { mCursor.getString(0) }, null, null,
- Keys.RANK + " ASC");
-
- int masterKeyId = -1;
- long fingerPrintId = -1;
- for (int i = 0; i < c.getCount(); ++i) {
- c.moveToPosition(i);
- children.add(new KeyChild(c.getLong(1), c.getInt(2) == 1, c.getInt(3), c.getInt(4),
- c.getInt(5) == 1, c.getInt(6) == 1));
- if (i == 0) {
- masterKeyId = c.getInt(0);
- fingerPrintId = c.getLong(1);
- }
- }
- c.close();
-
- if (masterKeyId != -1) {
- children.insertElementAt(new KeyChild(Apg.getFingerPrint(fingerPrintId), true), 0);
- c = mDatabase.query(UserIds.TABLE_NAME, new String[] { UserIds.USER_ID, // 0
- }, UserIds.KEY_ID + " = ? AND " + UserIds.RANK + " > 0", new String[] { ""
- + masterKeyId }, null, null, UserIds.RANK + " ASC");
-
- for (int i = 0; i < c.getCount(); ++i) {
- c.moveToPosition(i);
- children.add(new KeyChild(c.getString(0)));
- }
- c.close();
- }
-
- mChildren.set(groupPosition, children);
- return children;
- }
-
- public boolean hasStableIds() {
- return true;
- }
-
- public boolean isChildSelectable(int groupPosition, int childPosition) {
- return true;
- }
-
- public int getGroupCount() {
- return mCursor.getCount();
- }
-
- public Object getChild(int groupPosition, int childPosition) {
- return null;
- }
-
- public long getChildId(int groupPosition, int childPosition) {
- return childPosition;
- }
-
- public int getChildrenCount(int groupPosition) {
- return getChildrenOfGroup(groupPosition).size();
- }
-
- public Object getGroup(int position) {
- return position;
- }
-
- public long getGroupId(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getLong(1); // MASTER_KEY_ID
- }
-
- public int getKeyRingId(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getInt(0); // _ID
- }
-
- public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
- ViewGroup parent) {
- mCursor.moveToPosition(groupPosition);
-
- View view = mInflater.inflate(R.layout.key_list_group_item, null);
- view.setBackgroundResource(android.R.drawable.list_selector_background);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText("");
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
-
- String userId = mCursor.getString(2); // USER_ID
- if (userId != null) {
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mainUserIdRest.setText("<" + chunks[1]);
- }
- mainUserId.setText(userId);
- }
-
- if (mainUserId.getText().length() == 0) {
- mainUserId.setText(R.string.unknownUserId);
- }
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- }
- return view;
- }
-
- public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
- View convertView, ViewGroup parent) {
- mCursor.moveToPosition(groupPosition);
-
- Vector children = getChildrenOfGroup(groupPosition);
-
- KeyChild child = children.get(childPosition);
- View view = null;
- switch (child.type) {
- case KeyChild.KEY: {
- if (child.isMasterKey) {
- view = mInflater.inflate(R.layout.key_list_child_item_master_key, null);
- } else {
- view = mInflater.inflate(R.layout.key_list_child_item_sub_key, null);
- }
-
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- String keyIdStr = Apg.getSmallFingerPrint(child.keyId);
- keyId.setText(keyIdStr);
- TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
- String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
- keyDetails.setText("(" + algorithmStr + ")");
-
- ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
- if (!child.canEncrypt) {
- encryptIcon.setVisibility(View.GONE);
- }
-
- ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
- if (!child.canSign) {
- signIcon.setVisibility(View.GONE);
- }
- break;
- }
-
- case KeyChild.USER_ID: {
- view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
- TextView userId = (TextView) view.findViewById(R.id.userId);
- userId.setText(child.userId);
- break;
- }
-
- case KeyChild.FINGER_PRINT: {
- view = mInflater.inflate(R.layout.key_list_child_item_user_id, null);
- TextView userId = (TextView) view.findViewById(R.id.userId);
- userId.setText(getString(R.string.fingerprint) + ":\n"
- + child.fingerPrint.replace(" ", "\n"));
- break;
- }
- }
- return view;
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.filename: {
- if (resultCode == RESULT_OK && data != null) {
- String filename = data.getDataString();
- if (filename != null) {
- // Get rid of URI prefix:
- if (filename.startsWith("file://")) {
- filename = filename.substring(7);
- }
- // replace %20 and so on
- filename = Uri.decode(filename);
-
- FileDialog.setFilename(filename);
- }
- }
- return;
- }
-
- default: {
- break;
- }
- }
- super.onActivityResult(requestCode, resultCode, data);
- }
-}
diff --git a/src/org/apg/ui/KeyServerPreferenceActivity.java b/src/org/apg/ui/KeyServerPreferenceActivity.java
deleted file mode 100644
index 85d31779a..000000000
--- a/src/org/apg/ui/KeyServerPreferenceActivity.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import java.util.Vector;
-
-import org.apg.Apg;
-import org.apg.ui.widget.Editor;
-import org.apg.ui.widget.KeyServerEditor;
-import org.apg.ui.widget.Editor.EditorListener;
-import org.apg.R;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.TextView;
-
-public class KeyServerPreferenceActivity extends BaseActivity
- implements OnClickListener, EditorListener {
- private LayoutInflater mInflater;
- private ViewGroup mEditors;
- private View mAdd;
- private TextView mTitle;
- private TextView mSummary;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.key_server_preference);
-
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- mTitle = (TextView) findViewById(R.id.title);
- mSummary = (TextView) findViewById(R.id.summary);
-
- mTitle.setText(R.string.label_keyServers);
-
- mEditors = (ViewGroup) findViewById(R.id.editors);
- mAdd = findViewById(R.id.add);
- mAdd.setOnClickListener(this);
-
- Intent intent = getIntent();
- String servers[] = intent.getStringArrayExtra(Apg.EXTRA_KEY_SERVERS);
- if (servers != null) {
- for (int i = 0; i < servers.length; ++i) {
- KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, mEditors, false);
- view.setEditorListener(this);
- view.setValue(servers[i]);
- mEditors.addView(view);
- }
- }
-
- Button okButton = (Button) findViewById(R.id.btn_ok);
- okButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- okClicked();
- }
- });
-
- Button cancelButton = (Button) findViewById(R.id.btn_cancel);
- cancelButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- cancelClicked();
- }
- });
- }
-
- public void onDeleted(Editor editor) {
- // nothing to do
- }
-
- public void onClick(View v) {
- KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, mEditors, false);
- view.setEditorListener(this);
- mEditors.addView(view);
- }
-
- private void cancelClicked() {
- setResult(RESULT_CANCELED, null);
- finish();
- }
-
- private void okClicked() {
- Intent data = new Intent();
- Vector servers = new Vector();
- for (int i = 0; i < mEditors.getChildCount(); ++i) {
- KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
- String tmp = editor.getValue();
- if (tmp.length() > 0) {
- servers.add(tmp);
- }
- }
- String[] dummy = new String[0];
- data.putExtra(Apg.EXTRA_KEY_SERVERS, servers.toArray(dummy));
- setResult(RESULT_OK, data);
- finish();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // override this, so no option menu is added (as would be in BaseActivity), since
- // we're still in preferences
- return true;
- }
-}
diff --git a/src/org/apg/ui/KeyServerQueryActivity.java b/src/org/apg/ui/KeyServerQueryActivity.java
deleted file mode 100644
index 606acb575..000000000
--- a/src/org/apg/ui/KeyServerQueryActivity.java
+++ /dev/null
@@ -1,297 +0,0 @@
-package org.apg.ui;
-
-import java.util.List;
-import java.util.Vector;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.HkpKeyServer;
-import org.apg.Id;
-import org.apg.KeyServer.InsufficientQuery;
-import org.apg.KeyServer.KeyInfo;
-import org.apg.KeyServer.QueryException;
-import org.apg.KeyServer.TooManyResponses;
-import org.apg.R;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.LinearLayout.LayoutParams;
-import android.widget.ListView;
-import android.widget.Spinner;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class KeyServerQueryActivity extends BaseActivity {
- private ListView mList;
- private EditText mQuery;
- private Button mSearch;
- private KeyInfoListAdapter mAdapter;
- private Spinner mKeyServer;
-
- private int mQueryType;
- private String mQueryString;
- private long mQueryId;
- private volatile List mSearchResult;
- private volatile String mKeyData;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.key_server_query_layout);
-
- mQuery = (EditText) findViewById(R.id.query);
- mSearch = (Button) findViewById(R.id.btn_search);
- mList = (ListView) findViewById(R.id.list);
- mAdapter = new KeyInfoListAdapter(this);
- mList.setAdapter(mAdapter);
-
- mKeyServer = (Spinner) findViewById(R.id.keyServer);
- ArrayAdapter adapter =
- new ArrayAdapter(this,
- android.R.layout.simple_spinner_item,
- mPreferences.getKeyServers());
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mKeyServer.setAdapter(adapter);
- if (adapter.getCount() > 0) {
- mKeyServer.setSelection(0);
- } else {
- mSearch.setEnabled(false);
- }
-
- mList.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView> adapter, View view, int position, long keyId) {
- get(keyId);
- }
- });
-
- mSearch.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- String query = mQuery.getText().toString();
- search(query);
- }
- });
-
- Intent intent = getIntent();
- if (Apg.Intent.LOOK_UP_KEY_ID.equals(intent.getAction()) ||
- Apg.Intent.LOOK_UP_KEY_ID_AND_RETURN.equals(intent.getAction())) {
- long keyId = intent.getLongExtra(Apg.EXTRA_KEY_ID, 0);
- if (keyId != 0) {
- String query = "0x" + Apg.keyToHex(keyId);
- mQuery.setText(query);
- search(query);
- }
- }
- }
-
- private void search(String query) {
- showDialog(Id.dialog.querying);
- mQueryType = Id.keyserver.search;
- mQueryString = query;
- mAdapter.setKeys(new Vector());
- startThread();
- }
-
- private void get(long keyId) {
- showDialog(Id.dialog.querying);
- mQueryType = Id.keyserver.get;
- mQueryId = keyId;
- startThread();
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- ProgressDialog progress = (ProgressDialog) super.onCreateDialog(id);
- progress.setMessage(this.getString(R.string.progress_queryingServer,
- (String)mKeyServer.getSelectedItem()));
- return progress;
- }
-
- @Override
- public void run() {
- String error = null;
- Bundle data = new Bundle();
- Message msg = new Message();
-
- try {
- HkpKeyServer server = new HkpKeyServer((String)mKeyServer.getSelectedItem());
- if (mQueryType == Id.keyserver.search) {
- mSearchResult = server.search(mQueryString);
- } else if (mQueryType == Id.keyserver.get) {
- mKeyData = server.get(mQueryId);
- }
- } catch (QueryException e) {
- error = "" + e;
- } catch (InsufficientQuery e) {
- error = "Insufficient query.";
- } catch (TooManyResponses e) {
- error = "Too many responses.";
- }
-
- data.putInt(Constants.extras.STATUS, Id.message.done);
-
- if (error != null) {
- data.putString(Apg.EXTRA_ERROR, error);
- }
-
- msg.setData(data);
- sendMessage(msg);
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- removeDialog(Id.dialog.querying);
-
- Bundle data = msg.getData();
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
- return;
- }
-
- if (mQueryType == Id.keyserver.search) {
- if (mSearchResult != null) {
- Toast.makeText(this, getString(R.string.keysFound, mSearchResult.size()), Toast.LENGTH_SHORT).show();
- mAdapter.setKeys(mSearchResult);
- }
- } else if (mQueryType == Id.keyserver.get) {
- Intent orgIntent = getIntent();
- if (Apg.Intent.LOOK_UP_KEY_ID_AND_RETURN.equals(orgIntent.getAction())) {
- if (mKeyData != null) {
- Intent intent = new Intent();
- intent.putExtra(Apg.EXTRA_TEXT, mKeyData);
- setResult(RESULT_OK, intent);
- } else {
- setResult(RESULT_CANCELED);
- }
- finish();
- } else {
- if (mKeyData != null) {
- Intent intent = new Intent(this, PublicKeyListActivity.class);
- intent.setAction(Apg.Intent.IMPORT);
- intent.putExtra(Apg.EXTRA_TEXT, mKeyData);
- startActivity(intent);
- }
- }
- }
- }
-
- public class KeyInfoListAdapter extends BaseAdapter {
- protected LayoutInflater mInflater;
- protected Activity mActivity;
- protected List mKeys;
-
- public KeyInfoListAdapter(Activity activity) {
- mActivity = activity;
- mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mKeys = new Vector();
- }
-
- public void setKeys(List keys) {
- mKeys = keys;
- notifyDataSetChanged();
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- public int getCount() {
- return mKeys.size();
- }
-
- public Object getItem(int position) {
- return mKeys.get(position);
- }
-
- public long getItemId(int position) {
- return mKeys.get(position).keyId;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- KeyInfo keyInfo = mKeys.get(position);
-
- View view = mInflater.inflate(R.layout.key_server_query_result_item, null);
-
- 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 algorithm = (TextView) view.findViewById(R.id.algorithm);
- algorithm.setText("");
- TextView status = (TextView) view.findViewById(R.id.status);
- status.setText("");
-
- String userId = keyInfo.userIds.get(0);
- if (userId != null) {
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mainUserIdRest.setText("<" + chunks[1]);
- }
- mainUserId.setText(userId);
- }
-
- keyId.setText(Apg.getSmallFingerPrint(keyInfo.keyId));
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- }
-
- algorithm.setText("" + keyInfo.size + "/" + keyInfo.algorithm);
-
- if (keyInfo.revoked != null) {
- status.setText("revoked");
- } else {
- status.setVisibility(View.GONE);
- }
-
- LinearLayout ll = (LinearLayout) view.findViewById(R.id.list);
- if (keyInfo.userIds.size() == 1) {
- ll.setVisibility(View.GONE);
- } else {
- boolean first = true;
- boolean second = true;
- for (String uid : keyInfo.userIds) {
- if (first) {
- first = false;
- continue;
- }
- if (!second) {
- View sep = new View(mActivity);
- sep.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 1));
- sep.setBackgroundResource(android.R.drawable.divider_horizontal_dark);
- ll.addView(sep);
- }
- TextView uidView = (TextView) mInflater.inflate(R.layout.key_server_query_result_user_id, null);
- uidView.setText(uid);
- ll.addView(uidView);
- second = false;
- }
- }
-
- return view;
- }
- }
-}
diff --git a/src/org/apg/ui/MailListActivity.java b/src/org/apg/ui/MailListActivity.java
deleted file mode 100644
index ad1d08068..000000000
--- a/src/org/apg/ui/MailListActivity.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import java.util.Vector;
-import java.util.regex.Matcher;
-
-import org.apg.Apg;
-import org.apg.Preferences;
-import org.apg.R;
-
-import android.app.ListActivity;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.Html;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-public class MailListActivity extends ListActivity {
- LayoutInflater mInflater = null;
-
- public static final String EXTRA_ACCOUNT = "account";
-
- private static class Conversation {
- public long id;
- public String subject;
- public Vector messages;
-
- public Conversation(long id, String subject) {
- this.id = id;
- this.subject = subject;
- }
- }
-
- 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, boolean signedOnly) {
- this.parent = parent;
- this.id = id;
- this.subject = subject;
- this.fromAddress = fromAddress;
- this.replyTo = replyTo;
- this.data = data;
- if (this.replyTo == null || this.replyTo.equals("")) {
- this.replyTo = this.fromAddress;
- }
- this.signedOnly = signedOnly;
- }
- }
-
- private Vector mConversations;
- private Vector mMessages;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Preferences prefs = Preferences.getPreferences(this);
- BaseActivity.setLanguage(this, prefs.getLanguage());
-
- super.onCreate(savedInstanceState);
-
- mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- mConversations = new Vector();
- mMessages = new Vector();
-
- String account = getIntent().getExtras().getString(EXTRA_ACCOUNT);
- // TODO: what if account is null?
- Uri uri = Uri.parse("content://gmail-ls/conversations/" + account);
- Cursor cursor =
- managedQuery(uri, new String[] { "conversation_id", "subject" }, null, null, null);
- for (int i = 0; i < cursor.getCount(); ++i) {
- cursor.moveToPosition(i);
-
- int idIndex = cursor.getColumnIndex("conversation_id");
- int subjectIndex = cursor.getColumnIndex("subject");
- long conversationId = cursor.getLong(idIndex);
- Conversation conversation =
- new Conversation(conversationId, cursor.getString(subjectIndex));
- Uri messageUri = Uri.withAppendedPath(uri, "" + conversationId + "/messages");
- Cursor messageCursor =
- managedQuery(messageUri, new String[] {
- "messageId",
- "subject",
- "fromAddress",
- "replyToAddresses",
- "body" }, null, null, null);
- Vector messages = new Vector();
- for (int j = 0; j < messageCursor.getCount(); ++j) {
- messageCursor.moveToPosition(j);
- idIndex = messageCursor.getColumnIndex("messageId");
- subjectIndex = messageCursor.getColumnIndex("subject");
- int fromAddressIndex = messageCursor.getColumnIndex("fromAddress");
- int replyToIndex = messageCursor.getColumnIndex("replyToAddresses");
- 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 {
- 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, signedOnly);
-
- messages.add(message);
- mMessages.add(message);
- }
- conversation.messages = messages;
- mConversations.add(conversation);
- }
-
- setListAdapter(new MailboxAdapter());
- getListView().setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView> arg0, View v, int position, long id) {
- Intent intent = new Intent(MailListActivity.this, DecryptActivity.class);
- intent.setAction(Apg.Intent.DECRYPT);
- Message message = (Message) ((MailboxAdapter) getListAdapter()).getItem(position);
- intent.putExtra(Apg.EXTRA_TEXT, message.data);
- intent.putExtra(Apg.EXTRA_SUBJECT, message.subject);
- intent.putExtra(Apg.EXTRA_REPLY_TO, message.replyTo);
- startActivity(intent);
- }
- });
- }
-
- private class MailboxAdapter extends BaseAdapter implements ListAdapter {
-
- @Override
- public boolean isEnabled(int position) {
- Message message = (Message) getItem(position);
- return message.data != null;
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- public int getCount() {
- return mMessages.size();
- }
-
- public Object getItem(int position) {
- return mMessages.get(position);
- }
-
- public long getItemId(int position) {
- return mMessages.get(position).id;
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- 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.emailAddress);
- ImageView status = (ImageView) view.findViewById(R.id.ic_status);
-
- subject.setText(message.subject);
- email.setText(message.fromAddress);
- if (message.data != null) {
- if (message.signedOnly) {
- status.setImageResource(R.drawable.signed);
- } else {
- status.setImageResource(R.drawable.encrypted);
- }
- status.setVisibility(View.VISIBLE);
- } else {
- status.setVisibility(View.INVISIBLE);
- }
-
- return view;
- }
- }
-}
diff --git a/src/org/apg/ui/MainActivity.java b/src/org/apg/ui/MainActivity.java
deleted file mode 100644
index 8c985c2ac..000000000
--- a/src/org/apg/ui/MainActivity.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import java.security.Security;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.Id.dialog;
-import org.apg.Id.menu;
-import org.apg.Id.menu.option;
-import org.apg.provider.Accounts;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-import org.apg.R;
-
-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.database.Cursor;
-import android.database.SQLException;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.util.Linkify;
-import android.text.util.Linkify.TransformFilter;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-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;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Button;
-import android.widget.CursorAdapter;
-import android.widget.EditText;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class MainActivity extends BaseActivity {
- static {
- Security.addProvider(new BouncyCastleProvider());
- }
-
- private ListView mAccounts = null;
- private AccountListAdapter mListAdapter = null;
- private Cursor mAccountCursor;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- Button encryptMessageButton = (Button) findViewById(R.id.btn_encryptMessage);
- Button decryptMessageButton = (Button) findViewById(R.id.btn_decryptMessage);
- 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() {
- public void onClick(View v) {
- Intent intent = new Intent(MainActivity.this, EncryptActivity.class);
- intent.setAction(Apg.Intent.ENCRYPT);
- startActivity(intent);
- }
- });
-
- decryptMessageButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- Intent intent = new Intent(MainActivity.this, DecryptActivity.class);
- intent.setAction(Apg.Intent.DECRYPT);
- startActivity(intent);
- }
- });
-
- encryptFileButton.setOnClickListener(new OnClickListener() {
- 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() {
- public void onClick(View v) {
- Intent intent = new Intent(MainActivity.this, DecryptActivity.class);
- intent.setAction(Apg.Intent.DECRYPT_FILE);
- startActivity(intent);
- }
- });
-
- mAccountCursor =
- Apg.getDatabase().db().query(Accounts.TABLE_NAME,
- new String[] {
- Accounts._ID,
- Accounts.NAME,
- }, null, null, null, null, Accounts.NAME + " ASC");
- startManagingCursor(mAccountCursor);
-
- mListAdapter = new AccountListAdapter(this, mAccountCursor);
- mAccounts.setAdapter(mListAdapter);
- mAccounts.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView> arg0, View view, int index, long id) {
- String accountName = (String) mAccounts.getItemAtPosition(index);
- startActivity(new Intent(MainActivity.this, MailListActivity.class)
- .putExtra(MailListActivity.EXTRA_ACCOUNT, accountName));
- }
- });
- registerForContextMenu(mAccounts);
-
- if (!mPreferences.hasSeenHelp()) {
- showDialog(Id.dialog.help);
- }
-
- if (Apg.isReleaseVersion(this) && !mPreferences.hasSeenChangeLog(Apg.getVersion(this))) {
- showDialog(Id.dialog.change_log);
- }
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case Id.dialog.new_account: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setTitle(R.string.title_addAccount);
- alert.setMessage(R.string.specifyGoogleMailAccount);
-
- LayoutInflater inflater =
- (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View view = inflater.inflate(R.layout.add_account_dialog, null);
-
- final EditText input = (EditText) view.findViewById(R.id.input);
- alert.setView(view);
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- MainActivity.this.removeDialog(Id.dialog.new_account);
- String accountName = "" + input.getText();
-
- try {
- 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;
- }
- } catch (SecurityException e) {
- Toast.makeText(MainActivity.this,
- getString(R.string.errorMessage,
- getString(R.string.error_accountReadingNotAllowed)),
- Toast.LENGTH_SHORT).show();
- return;
- }
-
- ContentValues values = new ContentValues();
- values.put(Accounts.NAME, accountName);
- try {
- Apg.getDatabase().db().insert(Accounts.TABLE_NAME,
- Accounts.NAME, values);
- mAccountCursor.requery();
- mListAdapter.notifyDataSetChanged();
- } catch (SQLException e) {
- Toast.makeText(MainActivity.this,
- getString(R.string.errorMessage,
- 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(Id.dialog.new_account);
- }
- });
-
- return alert.create();
- }
-
- case Id.dialog.change_log: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setTitle("Changes " + Apg.getFullVersion(this));
- 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("Changes:\n" +
- "* \n" +
- "\n" +
- "WARNING: be careful editing your existing keys, as they " +
- "WILL be stripped of certificates right now.\n" +
- "\n" +
- "Also: 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(Id.dialog.change_log);
- mPreferences.setHasSeenChangeLog(
- Apg.getVersion(MainActivity.this), true);
- }
- });
-
- return alert.create();
- }
-
- case Id.dialog.help: {
- AlertDialog.Builder alert = new AlertDialog.Builder(this);
-
- alert.setTitle(R.string.title_help);
-
- 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(R.string.text_help);
-
- TransformFilter packageNames = new TransformFilter() {
- public final String transformUrl(final Matcher match, String url) {
- String name = match.group(1).toLowerCase();
- if (name.equals("astro")) {
- return "com.metago.astro";
- } else if (name.equals("k-9 mail")) {
- return "com.fsck.k9";
- } else {
- return "org.openintents.filemanager";
- }
- }
- };
-
- Pattern pattern = Pattern.compile("(OI File Manager|ASTRO|K-9 Mail)");
- String scheme = "market://search?q=pname:";
- message.setAutoLinkMask(0);
- Linkify.addLinks(message, pattern, scheme, null, packageNames);
-
- alert.setView(layout);
-
- alert.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- MainActivity.this.removeDialog(Id.dialog.help);
- mPreferences.setHasSeenHelp(true);
- }
- });
-
- return alert.create();
- }
-
- default: {
- return super.onCreateDialog(id);
- }
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.manage_public_keys, 0, R.string.menu_managePublicKeys)
- .setIcon(android.R.drawable.ic_menu_manage);
- menu.add(0, Id.menu.option.manage_secret_keys, 1, R.string.menu_manageSecretKeys)
- .setIcon(android.R.drawable.ic_menu_manage);
- menu.add(1, Id.menu.option.create, 2, R.string.menu_addAccount)
- .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.key_server, 4, R.string.menu_keyServer)
- .setIcon(android.R.drawable.ic_menu_search);
- menu.add(3, Id.menu.option.about, 5, R.string.menu_about)
- .setIcon(android.R.drawable.ic_menu_info_details);
- menu.add(3, Id.menu.option.help, 6, R.string.menu_help)
- .setIcon(android.R.drawable.ic_menu_help);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case Id.menu.option.create: {
- showDialog(Id.dialog.new_account);
- return true;
- }
-
- case Id.menu.option.manage_public_keys: {
- startActivity(new Intent(this, PublicKeyListActivity.class));
- return true;
- }
-
- case Id.menu.option.manage_secret_keys: {
- startActivity(new Intent(this, SecretKeyListActivity.class));
- return true;
- }
-
- case Id.menu.option.help: {
- showDialog(Id.dialog.help);
- return true;
- }
-
- case Id.menu.option.key_server: {
- startActivity(new Intent(this, KeyServerQueryActivity.class));
- return true;
- }
-
- default: {
- return super.onOptionsItemSelected(item);
- }
- }
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
-
- TextView nameTextView = (TextView) v.findViewById(R.id.accountName);
- if (nameTextView != null) {
- menu.setHeaderTitle(nameTextView.getText());
- menu.add(0, Id.menu.delete, 0, R.string.menu_deleteAccount);
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- AdapterView.AdapterContextMenuInfo info =
- (AdapterView.AdapterContextMenuInfo) menuItem.getMenuInfo();
-
- switch (menuItem.getItemId()) {
- case Id.menu.delete: {
- Apg.getDatabase().db().delete(Accounts.TABLE_NAME,
- Accounts._ID + " = ?",
- new String[] { "" + info.id });
- mAccountCursor.requery();
- mListAdapter.notifyDataSetChanged();
- return true;
- }
-
- default: {
- return super.onContextItemSelected(menuItem);
- }
- }
- }
-
-
- private static class AccountListAdapter extends CursorAdapter {
- private LayoutInflater mInflater;
-
- public AccountListAdapter(Context context, Cursor cursor) {
- super(context, cursor);
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- }
-
- @Override
- public Object getItem(int position) {
- Cursor c = getCursor();
- c.moveToPosition(position);
- return c.getString(c.getColumnIndex(Accounts.NAME));
- }
-
- @Override
- public int getCount() {
- return super.getCount();
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.account_item, null);
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- TextView nameTextView = (TextView) view.findViewById(R.id.accountName);
- int nameIndex = cursor.getColumnIndex(Accounts.NAME);
- final String account = cursor.getString(nameIndex);
- nameTextView.setText(account);
- }
-
- @Override
- public boolean isEnabled(int position) {
- return true;
- }
- }
-}
\ No newline at end of file
diff --git a/src/org/apg/ui/PreferencesActivity.java b/src/org/apg/ui/PreferencesActivity.java
deleted file mode 100644
index 421c9cc39..000000000
--- a/src/org/apg/ui/PreferencesActivity.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.Id;
-import org.apg.Preferences;
-import org.apg.Constants.pref;
-import org.apg.Id.choice;
-import org.apg.Id.request;
-import org.apg.Id.choice.compression;
-import org.apg.ui.widget.IntegerListPreference;
-import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.openpgp.PGPEncryptedData;
-import org.apg.R;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceActivity;
-import android.preference.PreferenceScreen;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Vector;
-
-public class PreferencesActivity extends PreferenceActivity {
- private ListPreference mLanguage = null;
- private IntegerListPreference mPassPhraseCacheTtl = null;
- private IntegerListPreference mEncryptionAlgorithm = null;
- private IntegerListPreference mHashAlgorithm = null;
- private IntegerListPreference mMessageCompression = null;
- private IntegerListPreference mFileCompression = null;
- private CheckBoxPreference mAsciiArmour = null;
- private CheckBoxPreference mForceV3Signatures = null;
- private PreferenceScreen mKeyServerPreference = null;
- private Preferences mPreferences;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- mPreferences = Preferences.getPreferences(this);
- BaseActivity.setLanguage(this, mPreferences.getLanguage());
- super.onCreate(savedInstanceState);
-
- addPreferencesFromResource(R.xml.apg_preferences);
-
- mLanguage = (ListPreference) findPreference(Constants.pref.LANGUAGE);
- Vector entryVector = new Vector(Arrays.asList(mLanguage.getEntries()));
- Vector entryValueVector = new Vector(Arrays.asList(mLanguage.getEntryValues()));
- String supportedLanguages[] = getResources().getStringArray(R.array.supported_languages);
- HashSet supportedLanguageSet = new HashSet(Arrays.asList(supportedLanguages));
- for (int i = entryVector.size() - 1; i > -1; --i)
- {
- if (!supportedLanguageSet.contains(entryValueVector.get(i)))
- {
- entryVector.remove(i);
- entryValueVector.remove(i);
- }
- }
- CharSequence dummy[] = new CharSequence[0];
- mLanguage.setEntries(entryVector.toArray(dummy));
- mLanguage.setEntryValues(entryValueVector.toArray(dummy));
- mLanguage.setValue(mPreferences.getLanguage());
- mLanguage.setSummary(mLanguage.getEntry());
- mLanguage.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mLanguage.setValue(newValue.toString());
- mLanguage.setSummary(mLanguage.getEntry());
- mPreferences.setLanguage(newValue.toString());
- return false;
- }
- });
-
- mPassPhraseCacheTtl = (IntegerListPreference) findPreference(Constants.pref.PASS_PHRASE_CACHE_TTL);
- mPassPhraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
- mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
- mPassPhraseCacheTtl.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mPassPhraseCacheTtl.setValue(newValue.toString());
- mPassPhraseCacheTtl.setSummary(mPassPhraseCacheTtl.getEntry());
- mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
- BaseActivity.startCacheService(PreferencesActivity.this, mPreferences);
- return false;
- }
- });
-
- mEncryptionAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_ENCRYPTION_ALGORITHM);
- int valueIds[] = {
- PGPEncryptedData.AES_128, PGPEncryptedData.AES_192, PGPEncryptedData.AES_256,
- PGPEncryptedData.BLOWFISH, PGPEncryptedData.TWOFISH, PGPEncryptedData.CAST5,
- PGPEncryptedData.DES, PGPEncryptedData.TRIPLE_DES, PGPEncryptedData.IDEA,
- };
- String entries[] = {
- "AES-128", "AES-192", "AES-256",
- "Blowfish", "Twofish", "CAST5",
- "DES", "Triple DES", "IDEA",
- };
- String values[] = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
- mEncryptionAlgorithm.setEntries(entries);
- mEncryptionAlgorithm.setEntryValues(values);
- mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm());
- mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
- mEncryptionAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mEncryptionAlgorithm.setValue(newValue.toString());
- mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
- mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mHashAlgorithm = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_HASH_ALGORITHM);
- valueIds = new int[] {
- HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160, HashAlgorithmTags.SHA1,
- HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256, HashAlgorithmTags.SHA384,
- HashAlgorithmTags.SHA512,
- };
- entries = new String[] {
- "MD5", "RIPEMD-160", "SHA-1",
- "SHA-224", "SHA-256", "SHA-384",
- "SHA-512",
- };
- values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
- mHashAlgorithm.setEntries(entries);
- mHashAlgorithm.setEntryValues(values);
- mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm());
- mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
- mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mHashAlgorithm.setValue(newValue.toString());
- mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
- mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mMessageCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_MESSAGE_COMPRESSION);
- valueIds = new int[] {
- Id.choice.compression.none,
- Id.choice.compression.zip,
- Id.choice.compression.zlib,
- Id.choice.compression.bzip2,
- };
- entries = new String[] {
- getString(R.string.choice_none) + " (" + getString(R.string.fast) + ")",
- "ZIP (" + getString(R.string.fast) + ")",
- "ZLIB (" + getString(R.string.fast) + ")",
- "BZIP2 (" + getString(R.string.very_slow) + ")",
- };
- values = new String[valueIds.length];
- for (int i = 0; i < values.length; ++i) {
- values[i] = "" + valueIds[i];
- }
- mMessageCompression.setEntries(entries);
- mMessageCompression.setEntryValues(values);
- mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
- mMessageCompression.setSummary(mMessageCompression.getEntry());
- mMessageCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mMessageCompression.setValue(newValue.toString());
- mMessageCompression.setSummary(mMessageCompression.getEntry());
- mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mFileCompression = (IntegerListPreference) findPreference(Constants.pref.DEFAULT_FILE_COMPRESSION);
- mFileCompression.setEntries(entries);
- mFileCompression.setEntryValues(values);
- mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
- mFileCompression.setSummary(mFileCompression.getEntry());
- mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mFileCompression.setValue(newValue.toString());
- mFileCompression.setSummary(mFileCompression.getEntry());
- mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
- return false;
- }
- });
-
- mAsciiArmour = (CheckBoxPreference) findPreference(Constants.pref.DEFAULT_ASCII_ARMOUR);
- mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
- mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mAsciiArmour.setChecked((Boolean)newValue);
- mPreferences.setDefaultAsciiArmour((Boolean)newValue);
- return false;
- }
- });
-
- mForceV3Signatures = (CheckBoxPreference) findPreference(Constants.pref.FORCE_V3_SIGNATURES);
- mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
- mForceV3Signatures.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener()
- {
- public boolean onPreferenceChange(Preference preference, Object newValue)
- {
- mForceV3Signatures.setChecked((Boolean)newValue);
- mPreferences.setForceV3Signatures((Boolean)newValue);
- return false;
- }
- });
-
- mKeyServerPreference = (PreferenceScreen) findPreference(Constants.pref.KEY_SERVERS);
- String servers[] = mPreferences.getKeyServers();
- mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, servers.length));
- mKeyServerPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- public boolean onPreferenceClick(Preference preference) {
- Intent intent = new Intent(PreferencesActivity.this,
- KeyServerPreferenceActivity.class);
- intent.putExtra(Apg.EXTRA_KEY_SERVERS, mPreferences.getKeyServers());
- startActivityForResult(intent, Id.request.key_server_preference);
- return false;
- }
- });
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.key_server_preference: {
- if (resultCode == RESULT_CANCELED || data == null) {
- return;
- }
- String servers[] = data.getStringArrayExtra(Apg.EXTRA_KEY_SERVERS);
- mPreferences.setKeyServers(servers);
- mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, servers.length));
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- break;
- }
- }
- }
-}
-
diff --git a/src/org/apg/ui/PublicKeyListActivity.java b/src/org/apg/ui/PublicKeyListActivity.java
deleted file mode 100644
index 81a79ce33..000000000
--- a/src/org/apg/ui/PublicKeyListActivity.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.Id;
-import org.apg.Constants.path;
-import org.apg.Id.menu;
-import org.apg.Id.request;
-import org.apg.Id.type;
-import org.apg.Id.menu.option;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.apg.R;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-
-public class PublicKeyListActivity extends KeyListActivity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- mExportFilename = Constants.path.APP_DIR + "/pubexport.asc";
- mKeyType = Id.type.public_key;
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys).setIcon(
- android.R.drawable.ic_menu_add);
- 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.search, 2, R.string.menu_search).setIcon(
- android.R.drawable.ic_menu_search);
- menu.add(1, Id.menu.option.preferences, 3, R.string.menu_preferences).setIcon(
- android.R.drawable.ic_menu_preferences);
- menu.add(1, Id.menu.option.about, 4, R.string.menu_about).setIcon(
- android.R.drawable.ic_menu_info_details);
- menu.add(1, Id.menu.option.scanQRCode, 5, R.string.menu_scanQRCode).setIcon(
- android.R.drawable.ic_menu_add);
- return true;
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
- int type = ExpandableListView.getPackedPositionType(info.packedPosition);
-
- if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- // TODO: user id? menu.setHeaderTitle("Key");
- menu.add(0, Id.menu.export, 0, R.string.menu_exportKey);
- menu.add(0, Id.menu.delete, 1, R.string.menu_deleteKey);
- menu.add(0, Id.menu.update, 1, R.string.menu_updateKey);
- menu.add(0, Id.menu.exportToServer, 1, R.string.menu_exportKeyToServer);
- menu.add(0, Id.menu.signKey, 1, R.string.menu_signKey);
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
- int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
-
- if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- return super.onContextItemSelected(menuItem);
- }
-
- switch (menuItem.getItemId()) {
- case Id.menu.update: {
- mSelectedItem = groupPosition;
- final int keyRingId = mListAdapter.getKeyRingId(groupPosition);
- long keyId = 0;
- Object keyRing = Apg.getKeyRing(keyRingId);
- if (keyRing != null && keyRing instanceof PGPPublicKeyRing) {
- keyId = Apg.getMasterKey((PGPPublicKeyRing) keyRing).getKeyID();
- }
- if (keyId == 0) {
- // this shouldn't happen
- return true;
- }
-
- Intent intent = new Intent(this, KeyServerQueryActivity.class);
- intent.setAction(Apg.Intent.LOOK_UP_KEY_ID_AND_RETURN);
- intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
- startActivityForResult(intent, Id.request.look_up_key_id);
-
- return true;
- }
-
- case Id.menu.exportToServer: {
- mSelectedItem = groupPosition;
- final int keyRingId = mListAdapter.getKeyRingId(groupPosition);
-
- Intent intent = new Intent(this, SendKeyActivity.class);
- intent.setAction(Apg.Intent.EXPORT_KEY_TO_SERVER);
- intent.putExtra(Apg.EXTRA_KEY_ID, keyRingId);
- startActivityForResult(intent, Id.request.export_to_server);
-
- return true;
- }
-
- case Id.menu.signKey: {
- mSelectedItem = groupPosition;
- final int keyRingId = mListAdapter.getKeyRingId(groupPosition);
- long keyId = 0;
- Object keyRing = Apg.getKeyRing(keyRingId);
- if (keyRing != null && keyRing instanceof PGPPublicKeyRing) {
- keyId = Apg.getMasterKey((PGPPublicKeyRing) keyRing).getKeyID();
- }
-
- if (keyId == 0) {
- // this shouldn't happen
- return true;
- }
-
- Intent intent = new Intent(this, SignKeyActivity.class);
- intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
- startActivity(intent);
-
- return true;
- }
-
- default: {
- return super.onContextItemSelected(menuItem);
- }
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case Id.menu.option.scanQRCode: {
- Intent intent = new Intent(this, ImportFromQRCodeActivity.class);
- intent.setAction(Apg.Intent.IMPORT_FROM_QR_CODE);
- startActivityForResult(intent, Id.request.import_from_qr_code);
-
- return true;
- }
-
- default: {
- return super.onOptionsItemSelected(item);
- }
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.look_up_key_id: {
- if (resultCode == RESULT_CANCELED || data == null
- || data.getStringExtra(Apg.EXTRA_TEXT) == null) {
- return;
- }
-
- Intent intent = new Intent(this, PublicKeyListActivity.class);
- intent.setAction(Apg.Intent.IMPORT);
- intent.putExtra(Apg.EXTRA_TEXT, data.getStringExtra(Apg.EXTRA_TEXT));
- handleIntent(intent);
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- break;
- }
- }
- }
-}
diff --git a/src/org/apg/ui/SecretKeyListActivity.java b/src/org/apg/ui/SecretKeyListActivity.java
deleted file mode 100644
index a5d351bc6..000000000
--- a/src/org/apg/ui/SecretKeyListActivity.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.AskForSecretKeyPassPhrase;
-import org.apg.Constants;
-import org.apg.Id;
-import org.apg.Constants.path;
-import org.apg.Id.dialog;
-import org.apg.Id.menu;
-import org.apg.Id.message;
-import org.apg.Id.type;
-import org.apg.Id.menu.option;
-import org.apg.R;
-
-import android.app.Dialog;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
-import android.widget.ExpandableListView.OnChildClickListener;
-
-import com.google.zxing.integration.android.IntentIntegrator;
-
-public class SecretKeyListActivity extends KeyListActivity implements OnChildClickListener {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- mExportFilename = Constants.path.APP_DIR + "/secexport.asc";
- mKeyType = Id.type.secret_key;
- super.onCreate(savedInstanceState);
- mList.setOnChildClickListener(this);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.import_keys, 0, R.string.menu_importKeys)
- .setIcon(android.R.drawable.ic_menu_add);
- 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.create, 2, R.string.menu_createKey)
- .setIcon(android.R.drawable.ic_menu_add);
- menu.add(3, Id.menu.option.search, 3, R.string.menu_search)
- .setIcon(android.R.drawable.ic_menu_search);
- menu.add(3, Id.menu.option.preferences, 4, R.string.menu_preferences)
- .setIcon(android.R.drawable.ic_menu_preferences);
- menu.add(3, Id.menu.option.about, 5, 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.create: {
- createKey();
- return true;
- }
-
- default: {
- return super.onOptionsItemSelected(item);
- }
- }
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- ExpandableListView.ExpandableListContextMenuInfo info =
- (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
- int type = ExpandableListView.getPackedPositionType(info.packedPosition);
-
- if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- // TODO: user id? menu.setHeaderTitle("Key");
- menu.add(0, Id.menu.edit, 0, R.string.menu_editKey);
- menu.add(0, Id.menu.export, 1, R.string.menu_exportKey);
- menu.add(0, Id.menu.delete, 2, R.string.menu_deleteKey);
- menu.add(0, Id.menu.share, 2, R.string.menu_share);
- }
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem) {
- ExpandableListContextMenuInfo info = (ExpandableListContextMenuInfo) menuItem.getMenuInfo();
- int type = ExpandableListView.getPackedPositionType(info.packedPosition);
- int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
-
- if (type != ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
- return super.onContextItemSelected(menuItem);
- }
-
- switch (menuItem.getItemId()) {
- case Id.menu.edit: {
- mSelectedItem = groupPosition;
- checkPassPhraseAndEdit();
- return true;
- }
-
- case Id.menu.share: {
- mSelectedItem = groupPosition;
-
- long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
- String msg = keyId + "," + Apg.getFingerPrint(keyId);;
-
- new IntentIntegrator(this).shareText(msg);
- }
-
- default: {
- return super.onContextItemSelected(menuItem);
- }
- }
- }
-
- public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
- int childPosition, long id) {
- mSelectedItem = groupPosition;
- checkPassPhraseAndEdit();
- return true;
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- switch (id) {
- case Id.dialog.pass_phrase: {
- long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
- return AskForSecretKeyPassPhrase.createDialog(this, keyId, this);
- }
-
- default: {
- return super.onCreateDialog(id);
- }
- }
- }
-
- public void checkPassPhraseAndEdit() {
- long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
- String passPhrase = Apg.getCachedPassPhrase(keyId);
- if (passPhrase == null) {
- showDialog(Id.dialog.pass_phrase);
- } else {
- Apg.setEditPassPhrase(passPhrase);
- editKey();
- }
- }
-
- @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, Id.message.create_key);
- }
-
- private void editKey() {
- long keyId = ((KeyListAdapter) mList.getExpandableListAdapter()).getGroupId(mSelectedItem);
- Intent intent = new Intent(this, EditKeyActivity.class);
- intent.putExtra(Apg.EXTRA_KEY_ID, keyId);
- startActivityForResult(intent, Id.message.edit_key);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.message.create_key: // intentionally no break
- case Id.message.edit_key: {
- if (resultCode == RESULT_OK) {
- refreshList();
- }
- break;
- }
-
- default: {
- break;
- }
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-}
diff --git a/src/org/apg/ui/SelectPublicKeyListActivity.java b/src/org/apg/ui/SelectPublicKeyListActivity.java
deleted file mode 100644
index 5216e7a3d..000000000
--- a/src/org/apg/ui/SelectPublicKeyListActivity.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import java.util.Vector;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.Id.menu;
-import org.apg.Id.menu.option;
-import org.apg.R;
-
-import android.app.SearchManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.TextView;
-
-public class SelectPublicKeyListActivity extends BaseActivity {
- protected ListView mList;
- protected SelectPublicKeyListAdapter mListAdapter;
- protected View mFilterLayout;
- protected Button mClearFilterButton;
- protected TextView mFilterInfo;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.select_public_key);
-
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- mList = (ListView) findViewById(R.id.list);
- // needed in Android 1.5, where the XML attribute gets ignored
- mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
-
- Button okButton = (Button) findViewById(R.id.btn_ok);
- okButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- okClicked();
- }
- });
-
- Button cancelButton = (Button) findViewById(R.id.btn_cancel);
- cancelButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- cancelClicked();
- }
- });
-
- mFilterLayout = findViewById(R.id.layout_filter);
- mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
- mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
-
- mClearFilterButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- handleIntent(new Intent());
- }
- });
-
- handleIntent(getIntent());
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- String searchString = null;
- if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- searchString = intent.getStringExtra(SearchManager.QUERY);
- if (searchString != null && searchString.trim().length() == 0) {
- searchString = null;
- }
- }
-
- long selectedKeyIds[] = null;
- selectedKeyIds = intent.getLongArrayExtra(Apg.EXTRA_SELECTION);
-
- if (selectedKeyIds == null) {
- Vector vector = new Vector();
- for (int i = 0; i < mList.getCount(); ++i) {
- if (mList.isItemChecked(i)) {
- vector.add(mList.getItemIdAtPosition(i));
- }
- }
- selectedKeyIds = new long[vector.size()];
- for (int i = 0; i < vector.size(); ++i) {
- selectedKeyIds[i] = vector.get(i);
- }
- }
-
- if (searchString == null) {
- mFilterLayout.setVisibility(View.GONE);
- } else {
- mFilterLayout.setVisibility(View.VISIBLE);
- mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- }
-
- if (mListAdapter != null) {
- mListAdapter.cleanup();
- }
-
- mListAdapter = new SelectPublicKeyListAdapter(this, mList, searchString, selectedKeyIds);
- mList.setAdapter(mListAdapter);
-
- if (selectedKeyIds != null) {
- for (int i = 0; i < mListAdapter.getCount(); ++i) {
- long keyId = mListAdapter.getItemId(i);
- for (int j = 0; j < selectedKeyIds.length; ++j) {
- if (keyId == selectedKeyIds[j]) {
- mList.setItemChecked(i, true);
- break;
- }
- }
- }
- }
- }
-
- private void cancelClicked() {
- setResult(RESULT_CANCELED, null);
- finish();
- }
-
- private void okClicked() {
- Intent data = new Intent();
- Vector keys = new Vector();
- Vector userIds = new Vector();
- for (int i = 0; i < mList.getCount(); ++i) {
- if (mList.isItemChecked(i)) {
- keys.add(mList.getItemIdAtPosition(i));
- userIds.add((String) mList.getItemAtPosition(i));
- }
- }
- long selectedKeyIds[] = new long[keys.size()];
- for (int i = 0; i < keys.size(); ++i) {
- selectedKeyIds[i] = keys.get(i);
- }
- String userIdArray[] = new String[0];
- data.putExtra(Apg.EXTRA_SELECTION, selectedKeyIds);
- data.putExtra(Apg.EXTRA_USER_IDS, userIds.toArray(userIdArray));
- setResult(RESULT_OK, data);
- finish();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.search, 0, R.string.menu_search)
- .setIcon(android.R.drawable.ic_menu_search);
- return true;
- }
-}
diff --git a/src/org/apg/ui/SelectPublicKeyListAdapter.java b/src/org/apg/ui/SelectPublicKeyListAdapter.java
deleted file mode 100644
index b2f49f74a..000000000
--- a/src/org/apg/ui/SelectPublicKeyListAdapter.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import java.util.Date;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.Id.database;
-import org.apg.provider.KeyRings;
-import org.apg.provider.Keys;
-import org.apg.provider.UserIds;
-import org.apg.R;
-
-import android.app.Activity;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-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 LayoutInflater mInflater;
- protected ListView mParent;
- protected SQLiteDatabase mDatabase;
- protected Cursor mCursor;
- protected String mSearchString;
- protected Activity mActivity;
-
- public SelectPublicKeyListAdapter(Activity activity, ListView parent,
- String searchString, long selectedKeyIds[]) {
- mSearchString = searchString;
-
- mActivity = activity;
- mParent = parent;
- mDatabase = Apg.getDatabase().db();
- mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- long now = new Date().getTime() / 1000;
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
- "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
- Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
- Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
- ") " +
- " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
- "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
- UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
- UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
-
- String inIdList = null;
-
- if (selectedKeyIds != null && selectedKeyIds.length > 0) {
- inIdList = KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID + " IN (";
- for (int i = 0; i < selectedKeyIds.length; ++i) {
- if (i != 0) {
- inIdList += ", ";
- }
- inIdList += DatabaseUtils.sqlEscapeString("" + selectedKeyIds[i]);
- }
- inIdList += ")";
- }
-
- if (searchString != null && searchString.trim().length() > 0) {
- String[] chunks = searchString.trim().split(" +");
- qb.appendWhere("(EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
- UserIds.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + UserIds.KEY_ID + " = " +
- Keys.TABLE_NAME + "." + Keys._ID);
- for (int i = 0; i < chunks.length; ++i) {
- qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
- qb.appendWhereEscapeString("%" + chunks[i] + "%");
- }
- qb.appendWhere("))");
-
- if (inIdList != null) {
- qb.appendWhere(" OR (" + inIdList + ")");
- }
- }
-
- String orderBy = UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC";
- if (inIdList != null) {
- orderBy = inIdList + " DESC, " + orderBy;
- }
-
- mCursor = qb.query(mDatabase,
- new String[] {
- KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
- UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
- "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + Keys.KEY_RING_ID + " = " +
- KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
- "tmp." + Keys.IS_REVOKED + " = '0' AND " +
- "tmp." + Keys.CAN_ENCRYPT + " = '1')", // 3
- "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + Keys.KEY_RING_ID + " = " +
- KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
- "tmp." + Keys.IS_REVOKED + " = '0' AND " +
- "tmp." + Keys.CAN_ENCRYPT + " = '1' AND " +
- "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
- "(tmp." + Keys.EXPIRY + " IS NULL OR " +
- "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
- },
- KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
- new String[] { "" + Id.database.type_public },
- null, null, orderBy);
-
- activity.startManagingCursor(mCursor);
- }
-
- public void cleanup() {
- if (mCursor != null) {
- mActivity.stopManagingCursor(mCursor);
- mCursor.close();
- }
- }
-
- @Override
- public boolean isEnabled(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getInt(4) > 0; // valid CAN_ENCRYPT
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- public int getCount() {
- return mCursor.getCount();
- }
-
- public Object getItem(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getString(2); // USER_ID
- }
-
- public long getItemId(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getLong(1); // MASTER_KEY_ID
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- mCursor.moveToPosition(position);
-
- View view = mInflater.inflate(R.layout.select_public_key_item, null);
- boolean enabled = isEnabled(position);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.unknownUserId);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- keyId.setText(R.string.noKey);
- TextView status = (TextView) view.findViewById(R.id.status);
- status.setText(R.string.unknownStatus);
-
- String userId = mCursor.getString(2); // USER_ID
- if (userId != null) {
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mainUserIdRest.setText("<" + chunks[1]);
- }
- mainUserId.setText(userId);
- }
-
- long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
- keyId.setText(Apg.getSmallFingerPrint(masterKeyId));
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- }
-
- if (enabled) {
- status.setText(R.string.canEncrypt);
- } else {
- if (mCursor.getInt(3) > 0) {
- // has some CAN_ENCRYPT keys, but col(4) = 0, so must be revoked or expired
- status.setText(R.string.expired);
- } else {
- status.setText(R.string.noKey);
- }
- }
-
- status.setText(status.getText() + " ");
-
- CheckBox selected = (CheckBox) view.findViewById(R.id.selected);
-
- if (!enabled) {
- mParent.setItemChecked(position, false);
- }
-
- selected.setChecked(mParent.isItemChecked(position));
-
- view.setEnabled(enabled);
- mainUserId.setEnabled(enabled);
- mainUserIdRest.setEnabled(enabled);
- keyId.setEnabled(enabled);
- selected.setEnabled(enabled);
- status.setEnabled(enabled);
-
- return view;
- }
-}
diff --git a/src/org/apg/ui/SelectSecretKeyListActivity.java b/src/org/apg/ui/SelectSecretKeyListActivity.java
deleted file mode 100644
index 191a0ecc7..000000000
--- a/src/org/apg/ui/SelectSecretKeyListActivity.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.Id.menu;
-import org.apg.Id.menu.option;
-import org.apg.R;
-
-import android.app.SearchManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Button;
-import android.widget.ListView;
-import android.widget.TextView;
-
-public class SelectSecretKeyListActivity extends BaseActivity {
- protected ListView mList;
- protected SelectSecretKeyListAdapter mListAdapter;
- protected View mFilterLayout;
- protected Button mClearFilterButton;
- protected TextView mFilterInfo;
-
- protected long mSelectedKeyId = 0;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- setContentView(R.layout.select_secret_key);
-
- mList = (ListView) findViewById(R.id.list);
-
- mList.setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView> adapterView, View view, int position, long id) {
- Intent data = new Intent();
- data.putExtra(Apg.EXTRA_KEY_ID, id);
- data.putExtra(Apg.EXTRA_USER_ID, (String)mList.getItemAtPosition(position));
- setResult(RESULT_OK, data);
- finish();
- }
- });
-
- mFilterLayout = findViewById(R.id.layout_filter);
- mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
- mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
-
- mClearFilterButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- handleIntent(new Intent());
- }
- });
-
- handleIntent(getIntent());
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- String searchString = null;
- if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- searchString = intent.getStringExtra(SearchManager.QUERY);
- if (searchString != null && searchString.trim().length() == 0) {
- searchString = null;
- }
- }
-
- if (searchString == null) {
- mFilterLayout.setVisibility(View.GONE);
- } else {
- mFilterLayout.setVisibility(View.VISIBLE);
- mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- }
-
- if (mListAdapter != null) {
- mListAdapter.cleanup();
- }
-
- mListAdapter = new SelectSecretKeyListAdapter(this, mList, searchString);
- mList.setAdapter(mListAdapter);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- menu.add(0, Id.menu.option.search, 0, R.string.menu_search)
- .setIcon(android.R.drawable.ic_menu_search);
- return true;
- }
-}
diff --git a/src/org/apg/ui/SelectSecretKeyListAdapter.java b/src/org/apg/ui/SelectSecretKeyListAdapter.java
deleted file mode 100644
index 1a7734245..000000000
--- a/src/org/apg/ui/SelectSecretKeyListAdapter.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package org.apg.ui;
-
-import java.util.Date;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.Id.database;
-import org.apg.provider.KeyRings;
-import org.apg.provider.Keys;
-import org.apg.provider.UserIds;
-import org.apg.R;
-
-import android.app.Activity;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-public class SelectSecretKeyListAdapter extends BaseAdapter {
- protected LayoutInflater mInflater;
- protected ListView mParent;
- protected SQLiteDatabase mDatabase;
- protected Cursor mCursor;
- protected String mSearchString;
- protected Activity mActivity;
-
- public SelectSecretKeyListAdapter(Activity activity, ListView parent, String searchString) {
- mSearchString = searchString;
-
- mActivity = activity;
- mParent = parent;
- mDatabase = Apg.getDatabase().db();
- mInflater = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- long now = new Date().getTime() / 1000;
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- qb.setTables(KeyRings.TABLE_NAME + " INNER JOIN " + Keys.TABLE_NAME + " ON " +
- "(" + KeyRings.TABLE_NAME + "." + KeyRings._ID + " = " +
- Keys.TABLE_NAME + "." + Keys.KEY_RING_ID + " AND " +
- Keys.TABLE_NAME + "." + Keys.IS_MASTER_KEY + " = '1'" +
- ") " +
- " INNER JOIN " + UserIds.TABLE_NAME + " ON " +
- "(" + Keys.TABLE_NAME + "." + Keys._ID + " = " +
- UserIds.TABLE_NAME + "." + UserIds.KEY_ID + " AND " +
- UserIds.TABLE_NAME + "." + UserIds.RANK + " = '0') ");
-
- if (searchString != null && searchString.trim().length() > 0) {
- String[] chunks = searchString.trim().split(" +");
- qb.appendWhere("EXISTS (SELECT tmp." + UserIds._ID + " FROM " +
- UserIds.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + UserIds.KEY_ID + " = " +
- Keys.TABLE_NAME + "." + Keys._ID);
- for (int i = 0; i < chunks.length; ++i) {
- qb.appendWhere(" AND tmp." + UserIds.USER_ID + " LIKE ");
- qb.appendWhereEscapeString("%" + chunks[i] + "%");
- }
- qb.appendWhere(")");
- }
-
- mCursor = qb.query(mDatabase,
- new String[] {
- KeyRings.TABLE_NAME + "." + KeyRings._ID, // 0
- KeyRings.TABLE_NAME + "." + KeyRings.MASTER_KEY_ID, // 1
- UserIds.TABLE_NAME + "." + UserIds.USER_ID, // 2
- "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + Keys.KEY_RING_ID + " = " +
- KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
- "tmp." + Keys.IS_REVOKED + " = '0' AND " +
- "tmp." + Keys.CAN_SIGN + " = '1')", // 3,
- "(SELECT COUNT(tmp." + Keys._ID + ") FROM " + Keys.TABLE_NAME + " AS tmp WHERE " +
- "tmp." + Keys.KEY_RING_ID + " = " +
- KeyRings.TABLE_NAME + "." + KeyRings._ID + " AND " +
- "tmp." + Keys.IS_REVOKED + " = '0' AND " +
- "tmp." + Keys.CAN_SIGN + " = '1' AND " +
- "tmp." + Keys.CREATION + " <= '" + now + "' AND " +
- "(tmp." + Keys.EXPIRY + " IS NULL OR " +
- "tmp." + Keys.EXPIRY + " >= '" + now + "'))", // 4
- },
- KeyRings.TABLE_NAME + "." + KeyRings.TYPE + " = ?",
- new String[] { "" + Id.database.type_secret },
- null, null, UserIds.TABLE_NAME + "." + UserIds.USER_ID + " ASC");
-
- activity.startManagingCursor(mCursor);
- }
-
- public void cleanup() {
- if (mCursor != null) {
- mActivity.stopManagingCursor(mCursor);
- mCursor.close();
- }
- }
-
- @Override
- public boolean isEnabled(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getInt(4) > 0; // valid CAN_SIGN
- }
-
- @Override
- public boolean hasStableIds() {
- return true;
- }
-
- public int getCount() {
- return mCursor.getCount();
- }
-
- public Object getItem(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getString(2); // USER_ID
- }
-
- public long getItemId(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getLong(1); // MASTER_KEY_ID
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- mCursor.moveToPosition(position);
-
- View view = mInflater.inflate(R.layout.select_secret_key_item, null);
- boolean enabled = isEnabled(position);
-
- TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
- mainUserId.setText(R.string.unknownUserId);
- TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- mainUserIdRest.setText("");
- TextView keyId = (TextView) view.findViewById(R.id.keyId);
- keyId.setText(R.string.noKey);
- TextView status = (TextView) view.findViewById(R.id.status);
- status.setText(R.string.unknownStatus);
-
- String userId = mCursor.getString(2); // USER_ID
- if (userId != null) {
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mainUserIdRest.setText("<" + chunks[1]);
- }
- mainUserId.setText(userId);
- }
-
- long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
- keyId.setText(Apg.getSmallFingerPrint(masterKeyId));
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- }
-
- if (enabled) {
- status.setText(R.string.canSign);
- } else {
- if (mCursor.getInt(3) > 0) {
- // has some CAN_SIGN keys, but col(4) = 0, so must be revoked or expired
- status.setText(R.string.expired);
- } else {
- status.setText(R.string.noKey);
- }
- }
-
- status.setText(status.getText() + " ");
-
- view.setEnabled(enabled);
- mainUserId.setEnabled(enabled);
- mainUserIdRest.setEnabled(enabled);
- keyId.setEnabled(enabled);
- status.setEnabled(enabled);
-
- return view;
- }
-}
\ No newline at end of file
diff --git a/src/org/apg/ui/SendKeyActivity.java b/src/org/apg/ui/SendKeyActivity.java
deleted file mode 100644
index c44e87469..000000000
--- a/src/org/apg/ui/SendKeyActivity.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package org.apg.ui;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.HkpKeyServer;
-import org.apg.Id;
-import org.apg.Constants.extras;
-import org.apg.Id.message;
-import org.spongycastle.openpgp.PGPKeyRing;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.apg.R;
-
-import android.os.Bundle;
-import android.os.Message;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.Spinner;
-import android.widget.Toast;
-
-/**
- * gpg --send-key activity
- *
- * Sends the selected public key to a key server
- */
-public class SendKeyActivity extends BaseActivity {
-
- private Button export;
- private Spinner keyServer;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- setContentView(R.layout.key_server_export_layout);
-
- export = (Button) findViewById(R.id.btn_export_to_server);
- keyServer = (Spinner) findViewById(R.id.keyServer);
-
- ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, mPreferences.getKeyServers());
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- keyServer.setAdapter(adapter);
- if (adapter.getCount() > 0) {
- keyServer.setSelection(0);
- } else {
- export.setEnabled(false);
- }
-
- export.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- startThread();
- }
- });
- }
-
- @Override
- public void run() {
- String error = null;
- Bundle data = new Bundle();
- Message msg = new Message();
-
- HkpKeyServer server = new HkpKeyServer((String) keyServer.getSelectedItem());
-
- int keyRingId = getIntent().getIntExtra(Apg.EXTRA_KEY_ID, -1);
-
- PGPKeyRing keyring = Apg.getKeyRing(keyRingId);
- if (keyring != null && keyring instanceof PGPPublicKeyRing) {
- boolean uploaded = Apg.uploadKeyRingToServer(server, (PGPPublicKeyRing) keyring);
- if (!uploaded) {
- error = "Unable to export key to selected server";
- }
- }
-
- data.putInt(Constants.extras.STATUS, Id.message.export_done);
-
- if (error != null) {
- data.putString(Apg.EXTRA_ERROR, error);
- }
-
- msg.setData(data);
- sendMessage(msg);
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- Bundle data = msg.getData();
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
- return;
- }
-
- Toast.makeText(this, R.string.keySendSuccess, Toast.LENGTH_SHORT).show();
- finish();
- }
-}
diff --git a/src/org/apg/ui/SignKeyActivity.java b/src/org/apg/ui/SignKeyActivity.java
deleted file mode 100644
index ab145c921..000000000
--- a/src/org/apg/ui/SignKeyActivity.java
+++ /dev/null
@@ -1,294 +0,0 @@
-package org.apg.ui;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SignatureException;
-import java.util.Iterator;
-
-import org.apg.Apg;
-import org.apg.Constants;
-import org.apg.HkpKeyServer;
-import org.apg.Id;
-import org.apg.Constants.extras;
-import org.apg.Id.dialog;
-import org.apg.Id.message;
-import org.apg.Id.request;
-import org.apg.Id.return_value;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPrivateKey;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
-import org.spongycastle.openpgp.PGPUtil;
-import org.apg.R;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Message;
-import android.util.Log;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.Spinner;
-import android.widget.Toast;
-
-/**
- * gpg --sign-key
- *
- * signs the specified public key with the specified secret master key
- */
-public class SignKeyActivity extends BaseActivity {
- private static final String TAG = "SignKeyActivity";
-
- private long pubKeyId = 0;
- private long masterKeyId = 0;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // check we havent already signed it
- setContentView(R.layout.sign_key_layout);
-
- final Spinner keyServer = (Spinner) findViewById(R.id.keyServer);
- ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, mPreferences.getKeyServers());
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- keyServer.setAdapter(adapter);
-
- final CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey);
- if (!sendKey.isChecked()) {
- keyServer.setEnabled(false);
- } else {
- keyServer.setEnabled(true);
- }
-
- sendKey.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (!isChecked) {
- keyServer.setEnabled(false);
- } else {
- keyServer.setEnabled(true);
- }
- }
- });
-
- Button sign = (Button) findViewById(R.id.sign);
- sign.setEnabled(false); // disabled until the user selects a key to sign with
- sign.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (pubKeyId != 0) {
- initiateSigning();
- }
- }
- });
-
- pubKeyId = getIntent().getLongExtra(Apg.EXTRA_KEY_ID, 0);
- if (pubKeyId == 0) {
- finish(); // nothing to do if we dont know what key to sign
- } else {
- // kick off the SecretKey selection activity so the user chooses which key to sign with first
- Intent intent = new Intent(this, SelectSecretKeyListActivity.class);
- startActivityForResult(intent, Id.request.secret_keys);
- }
- }
-
- /**
- * handles the UI bits of the signing process on the UI thread
- */
- private void initiateSigning() {
- PGPPublicKeyRing pubring = Apg.getPublicKeyRing(pubKeyId);
- if (pubring != null) {
- // if we have already signed this key, dont bother doing it again
- boolean alreadySigned = false;
-
- @SuppressWarnings("unchecked")
- Iterator itr = pubring.getPublicKey(pubKeyId).getSignatures();
- while (itr.hasNext()) {
- PGPSignature sig = itr.next();
- if (sig.getKeyID() == masterKeyId) {
- alreadySigned = true;
- break;
- }
- }
-
- if (!alreadySigned) {
- /*
- * get the user's passphrase for this key (if required)
- */
- String passphrase = Apg.getCachedPassPhrase(masterKeyId);
- if (passphrase == null) {
- showDialog(Id.dialog.pass_phrase);
- return; // bail out; need to wait until the user has entered the passphrase before trying again
- } else {
- startSigning();
- }
- } else {
- final Bundle status = new Bundle();
- Message msg = new Message();
-
- status.putString(Apg.EXTRA_ERROR, "Key has already been signed");
-
- status.putInt(Constants.extras.STATUS, Id.message.done);
-
- msg.setData(status);
- sendMessage(msg);
-
- setResult(Id.return_value.error);
- finish();
- }
- }
- }
-
- @Override
- public long getSecretKeyId() {
- return masterKeyId;
- }
-
- @Override
- public void passPhraseCallback(long keyId, String passPhrase) {
- super.passPhraseCallback(keyId, passPhrase);
- startSigning();
- }
-
- /**
- * kicks off the actual signing process on a background thread
- */
- private void startSigning() {
- showDialog(Id.dialog.signing);
- startThread();
- }
-
- @Override
- public void run() {
- final Bundle status = new Bundle();
- Message msg = new Message();
-
- try {
- String passphrase = Apg.getCachedPassPhrase(masterKeyId);
- if (passphrase == null || passphrase.length() <= 0) {
- status.putString(Apg.EXTRA_ERROR, "Unable to obtain passphrase");
- } else {
- PGPPublicKeyRing pubring = Apg.getPublicKeyRing(pubKeyId);
-
- /*
- * sign the incoming key
- */
- PGPSecretKey secretKey = Apg.getSecretKey(masterKeyId);
- PGPPrivateKey signingKey = secretKey.extractPrivateKey(passphrase.toCharArray(), BouncyCastleProvider.PROVIDER_NAME);
- PGPSignatureGenerator sGen = new PGPSignatureGenerator(secretKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256, BouncyCastleProvider.PROVIDER_NAME);
- sGen.initSign(PGPSignature.DIRECT_KEY, signingKey);
-
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
-
- PGPSignatureSubpacketVector packetVector = spGen.generate();
- sGen.setHashedSubpackets(packetVector);
-
- PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), sGen.generate());
- pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
-
- // check if we need to send the key to the server or not
- CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey);
- if (sendKey.isChecked()) {
- Spinner keyServer = (Spinner) findViewById(R.id.keyServer);
- HkpKeyServer server = new HkpKeyServer((String) keyServer.getSelectedItem());
-
- /*
- * upload the newly signed key to the key server
- */
-
- Apg.uploadKeyRingToServer(server, pubring);
- }
-
- // store the signed key in our local cache
- int retval = Apg.storeKeyRingInCache(pubring);
- if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
- status.putString(Apg.EXTRA_ERROR, "Failed to store signed key in local cache");
- }
- }
- } catch (PGPException e) {
- Log.e(TAG, "Failed to sign key", e);
- status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
- status.putInt(Constants.extras.STATUS, Id.message.done);
- return;
- } catch (NoSuchAlgorithmException e) {
- Log.e(TAG, "Failed to sign key", e);
- status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
- status.putInt(Constants.extras.STATUS, Id.message.done);
- return;
- } catch (NoSuchProviderException e) {
- Log.e(TAG, "Failed to sign key", e);
- status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
- status.putInt(Constants.extras.STATUS, Id.message.done);
- return;
- } catch (SignatureException e) {
- Log.e(TAG, "Failed to sign key", e);
- status.putString(Apg.EXTRA_ERROR, "Failed to sign key");
- status.putInt(Constants.extras.STATUS, Id.message.done);
- return;
- }
-
- status.putInt(Constants.extras.STATUS, Id.message.done);
-
- msg.setData(status);
- sendMessage(msg);
-
- if (status.containsKey(Apg.EXTRA_ERROR)) {
- setResult(Id.return_value.error);
- } else {
- setResult(Id.return_value.ok);
- }
-
- finish();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.secret_keys: {
- if (resultCode == RESULT_OK) {
- masterKeyId = data.getLongExtra(Apg.EXTRA_KEY_ID, 0);
-
- // re-enable the sign button so the user can initiate the sign process
- Button sign = (Button) findViewById(R.id.sign);
- sign.setEnabled(true);
- }
-
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
- }
- }
- }
-
- @Override
- public void doneCallback(Message msg) {
- super.doneCallback(msg);
-
- removeDialog(Id.dialog.signing);
-
- Bundle data = msg.getData();
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
- return;
- }
-
- Toast.makeText(this, R.string.keySignSuccess, Toast.LENGTH_SHORT).show();
- finish();
- }
-}
diff --git a/src/org/apg/ui/widget/Editor.java b/src/org/apg/ui/widget/Editor.java
deleted file mode 100644
index be95ad656..000000000
--- a/src/org/apg/ui/widget/Editor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui.widget;
-
-public interface Editor {
- public interface EditorListener {
- public void onDeleted(Editor editor);
- }
-
- public void setEditorListener(EditorListener listener);
-}
diff --git a/src/org/apg/ui/widget/IntegerListPreference.java b/src/org/apg/ui/widget/IntegerListPreference.java
deleted file mode 100644
index fa411a786..000000000
--- a/src/org/apg/ui/widget/IntegerListPreference.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright 2010 Google Inc.
- *
- * 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.apg.ui.widget;
-
-import android.content.Context;
-import android.preference.ListPreference;
-import android.util.AttributeSet;
-
-/**
- * A list preference which persists its values as integers instead of strings.
- * Code reading the values should use
- * {@link android.content.SharedPreferences#getInt}.
- * When using XML-declared arrays for entry values, the arrays should be regular
- * string arrays containing valid integer values.
- *
- * @author Rodrigo Damazio
- */
-public class IntegerListPreference extends ListPreference {
-
- public IntegerListPreference(Context context) {
- super(context);
-
- verifyEntryValues(null);
- }
-
- public IntegerListPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- verifyEntryValues(null);
- }
-
- @Override
- public void setEntryValues(CharSequence[] entryValues) {
- CharSequence[] oldValues = getEntryValues();
- super.setEntryValues(entryValues);
- verifyEntryValues(oldValues);
- }
-
- @Override
- public void setEntryValues(int entryValuesResId) {
- CharSequence[] oldValues = getEntryValues();
- super.setEntryValues(entryValuesResId);
- verifyEntryValues(oldValues);
- }
-
- @Override
- protected String getPersistedString(String defaultReturnValue) {
- // During initial load, there's no known default value
- int defaultIntegerValue = Integer.MIN_VALUE;
- if (defaultReturnValue != null) {
- defaultIntegerValue = Integer.parseInt(defaultReturnValue);
- }
-
- // When the list preference asks us to read a string, instead read an
- // integer.
- int value = getPersistedInt(defaultIntegerValue);
- return Integer.toString(value);
- }
-
- @Override
- protected boolean persistString(String value) {
- // When asked to save a string, instead save an integer
- return persistInt(Integer.parseInt(value));
- }
-
- private void verifyEntryValues(CharSequence[] oldValues) {
- CharSequence[] entryValues = getEntryValues();
- if (entryValues == null) {
- return;
- }
-
- for (CharSequence entryValue : entryValues) {
- try {
- Integer.parseInt(entryValue.toString());
- } catch (NumberFormatException nfe) {
- super.setEntryValues(oldValues);
- throw nfe;
- }
- }
- }
-}
diff --git a/src/org/apg/ui/widget/KeyEditor.java b/src/org/apg/ui/widget/KeyEditor.java
deleted file mode 100644
index ef98f794a..000000000
--- a/src/org/apg/ui/widget/KeyEditor.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui.widget;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.util.Choice;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.apg.R;
-
-import android.app.DatePickerDialog;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.DatePicker;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Vector;
-
-public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
- private PGPSecretKey mKey;
-
- private EditorListener mEditorListener = null;
-
- private boolean mIsMasterKey;
- ImageButton mDeleteButton;
- TextView mAlgorithm;
- TextView mKeyId;
- Spinner mUsage;
- TextView mCreationDate;
- Button mExpiryDateButton;
- GregorianCalendar mExpiryDate;
-
- private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
- new DatePickerDialog.OnDateSetListener() {
- public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
- GregorianCalendar date = new GregorianCalendar(year, monthOfYear, dayOfMonth);
- setExpiryDate(date);
- }
- };
-
- public KeyEditor(Context context) {
- super(context);
- }
-
- public KeyEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mAlgorithm = (TextView) findViewById(R.id.algorithm);
- mKeyId = (TextView) findViewById(R.id.keyId);
- mCreationDate = (TextView) findViewById(R.id.creation);
- mExpiryDateButton = (Button) findViewById(R.id.expiry);
- mUsage = (Spinner) findViewById(R.id.usage);
- 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 adapter =
- new ArrayAdapter(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.delete);
- mDeleteButton.setOnClickListener(this);
-
- setExpiryDate(null);
-
- mExpiryDateButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- GregorianCalendar date = mExpiryDate;
- if (date == null) {
- date = new GregorianCalendar();
- }
-
- DatePickerDialog dialog =
- new DatePickerDialog(getContext(), mExpiryDateSetListener,
- date.get(Calendar.YEAR),
- date.get(Calendar.MONTH),
- date.get(Calendar.DAY_OF_MONTH));
- dialog.setCancelable(true);
- dialog.setButton(Dialog.BUTTON_NEGATIVE,
- getContext().getString(R.string.btn_noDate),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- setExpiryDate(null);
- }
- });
- dialog.show();
- }
- });
-
- super.onFinishInflate();
- }
-
- public void setValue(PGPSecretKey key, boolean isMasterKey) {
- mKey = key;
-
- mIsMasterKey = isMasterKey;
- if (mIsMasterKey) {
- mDeleteButton.setVisibility(View.INVISIBLE);
- }
-
- mAlgorithm.setText(Apg.getAlgorithmInfo(key));
- String keyId1Str = Apg.getSmallFingerPrint(key.getKeyID());
- String keyId2Str = Apg.getSmallFingerPrint(key.getKeyID() >> 32);
- mKeyId.setText(keyId1Str + " " + keyId2Str);
-
- Vector choices = new Vector();
- 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 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)));
- }
-
- ArrayAdapter adapter =
- new ArrayAdapter(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)) {
- selectId = Id.choice.usage.sign_and_encrypt;
- } else {
- selectId = Id.choice.usage.encrypt_only;
- }
- } else {
- 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();
- cal.setTime(Apg.getCreationDate(key));
- mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime()));
- cal = new GregorianCalendar();
- Date date = Apg.getExpiryDate(key);
- if (date == null) {
- setExpiryDate(null);
- } else {
- cal.setTime(Apg.getExpiryDate(key));
- setExpiryDate(cal);
- }
- }
-
- public PGPSecretKey getValue() {
- return mKey;
- }
-
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup)getParent();
- if (v == mDeleteButton) {
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
- }
- }
- }
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-
- private void setExpiryDate(GregorianCalendar date) {
- mExpiryDate = date;
- if (date == null) {
- mExpiryDateButton.setText(R.string.none);
- } else {
- mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime()));
- }
- }
-
- public GregorianCalendar getExpiryDate() {
- return mExpiryDate;
- }
-
- public int getUsage() {
- return ((Choice) mUsage.getSelectedItem()).getId();
- }
-}
diff --git a/src/org/apg/ui/widget/KeyServerEditor.java b/src/org/apg/ui/widget/KeyServerEditor.java
deleted file mode 100644
index 3d8634c76..000000000
--- a/src/org/apg/ui/widget/KeyServerEditor.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui.widget;
-
-import org.apg.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener {
- private EditorListener mEditorListener = null;
-
- ImageButton mDeleteButton;
- TextView mServer;
-
- public KeyServerEditor(Context context) {
- super(context);
- }
-
- public KeyServerEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mServer = (TextView) findViewById(R.id.server);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
-
- super.onFinishInflate();
- }
-
- public void setValue(String value) {
- mServer.setText(value);
- }
-
- public String getValue() {
- return mServer.getText().toString().trim();
- }
-
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup)getParent();
- if (v == mDeleteButton) {
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
- }
- }
- }
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-}
diff --git a/src/org/apg/ui/widget/SectionView.java b/src/org/apg/ui/widget/SectionView.java
deleted file mode 100644
index 220699124..000000000
--- a/src/org/apg/ui/widget/SectionView.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui.widget;
-
-import org.apg.Apg;
-import org.apg.Id;
-import org.apg.ui.widget.Editor.EditorListener;
-import org.apg.util.Choice;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.apg.R;
-
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.Spinner;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidParameterException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.util.Vector;
-
-public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Runnable {
- private LayoutInflater mInflater;
- private View mAdd;
- private ViewGroup mEditors;
- private TextView mTitle;
- private int mType = 0;
-
- private Choice mNewKeyAlgorithmChoice;
- private int mNewKeySize;
-
- volatile private PGPSecretKey mNewKey;
- private ProgressDialog mProgressDialog;
- private Thread mRunningThread = null;
-
- private Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- Bundle data = msg.getData();
- if (data != null) {
- boolean closeProgressDialog = data.getBoolean("closeProgressDialog");
- if (closeProgressDialog) {
- if (mProgressDialog != null) {
- mProgressDialog.dismiss();
- mProgressDialog = null;
- }
- }
-
- String error = data.getString(Apg.EXTRA_ERROR);
- if (error != null) {
- Toast.makeText(getContext(),
- getContext().getString(R.string.errorMessage, error),
- Toast.LENGTH_SHORT).show();
- }
-
- boolean gotNewKey = data.getBoolean("gotNewKey");
- if (gotNewKey) {
- KeyEditor view =
- (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
- mEditors, false);
- view.setEditorListener(SectionView.this);
- boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(mNewKey, isMasterKey);
- mEditors.addView(view);
- SectionView.this.updateEditorsVisible();
- }
- }
- }
- };
-
- public SectionView(Context context) {
- super(context);
- }
-
- public SectionView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public ViewGroup getEditors() {
- return mEditors;
- }
-
- public void setType(int type) {
- mType = type;
- switch (type) {
- case Id.type.user_id: {
- mTitle.setText(R.string.section_userIds);
- break;
- }
-
- case Id.type.key: {
- mTitle.setText(R.string.section_keys);
- break;
- }
-
- default: {
- break;
- }
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected void onFinishInflate() {
- mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mAdd = findViewById(R.id.header);
- mAdd.setOnClickListener(this);
-
- mEditors = (ViewGroup) findViewById(R.id.editors);
- mTitle = (TextView) findViewById(R.id.title);
-
- updateEditorsVisible();
- super.onFinishInflate();
- }
-
- /** {@inheritDoc} */
- public void onDeleted(Editor editor) {
- this.updateEditorsVisible();
- }
-
- protected void updateEditorsVisible() {
- final boolean hasChildren = mEditors.getChildCount() > 0;
- mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
- }
-
- /** {@inheritDoc} */
- public void onClick(View v) {
- switch (mType) {
- case Id.type.user_id: {
- UserIdEditor view =
- (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
- mEditors, false);
- view.setEditorListener(this);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
- mEditors.addView(view);
- break;
- }
-
- 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(R.string.title_createKey);
- dialog.setMessage(R.string.keyCreationElGamalInfo);
-
- boolean wouldBeMasterKey = (mEditors.getChildCount() == 0);
-
- final Spinner algorithm = (Spinner) view.findViewById(R.id.algorithm);
- Vector choices = new Vector();
- 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 adapter =
- new ArrayAdapter(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.size(); ++i) {
- if (choices.get(i).getId() == Id.choice.algorithm.rsa) {
- algorithm.setSelection(i);
- break;
- }
- }
-
- final EditText keySize = (EditText) view.findViewById(R.id.size);
-
- dialog.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface di, int id) {
- di.dismiss();
- try {
- mNewKeySize = Integer.parseInt("" + keySize.getText());
- } catch (NumberFormatException e) {
- mNewKeySize = 0;
- }
-
- mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem();
- createKey();
- }
- });
-
- dialog.setCancelable(true);
- dialog.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface di, int id) {
- di.dismiss();
- }
- });
-
- dialog.create().show();
- break;
- }
-
- default: {
- break;
- }
- }
- this.updateEditorsVisible();
- }
-
- public void setUserIds(Vector list) {
- if (mType != Id.type.user_id) {
- return;
- }
-
- mEditors.removeAllViews();
- for (String userId : list) {
- UserIdEditor view =
- (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, mEditors, false);
- view.setEditorListener(this);
- view.setValue(userId);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
- mEditors.addView(view);
- }
-
- this.updateEditorsVisible();
- }
-
- public void setKeys(Vector list) {
- if (mType != Id.type.key) {
- return;
- }
-
- mEditors.removeAllViews();
- for (PGPSecretKey key : list) {
- KeyEditor view =
- (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, false);
- view.setEditorListener(this);
- boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(key, isMasterKey);
- mEditors.addView(view);
- }
-
- this.updateEditorsVisible();
- }
-
- private void createKey() {
- mProgressDialog = new ProgressDialog(getContext());
- mProgressDialog.setMessage(getContext().getString(R.string.progress_generating));
- mProgressDialog.setCancelable(false);
- mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- mProgressDialog.show();
- mRunningThread = new Thread(this);
- mRunningThread.start();
- }
-
- public void run() {
- String error = null;
- try {
- 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;
- } catch (NoSuchAlgorithmException e) {
- error = "" + e;
- } catch (PGPException e) {
- error = "" + e;
- } catch (InvalidParameterException e) {
- error = "" + e;
- } catch (InvalidAlgorithmParameterException e) {
- error = "" + e;
- } catch (Apg.GeneralException e) {
- error = "" + e;
- }
-
- Message message = new Message();
- Bundle data = new Bundle();
- data.putBoolean("closeProgressDialog", true);
- if (error != null) {
- data.putString(Apg.EXTRA_ERROR, error);
- } else {
- data.putBoolean("gotNewKey", true);
- }
- message.setData(data);
- mHandler.sendMessage(message);
- }
-}
diff --git a/src/org/apg/ui/widget/UserIdEditor.java b/src/org/apg/ui/widget/UserIdEditor.java
deleted file mode 100644
index b154803cf..000000000
--- a/src/org/apg/ui/widget/UserIdEditor.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.ui.widget;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apg.R;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.RadioButton;
-
-public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
- private EditorListener mEditorListener = null;
-
- private ImageButton mDeleteButton;
- private RadioButton mIsMainUserId;
- private EditText mName;
- private EditText mEmail;
- private EditText mComment;
-
- private static final Pattern EMAIL_PATTERN =
- Pattern.compile("^([a-zA-Z0-9_.-])+@([a-zA-Z0-9_.-])+[.]([a-zA-Z])+([a-zA-Z])+",
- Pattern.CASE_INSENSITIVE);
-
- public static class NoNameException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public NoNameException(String message) {
- super(message);
- }
- }
-
- public static class NoEmailException extends Exception {
- static final long serialVersionUID = 0xf812773344L;
-
- public NoEmailException(String message) {
- super(message);
- }
- }
-
- public static class InvalidEmailException extends Exception {
- static final long serialVersionUID = 0xf812773345L;
-
- public InvalidEmailException(String message) {
- super(message);
- }
- }
-
- public UserIdEditor(Context context) {
- super(context);
- }
-
- public UserIdEditor(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onFinishInflate() {
- setDrawingCacheEnabled(true);
- setAlwaysDrawnWithCacheEnabled(true);
-
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
- mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
- mIsMainUserId.setOnClickListener(this);
-
- mName = (EditText) findViewById(R.id.name);
- mEmail = (EditText) findViewById(R.id.email);
- mComment = (EditText) findViewById(R.id.comment);
-
- super.onFinishInflate();
- }
-
- public void setValue(String userId) {
- mName.setText("");
- mComment.setText("");
- mEmail.setText("");
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mComment.setText(matcher.group(2));
- mEmail.setText(matcher.group(3));
- return;
- }
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mEmail.setText(matcher.group(2));
- return;
- }
- }
-
- public String getValue() throws NoNameException, NoEmailException, InvalidEmailException {
- String name = ("" + mName.getText()).trim();
- String email = ("" + mEmail.getText()).trim();
- String comment = ("" + mComment.getText()).trim();
-
- if (email.length() > 0) {
- Matcher emailMatcher = EMAIL_PATTERN.matcher(email);
- if (!emailMatcher.matches()) {
- throw new InvalidEmailException(
- getContext().getString(R.string.error_invalidEmail, email));
- }
- }
-
- String userId = name;
- if (comment.length() > 0) {
- userId += " (" + comment + ")";
- }
- if (email.length() > 0) {
- userId += " <" + email + ">";
- }
-
- if (userId.equals("")) {
- // ok, empty one...
- return userId;
- }
-
- // otherwise make sure that name and email exist
- if (name.equals("")) {
- throw new NoNameException("need a name");
- }
-
- if (email.equals("")) {
- throw new NoEmailException("need an email");
- }
-
- return userId;
- }
-
- public void onClick(View v) {
- final ViewGroup parent = (ViewGroup)getParent();
- if (v == mDeleteButton) {
- boolean wasMainUserId = mIsMainUserId.isChecked();
- parent.removeView(this);
- if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
- }
- if (wasMainUserId && parent.getChildCount() > 0) {
- UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
- editor.setIsMainUserId(true);
- }
- } else if (v == mIsMainUserId) {
- for (int i = 0; i < parent.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) parent.getChildAt(i);
- if (editor == this) {
- editor.setIsMainUserId(true);
- } else {
- editor.setIsMainUserId(false);
- }
- }
- }
- }
-
- public void setIsMainUserId(boolean value) {
- mIsMainUserId.setChecked(value);
- }
-
- public boolean isMainUserId() {
- return mIsMainUserId.isChecked();
- }
-
- public void setEditorListener(EditorListener listener) {
- mEditorListener = listener;
- }
-}
diff --git a/src/org/apg/util/ApgCon.java b/src/org/apg/util/ApgCon.java
deleted file mode 100644
index 73ee66ad9..000000000
--- a/src/org/apg/util/ApgCon.java
+++ /dev/null
@@ -1,836 +0,0 @@
-/*
- * Copyright (C) 2011 Markus Doits
- *
- * 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.apg.util;
-
-import org.apg.util.ApgConInterface.OnCallFinishListener;
-import org.apg.IApgService;
-
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-
-/**
- * A APG-AIDL-Wrapper
- *
- *
- * This class can be used by other projects to simplify connecting to the
- * APG-AIDL-Service. Kind of wrapper of for AIDL.
- *
- *
- *
- * It is not used in this project.
- *
- *
- * @author Markus Doits
- * @version 1.1rc1
- *
- */
-public class ApgCon {
- private static final boolean LOCAL_LOGV = true;
- private static final boolean LOCAL_LOGD = true;
-
- private final static String TAG = "ApgCon";
- private final static int API_VERSION = 2; // aidl api-version it expects
- private final static String BLOB_URI = "content://org.thialfihar.android.apg.provider.apgserviceblobprovider";
-
- /**
- * How many seconds to wait for a connection to AGP when connecting.
- * Being unsuccessful for this number of seconds, a connection
- * is assumed to be failed.
- */
- public int secondsToWaitForConnection = 15;
-
- private class CallAsync extends AsyncTask {
-
- @Override
- protected Void doInBackground(String... arg) {
- if( LOCAL_LOGD ) Log.d(TAG, "Async execution starting");
- call(arg[0]);
- return null;
- }
-
- protected void onPostExecute(Void res) {
- if( LOCAL_LOGD ) Log.d(TAG, "Async execution finished");
- mAsyncRunning = false;
-
- }
-
- }
-
- private final Context mContext;
- private final error mConnectionStatus;
- private boolean mAsyncRunning = false;
- private OnCallFinishListener mOnCallFinishListener;
-
- private final Bundle mResult = new Bundle();
- private final Bundle mArgs = new Bundle();
- private final ArrayList mErrorList = new ArrayList();
- private final ArrayList mWarningList = new ArrayList();
-
- /** Remote service for decrypting and encrypting data */
- private IApgService mApgService = null;
-
- /** Set apgService accordingly to connection status */
- private ServiceConnection mApgConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if( LOCAL_LOGD ) Log.d(TAG, "IApgService bound to apgService");
- mApgService = IApgService.Stub.asInterface(service);
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if( LOCAL_LOGD ) Log.d(TAG, "IApgService disconnected");
- mApgService = null;
- }
- };
-
- /**
- * Different types of local errors
- */
- public static enum error {
- /**
- * no error
- */
- NO_ERROR,
- /**
- * generic error
- */
- GENERIC,
- /**
- * connection to apg service not possible
- */
- CANNOT_BIND_TO_APG,
- /**
- * function to call not provided
- */
- CALL_MISSING,
- /**
- * apg service does not know what to do
- */
- CALL_NOT_KNOWN,
- /**
- * could not find APG being installed
- */
- APG_NOT_FOUND,
- /**
- * found APG but without AIDL interface
- */
- APG_AIDL_MISSING,
- /**
- * found APG but with wrong API
- */
- APG_API_MISSMATCH
- }
-
- private static enum ret {
- ERROR, // returned from AIDL
- RESULT, // returned from AIDL
- WARNINGS, // mixed AIDL and LOCAL
- ERRORS, // mixed AIDL and LOCAL
- }
-
- /**
- * Constructor
- *
- *
- * Creates a new ApgCon object and searches for the right APG version on
- * initialization. If not found, errors are printed to the error log.
- *
- *
- * @param ctx
- * the running context
- */
- public ApgCon(Context ctx) {
- if( LOCAL_LOGV ) Log.v(TAG, "EncryptionService created");
- mContext = ctx;
-
- error tmpError = null;
- try {
- if( LOCAL_LOGV ) Log.v(TAG, "Searching for the right APG version");
- ServiceInfo apgServices[] = ctx.getPackageManager().getPackageInfo("org.thialfihar.android.apg",
- PackageManager.GET_SERVICES | PackageManager.GET_META_DATA).services;
- if (apgServices == null) {
- Log.e(TAG, "Could not fetch services");
- tmpError = error.GENERIC;
- } else {
- boolean apgServiceFound = false;
- for (ServiceInfo inf : apgServices) {
- if( LOCAL_LOGV ) Log.v(TAG, "Found service of APG: " + inf.name);
- if (inf.name.equals("org.thialfihar.android.apg.ApgService")) {
- apgServiceFound = true;
- if (inf.metaData == null) {
- Log.w(TAG, "Could not determine ApgService API");
- Log.w(TAG, "This probably won't work!");
- mWarningList.add("(LOCAL) Could not determine ApgService API");
- tmpError = error.APG_API_MISSMATCH;
- } else if (inf.metaData.getInt("api_version") != API_VERSION) {
- Log.w(TAG, "Found ApgService API version " + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
- Log.w(TAG, "This probably won't work!");
- mWarningList.add("(LOCAL) Found ApgService API version " + inf.metaData.getInt("api_version") + " but exspected " + API_VERSION);
- tmpError = error.APG_API_MISSMATCH;
- } else {
- if( LOCAL_LOGV ) Log.v(TAG, "Found api_version " + API_VERSION + ", everything should work");
- tmpError = error.NO_ERROR;
- }
- }
- }
-
- if (!apgServiceFound) {
- Log.e(TAG, "Could not find APG with AIDL interface, this probably won't work");
- mErrorList.add("(LOCAL) Could not find APG with AIDL interface, this probably won't work");
- mResult.putInt(ret.ERROR.name(), error.APG_AIDL_MISSING.ordinal());
- tmpError = error.APG_NOT_FOUND;
- }
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Could not find APG, is it installed?", e);
- mErrorList.add("(LOCAL) Could not find APG, is it installed?");
- mResult.putInt(ret.ERROR.name(), error.APG_NOT_FOUND.ordinal());
- tmpError = error.APG_NOT_FOUND;
- }
-
- mConnectionStatus = tmpError;
-
- }
-
- /** try to connect to the apg service */
- private boolean connect() {
- if( LOCAL_LOGV ) Log.v(TAG, "trying to bind the apgService to context");
-
- if (mApgService != null) {
- if( LOCAL_LOGV ) Log.v(TAG, "allready connected");
- return true;
- }
-
- try {
- mContext.bindService(new Intent(IApgService.class.getName()), mApgConnection, Context.BIND_AUTO_CREATE);
- } catch (Exception e) {
- Log.e(TAG, "could not bind APG service", e);
- return false;
- }
-
- int waitCount = 0;
- while (mApgService == null && waitCount++ < secondsToWaitForConnection) {
- if( LOCAL_LOGV ) Log.v(TAG, "sleeping 1 second to wait for apg");
- android.os.SystemClock.sleep(1000);
- }
-
- if (waitCount >= secondsToWaitForConnection) {
- if( LOCAL_LOGV ) Log.v(TAG, "slept waiting for nothing!");
- return false;
- }
-
- return true;
- }
-
- /**
- * Disconnects ApgCon from Apg
- *
- *
- * This should be called whenever all work with APG is done (e.g. everything
- * you wanted to encrypt is encrypted), since connections with AIDL should
- * not be upheld indefinitely.
- *
- *
- *
- * Also, if you destroy you end using your ApgCon-instance, this must be
- * called or else the connection to APG is leaked
- *
- */
- public void disconnect() {
- if( LOCAL_LOGV ) Log.v(TAG, "disconnecting apgService");
- if (mApgService != null) {
- mContext.unbindService(mApgConnection);
- mApgService = null;
- }
- }
-
- private boolean initialize() {
- if (mApgService == null) {
- if (!connect()) {
- if( LOCAL_LOGV ) Log.v(TAG, "connection to apg service failed");
- return false;
- }
- }
- return true;
- }
-
- /**
- * Calls a function from APG's AIDL-interface
- *
- *
- * After you have set up everything with {@link #setArg(String, String)}
- * (and variants), you can call a function of the AIDL-interface. This
- * will:
- *
- *
start connection to the remote interface (if not already connected)
- *
call the function passed with all parameters synchronously
- *
set up everything to retrieve the result and/or warnings/errors
- *
call the callback if provided
- *
- *
- *
- *
- * Note your thread will be blocked during execution - if you want to call
- * the function asynchronously, see {@link #callAsync(String)}.
- *
- *
- * @param function
- * a remote function to call
- * @return true, if call successful (= no errors), else false
- *
- * @see #callAsync(String)
- * @see #setArg(String, String)
- * @see #setOnCallFinishListener(OnCallFinishListener)
- */
- public boolean call(String function) {
- boolean success = this.call(function, mArgs, mResult);
- if (mOnCallFinishListener != null) {
- try {
- if( LOCAL_LOGD ) Log.d(TAG, "About to execute callback");
- mOnCallFinishListener.onCallFinish(mResult);
- if( LOCAL_LOGD ) Log.d(TAG, "Callback executed");
- } catch (Exception e) {
- Log.w(TAG, "Exception on callback: (" + e.getClass() + ") " + e.getMessage(), e);
- mWarningList.add("(LOCAL) Could not execute callback (" + e.getClass() + "): " + e.getMessage());
- }
- }
- return success;
- }
-
- /**
- * Calls a function of remote interface asynchronously
- *
- *
- * This does exactly the same as {@link #call(String)}, but asynchronously.
- * While connection to APG and work are done in background, your thread can
- * go on executing.
- *
- *
- *
- * To see whether the task is finished, you have two possibilities:
- *
- *
In your thread, poll {@link #isRunning()}
- *
Supply a callback with {@link #setOnCallFinishListener(OnCallFinishListener)}
- *
- *
- *
- * @param function
- * a remote function to call
- *
- * @see #call(String)
- * @see #isRunning()
- * @see #setOnCallFinishListener(OnCallFinishListener)
- */
- public void callAsync(String function) {
- mAsyncRunning = true;
- new CallAsync().execute(function);
- }
-
- private boolean call(String function, Bundle pArgs, Bundle pReturn) {
-
- if (!initialize()) {
- mErrorList.add("(LOCAL) Cannot bind to ApgService");
- mResult.putInt(ret.ERROR.name(), error.CANNOT_BIND_TO_APG.ordinal());
- return false;
- }
-
- if (function == null || function.length() == 0) {
- mErrorList.add("(LOCAL) Function to call missing");
- mResult.putInt(ret.ERROR.name(), error.CALL_MISSING.ordinal());
- return false;
- }
-
- try {
- Boolean success = (Boolean) IApgService.class.getMethod(function, Bundle.class, Bundle.class).invoke(mApgService, pArgs, pReturn);
- mErrorList.addAll(pReturn.getStringArrayList(ret.ERRORS.name()));
- mWarningList.addAll(pReturn.getStringArrayList(ret.WARNINGS.name()));
- return success;
- } catch (NoSuchMethodException e) {
- Log.e(TAG, "Remote call not known (" + function + "): " + e.getMessage(), e);
- mErrorList.add("(LOCAL) Remote call not known (" + function + "): " + e.getMessage());
- mResult.putInt(ret.ERROR.name(), error.CALL_NOT_KNOWN.ordinal());
- return false;
- } catch (InvocationTargetException e) {
- Throwable orig = e.getTargetException();
- Log.w(TAG, "Exception of type '" + orig.getClass() + "' on AIDL call '" + function + "': " + orig.getMessage(), orig);
- mErrorList.add("(LOCAL) Exception of type '" + orig.getClass() + "' on AIDL call '" + function + "': " + orig.getMessage());
- return false;
- } catch (Exception e) {
- Log.e(TAG, "Generic error (" + e.getClass() + "): " + e.getMessage(), e);
- mErrorList.add("(LOCAL) Generic error (" + e.getClass() + "): " + e.getMessage());
- mResult.putInt(ret.ERROR.name(), error.GENERIC.ordinal());
- return false;
- }
-
- }
-
- /**
- * Set a string argument for APG
- *
- *
- * This defines a string argument for APG's AIDL-interface.
- *
- *
- *
- * To know what key-value-pairs are possible (or required), take a look into
- * the IApgService.aidl
- *
- *
- *
- * Note that parameters are not reseted after a call, so you have to
- * reset ({@link #clearArgs()}) them manually if you want to.
- *
- *
- *
- * @param key
- * the key
- * @param val
- * the value
- *
- * @see #clearArgs()
- */
- public void setArg(String key, String val) {
- mArgs.putString(key, val);
- }
-
- /**
- * Set a string-array argument for APG
- *
- *
- * If the AIDL-parameter is an {@literal ArrayList}, you have to use
- * this function.
- *
- *
- *
- * @param key
- * the key
- * @param vals
- * the value
- *
- * @see #setArg(String, String)
- */
- public void setArg(String key, String vals[]) {
- ArrayList list = new ArrayList();
- for (String val : vals) {
- list.add(val);
- }
- mArgs.putStringArrayList(key, list);
- }
-
- /**
- * Set up a boolean argument for APG
- *
- * @param key
- * the key
- * @param vals
- * the value
- *
- * @see #setArg(String, String)
- */
- public void setArg(String key, boolean val) {
- mArgs.putBoolean(key, val);
- }
-
- /**
- * Set up a int argument for APG
- *
- * @param key
- * the key
- * @param vals
- * the value
- *
- * @see #setArg(String, String)
- */
- public void setArg(String key, int val) {
- mArgs.putInt(key, val);
- }
-
- /**
- * Set up a int-array argument for APG
- *
- * If the AIDL-parameter is an {@literal ArrayList}, you have to
- * use this function.
- *
- *
- * @param key
- * the key
- * @param vals
- * the value
- *
- * @see #setArg(String, String)
- */
- public void setArg(String key, int vals[]) {
- ArrayList list = new ArrayList();
- for (int val : vals) {
- list.add(val);
- }
- mArgs.putIntegerArrayList(key, list);
- }
-
- /**
- * Set up binary data to en/decrypt
- *
- * @param is
- * InputStream to get the data from
- */
- public void setBlob(InputStream is) {
- if( LOCAL_LOGD ) Log.d(TAG, "setBlob() called");
- // 1. get the new contentUri
- ContentResolver cr = mContext.getContentResolver();
- Uri contentUri = cr.insert(Uri.parse(BLOB_URI), new ContentValues());
-
- // 2. insert binary data
- OutputStream os = null;
- try {
- os = cr.openOutputStream(contentUri, "w");
- } catch( Exception e ) {
- Log.e(TAG, "... exception on setBlob", e);
- }
-
- byte[] buffer = new byte[8];
- int len = 0;
- try {
- while( (len = is.read(buffer)) != -1) {
- os.write(buffer, 0, len);
- }
- if(LOCAL_LOGD) Log.d(TAG, "... write finished, now closing");
- os.close();
- } catch (Exception e) {
- Log.e(TAG, "... error on writing buffer", e);
- }
-
- mArgs.putString("BLOB", contentUri.toString() );
- }
-
- /**
- * Clears all arguments
- *
- *
- * Anything the has been set up with the various
- * {@link #setArg(String, String)} functions is cleared.
- *
- *
- *
- * Note that any warning, error, callback, result, etc. is NOT cleared with
- * this.
- *
- *
- * @see #reset()
- */
- public void clearArgs() {
- mArgs.clear();
- }
-
- /**
- * Return the object associated with the key
- *
- * @param key
- * the object's key you want to return
- * @return an object at position key, or null if not set
- */
- public Object getArg(String key) {
- return mArgs.get(key);
- }
-
- /**
- * Iterates through the errors
- *
- *
- * With this method you can iterate through all errors. The errors are only
- * returned once and deleted immediately afterwards, so you can only return
- * each error once.
- *
- *
- * @return a human readable description of a error that happened, or null if
- * no more errors
- *
- * @see #hasNextError()
- * @see #clearErrors()
- */
- public String getNextError() {
- if (mErrorList.size() != 0)
- return mErrorList.remove(0);
- else
- return null;
- }
-
- /**
- * Check if there are any new errors
- *
- * @return true, if there are unreturned errors, false otherwise
- *
- * @see #getNextError()
- */
- public boolean hasNextError() {
- return mErrorList.size() != 0;
- }
-
- /**
- * Get the numeric representation of the last error
- *
- *
- * Values <100 mean the error happened locally, values >=100 mean the error
- * happened at the remote side (APG). See the IApgService.aidl (or get the
- * human readable description with {@link #getNextError()}) for what
- * errors >=100 mean.
- *
- *
- * @return the id of the error that happened
- */
- public int getError() {
- if (mResult.containsKey(ret.ERROR.name()))
- return mResult.getInt(ret.ERROR.name());
- else
- return -1;
- }
-
- /**
- * Iterates through the warnings
- *
- *
- * With this method you can iterate through all warnings. Warnings are
- * only returned once and deleted immediately afterwards, so you can only
- * return each warning once.
- *
- *
- * @return a human readable description of a warning that happened, or null
- * if no more warnings
- *
- * @see #hasNextWarning()
- * @see #clearWarnings()
- */
- public String getNextWarning() {
- if (mWarningList.size() != 0)
- return mWarningList.remove(0);
- else
- return null;
- }
-
- /**
- * Check if there are any new warnings
- *
- * @return true, if there are unreturned warnings, false otherwise
- *
- * @see #getNextWarning()
- */
- public boolean hasNextWarning() {
- return mWarningList.size() != 0;
- }
-
- /**
- * Get the result
- *
- *
- * This gets your result. After doing an encryption or decryption with APG,
- * you get the output with this function.
- *
- *
- *
- * Note when your last remote call is unsuccessful, the result will
- * still have the same value like the last successful call (or null, if no
- * call was successful). To ensure you do not work with old call's results,
- * either be sure to {@link #reset()} (or at least {@link #clearResult()})
- * your instance before each new call or always check that
- * {@link #hasNextError()} is false.
- *
- *
- *
- * Note: When handling binary data with {@link #setBlob(InputStream)}, you
- * get your result with {@link #getBlobResult()}.
- *
- *
- * @return the mResult of the last {@link #call(String)} or
- * {@link #callAsync(String)}.
- *
- * @see #reset()
- * @see #clearResult()
- * @see #getResultBundle()
- * @see #getBlobResult()
- */
- public String getResult() {
- return mResult.getString(ret.RESULT.name());
- }
-
- /**
- * Get the binary result
- *
- *
- * This gets your binary result. It only works if you called {@link #setBlob(InputStream)} before.
- *
- * If you did not call encrypt nor decrypt, this will be the same data as you inputed.
- *
- *
- * @return InputStream of the binary data which was en/decrypted
- *
- * @see #setBlob(InputStream)
- * @see #getResult()
- */
- public InputStream getBlobResult() {
- if(mArgs.containsKey("BLOB")) {
- ContentResolver cr = mContext.getContentResolver();
- InputStream in = null;
- try {
- in = cr.openInputStream(Uri.parse(mArgs.getString("BLOB")));
- } catch( Exception e ) {
- Log.e(TAG, "Could not return blob in result", e);
- }
- return in;
- } else {
- return null;
- }
- }
-
- /**
- * Get the result bundle
- *
- *
- * Unlike {@link #getResult()}, which only returns any en-/decrypted
- * message, this function returns the complete information that was returned
- * by Apg. This also includes the "RESULT", but additionally the warnings,
- * errors and any other information.
- *
- *
- * For warnings and errors it is suggested to use the functions that are
- * provided here, namely {@link #getError()}, {@link #getNextError()},
- * {@link #get_next_Warning()} etc.), but if any call returns something non
- * standard, you have access to the complete result bundle to extract the
- * information.
- *
- *
- * @return the complete result bundle of the last call to apg
- */
- public Bundle getResultBundle() {
- return mResult;
- }
-
- public error getConnectionStatus() {
- return mConnectionStatus;
- }
-
- /**
- * Clears all unfetched errors
- *
- * @see #getNextError()
- * @see #hasNextError()
- */
- public void clearErrors() {
- mErrorList.clear();
- mResult.remove(ret.ERROR.name());
- }
-
- /**
- * Clears all unfetched warnings
- *
- * @see #getNextWarning()
- * @see #hasNextWarning()
- */
- public void clearWarnings() {
- mWarningList.clear();
- }
-
- /**
- * Clears the last mResult
- *
- * @see #getResult()
- */
- public void clearResult() {
- mResult.remove(ret.RESULT.name());
- }
-
- /**
- * Set a callback listener when call to AIDL finishes
- *
- * @param obj
- * a object to call back after async execution
- * @see ApgConInterface
- */
- public void setOnCallFinishListener(OnCallFinishListener lis) {
- mOnCallFinishListener = lis;
- }
-
- /**
- * Clears any callback object
- *
- * @see #setOnCallFinishListener(OnCallFinishListener)
- */
- public void clearOnCallFinishListener() {
- mOnCallFinishListener = null;
- }
-
- /**
- * Checks if an async execution is running
- *
- *
- * If you started something with {@link #callAsync(String)}, this will
- * return true if the task is still running
- *
- *
- * @return true, if an async task is still running, false otherwise
- *
- * @see #callAsync(String)
- *
- */
- public boolean isRunning() {
- return mAsyncRunning;
- }
-
- /**
- * Completely resets your instance
- *
- *
- * This currently resets everything in this instance. Errors, warnings,
- * results, callbacks, ... are removed. Any connection to the remote
- * interface is upheld, though.
- *
- *
- *
- * Note when an async execution ({@link #callAsync(String)}) is
- * running, it's result, warnings etc. will still be evaluated (which might
- * be not what you want). Also mind that any callback you set is also
- * reseted, so when finishing the execution any before defined callback will
- * NOT BE TRIGGERED.
- *
- */
- public void reset() {
- clearErrors();
- clearWarnings();
- clearArgs();
- clearOnCallFinishListener();
- mResult.clear();
- }
-
-}
diff --git a/src/org/apg/util/ApgConInterface.java b/src/org/apg/util/ApgConInterface.java
deleted file mode 100644
index 2b66164f1..000000000
--- a/src/org/apg/util/ApgConInterface.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.apg.util;
-
-public interface ApgConInterface {
- public static interface OnCallFinishListener {
- public abstract void onCallFinish(android.os.Bundle result);
- }
-}
diff --git a/src/org/apg/util/Choice.java b/src/org/apg/util/Choice.java
deleted file mode 100644
index 1dbd9b215..000000000
--- a/src/org/apg/util/Choice.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.util;
-
-public class Choice {
- private String mName;
- private int mId;
-
- public Choice() {
- mId = -1;
- mName = "";
- }
-
- public Choice(int id, String name) {
- mId = id;
- mName = name;
- }
-
- public int getId() {
- return mId;
- }
-
- public String getName() {
- return mName;
- }
-
- @Override
- public String toString() {
- return mName;
- }
-}
diff --git a/src/org/apg/util/Compatibility.java b/src/org/apg/util/Compatibility.java
deleted file mode 100644
index 10b3c7f6b..000000000
--- a/src/org/apg/util/Compatibility.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.apg.util;
-
-import java.lang.reflect.Method;
-
-import android.content.Context;
-import android.util.Log;
-
-public class Compatibility {
-
- private static final String clipboardLabel = "APG";
-
- /**
- * Wrapper around ClipboardManager based on Android version using Reflection API, from
- * http://www.projectsexception.com/blog/?p=87
- *
- * @param context
- * @param text
- */
- public static void copyToClipboard(Context context, String text) {
- Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
- try {
- if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) {
- Method method = clipboard.getClass().getMethod("setText", CharSequence.class);
- method.invoke(clipboard, text);
- } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) {
- Class> clazz = Class.forName("android.content.ClipData");
- Method method = clazz.getMethod("newPlainText", CharSequence.class,
- CharSequence.class);
- Object clip = method.invoke(null, clipboardLabel, text);
- method = clipboard.getClass().getMethod("setPrimaryClip", clazz);
- method.invoke(clipboard, clip);
- }
- } catch (Exception e) {
- Log.e("ProjectsException", "There was and error copying the text to the clipboard: "
- + e.getMessage());
- }
- }
-
- /**
- * Wrapper around ClipboardManager based on Android version using Reflection API
- *
- * @param context
- * @param text
- */
- public static CharSequence getClipboardText(Context context) {
- Object clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE);
- try {
- if ("android.text.ClipboardManager".equals(clipboard.getClass().getName())) {
- // CharSequence text = clipboard.getText();
- Method method = clipboard.getClass().getMethod("getText");
- Object text = method.invoke(clipboard);
-
- return (CharSequence) text;
- } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) {
- // ClipData clipData = clipboard.getPrimaryClip();
- Method methodGetPrimaryClip = clipboard.getClass().getMethod("getPrimaryClip");
- Object clipData = methodGetPrimaryClip.invoke(clipboard);
-
- // ClipData.Item clipDataItem = clipData.getItemAt(0);
- Method methodGetItemAt = clipData.getClass().getMethod("getItemAt", Integer.TYPE);
- Object clipDataItem = methodGetItemAt.invoke(clipData, 0);
-
- // CharSequence text = clipDataItem.coerceToText(context);
- Method methodGetString = clipDataItem.getClass().getMethod("coerceToText",
- Context.class);
- Object text = methodGetString.invoke(clipDataItem, context);
-
- return (CharSequence) text;
- } else {
- return null;
- }
- } catch (Exception e) {
- Log.e("ProjectsException", "There was and error getting the text from the clipboard: "
- + e.getMessage());
-
- return null;
- }
- }
-}
diff --git a/src/org/apg/util/IterableIterator.java b/src/org/apg/util/IterableIterator.java
deleted file mode 100644
index be6632ac0..000000000
--- a/src/org/apg/util/IterableIterator.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2010 Thialfihar
- *
- * 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.apg.util;
-
-import java.util.Iterator;
-
-public class IterableIterator implements Iterable {
- private Iterator mIter;
-
- public IterableIterator(Iterator iter) {
- mIter = iter;
- }
-
- public Iterator iterator() {
- return mIter;
- }
-}
--
cgit v1.2.3