From 1feb948acf81532f82b36456080920543004b097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 16 Jan 2013 14:31:16 +0100 Subject: Renaming APG to OpenPGP Keychain --- APG-API-Demo/.gitignore | 23 - APG-API-Demo/AndroidManifest.xml | 44 - APG-API-Demo/build.xml | 83 - APG-API-Demo/proguard-project.txt | 20 - APG-API-Demo/project.properties | 12 - APG-API-Demo/res/drawable-hdpi/icon.png | Bin 5308 -> 0 bytes APG-API-Demo/res/drawable-ldpi/icon.png | Bin 2027 -> 0 bytes APG-API-Demo/res/drawable-mdpi/icon.png | Bin 3035 -> 0 bytes APG-API-Demo/res/drawable-xhdpi/icon.png | Bin 8165 -> 0 bytes APG-API-Demo/res/layout/aidl_demo.xml | 72 - APG-API-Demo/res/layout/aidl_demo2.xml | 40 - APG-API-Demo/res/layout/content_provider_demo.xml | 33 - APG-API-Demo/res/layout/intent_demo.xml | 93 - APG-API-Demo/res/xml/base_preference.xml | 23 - .../android/apg/demo/AidlDemoActivity.java | 213 --- .../android/apg/demo/AidlDemoActivity2.java | 165 -- .../thialfihar/android/apg/demo/BaseActivity.java | 92 - .../apg/demo/ContentProviderDemoActivity.java | 68 - .../android/apg/demo/IntentDemoActivity.java | 110 -- APG-API-Lib/.gitignore | 23 - APG-API-Lib/AndroidManifest.xml | 11 - APG-API-Lib/build.xml | 83 - APG-API-Lib/libs/android-support-v4.jar | Bin 271754 -> 0 bytes APG-API-Lib/proguard-project.txt | 20 - APG-API-Lib/project.properties | 15 - APG-API-Lib/res/.readme | 1 - .../apg/integration/ApgContentProviderHelper.java | 266 --- .../android/apg/integration/ApgData.java | 116 -- .../android/apg/integration/ApgIntentHelper.java | 451 ----- .../apg/integration/ApgIntentHelperSupportV4.java | 43 - .../apg/integration/ApgIntentHelperV30.java | 46 - .../android/apg/integration/ApgServiceHelper.java | 102 -- .../android/apg/integration/ApgUtil.java | 72 - .../android/apg/integration/Constants.java | 27 - .../android/apg/service/IApgApiService.aidl | 146 -- .../android/apg/service/IApgKeyService.aidl | 32 - .../apg/service/handler/IApgDecryptHandler.aidl | 27 - .../apg/service/handler/IApgEncryptHandler.aidl | 28 - .../handler/IApgGetDecryptionKeyIdHandler.aidl | 25 - .../service/handler/IApgGetKeyringsHandler.aidl | 28 - .../apg/service/handler/IApgSignHandler.aidl | 28 - .../apg/service/handler/IApgVerifyHandler.aidl | 26 - APG/.gitignore | 24 - APG/AndroidManifest.xml | 447 ----- APG/android-libs/ActionBarSherlock/.gitignore | 23 - .../ActionBarSherlock/AndroidManifest.xml | 6 - APG/android-libs/ActionBarSherlock/README.md | 15 - APG/android-libs/ActionBarSherlock/build.xml | 92 - .../ActionBarSherlock/libs/android-support-v4.jar | Bin 271754 -> 0 bytes APG/android-libs/ActionBarSherlock/pom.xml | 148 -- .../ActionBarSherlock/proguard-project.txt | 20 - .../ActionBarSherlock/project.properties | 12 - .../abs__primary_text_disable_only_holo_dark.xml | 20 - .../abs__primary_text_disable_only_holo_light.xml | 21 - .../res/color/abs__primary_text_holo_dark.xml | 24 - .../res/color/abs__primary_text_holo_light.xml | 26 - .../abs__ab_bottom_solid_dark_holo.9.png | Bin 144 -> 0 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 138 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 144 -> 0 bytes .../abs__ab_bottom_transparent_dark_holo.9.png | Bin 135 -> 0 bytes .../abs__ab_bottom_transparent_light_holo.9.png | Bin 134 -> 0 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 2863 -> 0 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 2859 -> 0 bytes .../drawable-hdpi/abs__ab_solid_dark_holo.9.png | Bin 146 -> 0 bytes .../drawable-hdpi/abs__ab_solid_light_holo.9.png | Bin 145 -> 0 bytes .../drawable-hdpi/abs__ab_solid_shadow_holo.9.png | Bin 192 -> 0 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 146 -> 0 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 146 -> 0 bytes .../abs__ab_stacked_transparent_dark_holo.9.png | Bin 139 -> 0 bytes .../abs__ab_stacked_transparent_light_holo.9.png | Bin 133 -> 0 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 155 -> 0 bytes .../abs__ab_transparent_light_holo.9.png | Bin 145 -> 0 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 104 -> 0 bytes .../abs__btn_cab_done_default_holo_light.9.png | Bin 102 -> 0 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 112 -> 0 bytes .../abs__btn_cab_done_focused_holo_light.9.png | Bin 108 -> 0 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 110 -> 0 bytes .../abs__btn_cab_done_pressed_holo_light.9.png | Bin 108 -> 0 bytes .../abs__cab_background_bottom_holo_dark.9.png | Bin 149 -> 0 bytes .../abs__cab_background_bottom_holo_light.9.png | Bin 145 -> 0 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 147 -> 0 bytes .../abs__cab_background_top_holo_light.9.png | Bin 147 -> 0 bytes .../drawable-hdpi/abs__dialog_full_holo_dark.9.png | Bin 1414 -> 0 bytes .../abs__dialog_full_holo_light.9.png | Bin 1537 -> 0 bytes .../drawable-hdpi/abs__ic_ab_back_holo_dark.png | Bin 602 -> 0 bytes .../drawable-hdpi/abs__ic_ab_back_holo_light.png | Bin 546 -> 0 bytes .../drawable-hdpi/abs__ic_cab_done_holo_dark.png | Bin 713 -> 0 bytes .../drawable-hdpi/abs__ic_cab_done_holo_light.png | Bin 737 -> 0 bytes .../res/drawable-hdpi/abs__ic_clear_disabled.png | Bin 1774 -> 0 bytes .../res/drawable-hdpi/abs__ic_clear_normal.png | Bin 1945 -> 0 bytes ...bs__ic_clear_search_api_disabled_holo_light.png | Bin 1504 -> 0 bytes .../abs__ic_clear_search_api_holo_light.png | Bin 1540 -> 0 bytes .../res/drawable-hdpi/abs__ic_go.png | Bin 1415 -> 0 bytes .../abs__ic_go_search_api_holo_light.png | Bin 1252 -> 0 bytes .../abs__ic_menu_moreoverflow_normal_holo_dark.png | Bin 144 -> 0 bytes ...abs__ic_menu_moreoverflow_normal_holo_light.png | Bin 148 -> 0 bytes .../drawable-hdpi/abs__ic_menu_share_holo_dark.png | Bin 467 -> 0 bytes .../abs__ic_menu_share_holo_light.png | Bin 505 -> 0 bytes .../res/drawable-hdpi/abs__ic_search.png | Bin 2280 -> 0 bytes .../abs__ic_search_api_holo_light.png | Bin 2271 -> 0 bytes .../res/drawable-hdpi/abs__ic_voice_search.png | Bin 2070 -> 0 bytes .../abs__ic_voice_search_api_holo_light.png | Bin 1833 -> 0 bytes .../drawable-hdpi/abs__list_activated_holo.9.png | Bin 154 -> 0 bytes .../abs__list_divider_holo_dark.9.png | Bin 78 -> 0 bytes .../abs__list_divider_holo_light.9.png | Bin 76 -> 0 bytes .../res/drawable-hdpi/abs__list_focused_holo.9.png | Bin 159 -> 0 bytes .../drawable-hdpi/abs__list_longpressed_holo.9.png | Bin 154 -> 0 bytes .../abs__list_pressed_holo_dark.9.png | Bin 159 -> 0 bytes .../abs__list_pressed_holo_light.9.png | Bin 159 -> 0 bytes .../abs__list_selector_disabled_holo_dark.9.png | Bin 189 -> 0 bytes .../abs__list_selector_disabled_holo_light.9.png | Bin 189 -> 0 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 922 -> 0 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 1061 -> 0 bytes .../drawable-hdpi/abs__progress_bg_holo_dark.9.png | Bin 178 -> 0 bytes .../abs__progress_bg_holo_light.9.png | Bin 174 -> 0 bytes .../abs__progress_primary_holo_dark.9.png | Bin 917 -> 0 bytes .../abs__progress_primary_holo_light.9.png | Bin 917 -> 0 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 188 -> 0 bytes .../abs__progress_secondary_holo_light.9.png | Bin 188 -> 0 bytes .../drawable-hdpi/abs__spinner_48_inner_holo.png | Bin 2081 -> 0 bytes .../drawable-hdpi/abs__spinner_48_outer_holo.png | Bin 1811 -> 0 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 311 -> 0 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 312 -> 0 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 306 -> 0 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 306 -> 0 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 524 -> 0 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 523 -> 0 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 464 -> 0 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 458 -> 0 bytes .../abs__tab_selected_focused_holo.9.png | Bin 147 -> 0 bytes .../res/drawable-hdpi/abs__tab_selected_holo.9.png | Bin 148 -> 0 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 147 -> 0 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 145 -> 0 bytes .../abs__textfield_search_default_holo_dark.9.png | Bin 110 -> 0 bytes .../abs__textfield_search_default_holo_light.9.png | Bin 105 -> 0 bytes ..._textfield_search_right_default_holo_dark.9.png | Bin 108 -> 0 bytes ...textfield_search_right_default_holo_light.9.png | Bin 103 -> 0 bytes ...textfield_search_right_selected_holo_dark.9.png | Bin 114 -> 0 bytes ...extfield_search_right_selected_holo_light.9.png | Bin 111 -> 0 bytes .../abs__textfield_search_selected_holo_dark.9.png | Bin 114 -> 0 bytes ...abs__textfield_search_selected_holo_light.9.png | Bin 112 -> 0 bytes .../abs__ab_bottom_solid_dark_holo.9.png | Bin 134 -> 0 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 129 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 134 -> 0 bytes .../abs__ab_bottom_transparent_dark_holo.9.png | Bin 123 -> 0 bytes .../abs__ab_bottom_transparent_light_holo.9.png | Bin 123 -> 0 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 2849 -> 0 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 191 -> 0 bytes .../drawable-mdpi/abs__ab_solid_dark_holo.9.png | Bin 133 -> 0 bytes .../drawable-mdpi/abs__ab_solid_light_holo.9.png | Bin 133 -> 0 bytes .../drawable-mdpi/abs__ab_solid_shadow_holo.9.png | Bin 168 -> 0 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 134 -> 0 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 133 -> 0 bytes .../abs__ab_stacked_transparent_dark_holo.9.png | Bin 127 -> 0 bytes .../abs__ab_stacked_transparent_light_holo.9.png | Bin 123 -> 0 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 139 -> 0 bytes .../abs__ab_transparent_light_holo.9.png | Bin 133 -> 0 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 101 -> 0 bytes .../abs__btn_cab_done_default_holo_light.9.png | Bin 99 -> 0 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 109 -> 0 bytes .../abs__btn_cab_done_focused_holo_light.9.png | Bin 105 -> 0 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 107 -> 0 bytes .../abs__btn_cab_done_pressed_holo_light.9.png | Bin 105 -> 0 bytes .../abs__cab_background_bottom_holo_dark.9.png | Bin 127 -> 0 bytes .../abs__cab_background_bottom_holo_light.9.png | Bin 124 -> 0 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 130 -> 0 bytes .../abs__cab_background_top_holo_light.9.png | Bin 128 -> 0 bytes .../drawable-mdpi/abs__dialog_full_holo_dark.9.png | Bin 882 -> 0 bytes .../abs__dialog_full_holo_light.9.png | Bin 1003 -> 0 bytes .../drawable-mdpi/abs__ic_ab_back_holo_dark.png | Bin 466 -> 0 bytes .../drawable-mdpi/abs__ic_ab_back_holo_light.png | Bin 438 -> 0 bytes .../drawable-mdpi/abs__ic_cab_done_holo_dark.png | Bin 566 -> 0 bytes .../drawable-mdpi/abs__ic_cab_done_holo_light.png | Bin 552 -> 0 bytes .../res/drawable-mdpi/abs__ic_clear_disabled.png | Bin 1775 -> 0 bytes .../res/drawable-mdpi/abs__ic_clear_normal.png | Bin 1869 -> 0 bytes ...bs__ic_clear_search_api_disabled_holo_light.png | Bin 740 -> 0 bytes .../abs__ic_clear_search_api_holo_light.png | Bin 743 -> 0 bytes .../res/drawable-mdpi/abs__ic_go.png | Bin 1538 -> 0 bytes .../abs__ic_go_search_api_holo_light.png | Bin 570 -> 0 bytes .../abs__ic_menu_moreoverflow_normal_holo_dark.png | Bin 122 -> 0 bytes ...abs__ic_menu_moreoverflow_normal_holo_light.png | Bin 131 -> 0 bytes .../drawable-mdpi/abs__ic_menu_share_holo_dark.png | Bin 332 -> 0 bytes .../abs__ic_menu_share_holo_light.png | Bin 355 -> 0 bytes .../res/drawable-mdpi/abs__ic_search.png | Bin 2280 -> 0 bytes .../abs__ic_search_api_holo_light.png | Bin 1541 -> 0 bytes .../res/drawable-mdpi/abs__ic_voice_search.png | Bin 1937 -> 0 bytes .../abs__ic_voice_search_api_holo_light.png | Bin 794 -> 0 bytes .../drawable-mdpi/abs__list_activated_holo.9.png | Bin 151 -> 0 bytes .../abs__list_divider_holo_dark.9.png | Bin 78 -> 0 bytes .../abs__list_divider_holo_light.9.png | Bin 76 -> 0 bytes .../res/drawable-mdpi/abs__list_focused_holo.9.png | Bin 158 -> 0 bytes .../drawable-mdpi/abs__list_longpressed_holo.9.png | Bin 151 -> 0 bytes .../abs__list_pressed_holo_dark.9.png | Bin 158 -> 0 bytes .../abs__list_pressed_holo_light.9.png | Bin 158 -> 0 bytes .../abs__list_selector_disabled_holo_dark.9.png | Bin 172 -> 0 bytes .../abs__list_selector_disabled_holo_light.9.png | Bin 171 -> 0 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 651 -> 0 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 720 -> 0 bytes .../drawable-mdpi/abs__progress_bg_holo_dark.9.png | Bin 165 -> 0 bytes .../abs__progress_bg_holo_light.9.png | Bin 159 -> 0 bytes .../abs__progress_primary_holo_dark.9.png | Bin 572 -> 0 bytes .../abs__progress_primary_holo_light.9.png | Bin 572 -> 0 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 170 -> 0 bytes .../abs__progress_secondary_holo_light.9.png | Bin 170 -> 0 bytes .../drawable-mdpi/abs__spinner_48_inner_holo.png | Bin 1336 -> 0 bytes .../drawable-mdpi/abs__spinner_48_outer_holo.png | Bin 1165 -> 0 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 254 -> 0 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 255 -> 0 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 249 -> 0 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 249 -> 0 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 417 -> 0 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 424 -> 0 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 370 -> 0 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 370 -> 0 bytes .../abs__tab_selected_focused_holo.9.png | Bin 148 -> 0 bytes .../res/drawable-mdpi/abs__tab_selected_holo.9.png | Bin 151 -> 0 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 150 -> 0 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 155 -> 0 bytes .../abs__textfield_search_default_holo_dark.9.png | Bin 106 -> 0 bytes .../abs__textfield_search_default_holo_light.9.png | Bin 100 -> 0 bytes ..._textfield_search_right_default_holo_dark.9.png | Bin 105 -> 0 bytes ...textfield_search_right_default_holo_light.9.png | Bin 98 -> 0 bytes ...textfield_search_right_selected_holo_dark.9.png | Bin 107 -> 0 bytes ...extfield_search_right_selected_holo_light.9.png | Bin 107 -> 0 bytes .../abs__textfield_search_selected_holo_dark.9.png | Bin 109 -> 0 bytes ...abs__textfield_search_selected_holo_light.9.png | Bin 109 -> 0 bytes .../res/drawable-v11/abs__progress_medium_holo.xml | 34 - .../abs__ab_bottom_solid_dark_holo.9.png | Bin 165 -> 0 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 157 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 166 -> 0 bytes .../abs__ab_bottom_transparent_dark_holo.9.png | Bin 153 -> 0 bytes .../abs__ab_bottom_transparent_light_holo.9.png | Bin 152 -> 0 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 2878 -> 0 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 2873 -> 0 bytes .../drawable-xhdpi/abs__ab_solid_dark_holo.9.png | Bin 163 -> 0 bytes .../drawable-xhdpi/abs__ab_solid_light_holo.9.png | Bin 163 -> 0 bytes .../drawable-xhdpi/abs__ab_solid_shadow_holo.9.png | Bin 290 -> 0 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 163 -> 0 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 163 -> 0 bytes .../abs__ab_stacked_transparent_dark_holo.9.png | Bin 158 -> 0 bytes .../abs__ab_stacked_transparent_light_holo.9.png | Bin 152 -> 0 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 171 -> 0 bytes .../abs__ab_transparent_light_holo.9.png | Bin 160 -> 0 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 109 -> 0 bytes .../abs__btn_cab_done_default_holo_light.9.png | Bin 108 -> 0 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 112 -> 0 bytes .../abs__btn_cab_done_focused_holo_light.9.png | Bin 113 -> 0 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 115 -> 0 bytes .../abs__btn_cab_done_pressed_holo_light.9.png | Bin 113 -> 0 bytes .../abs__cab_background_bottom_holo_dark.9.png | Bin 166 -> 0 bytes .../abs__cab_background_bottom_holo_light.9.png | Bin 161 -> 0 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 174 -> 0 bytes .../abs__cab_background_top_holo_light.9.png | Bin 161 -> 0 bytes .../abs__dialog_full_holo_dark.9.png | Bin 2159 -> 0 bytes .../abs__dialog_full_holo_light.9.png | Bin 2302 -> 0 bytes .../drawable-xhdpi/abs__ic_ab_back_holo_dark.png | Bin 741 -> 0 bytes .../drawable-xhdpi/abs__ic_ab_back_holo_light.png | Bin 661 -> 0 bytes .../drawable-xhdpi/abs__ic_cab_done_holo_dark.png | Bin 970 -> 0 bytes .../drawable-xhdpi/abs__ic_cab_done_holo_light.png | Bin 915 -> 0 bytes .../res/drawable-xhdpi/abs__ic_clear_disabled.png | Bin 2531 -> 0 bytes ...bs__ic_clear_search_api_disabled_holo_light.png | Bin 1315 -> 0 bytes .../abs__ic_clear_search_api_holo_light.png | Bin 1447 -> 0 bytes .../res/drawable-xhdpi/abs__ic_go.png | Bin 1983 -> 0 bytes .../abs__ic_go_search_api_holo_light.png | Bin 836 -> 0 bytes .../abs__ic_menu_moreoverflow_normal_holo_dark.png | Bin 167 -> 0 bytes ...abs__ic_menu_moreoverflow_normal_holo_light.png | Bin 184 -> 0 bytes .../abs__ic_menu_share_holo_dark.png | Bin 699 -> 0 bytes .../abs__ic_menu_share_holo_light.png | Bin 935 -> 0 bytes .../res/drawable-xhdpi/abs__ic_search.png | Bin 3784 -> 0 bytes .../abs__ic_search_api_holo_light.png | Bin 3037 -> 0 bytes .../res/drawable-xhdpi/abs__ic_voice_search.png | Bin 3053 -> 0 bytes .../abs__ic_voice_search_api_holo_light.png | Bin 1414 -> 0 bytes .../drawable-xhdpi/abs__list_activated_holo.9.png | Bin 158 -> 0 bytes .../abs__list_divider_holo_dark.9.png | Bin 83 -> 0 bytes .../abs__list_divider_holo_light.9.png | Bin 83 -> 0 bytes .../drawable-xhdpi/abs__list_focused_holo.9.png | Bin 163 -> 0 bytes .../abs__list_longpressed_holo.9.png | Bin 158 -> 0 bytes .../abs__list_pressed_holo_dark.9.png | Bin 163 -> 0 bytes .../abs__list_pressed_holo_light.9.png | Bin 163 -> 0 bytes .../abs__list_selector_disabled_holo_dark.9.png | Bin 190 -> 0 bytes .../abs__list_selector_disabled_holo_light.9.png | Bin 188 -> 0 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 1362 -> 0 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 1551 -> 0 bytes .../abs__progress_bg_holo_dark.9.png | Bin 174 -> 0 bytes .../abs__progress_bg_holo_light.9.png | Bin 172 -> 0 bytes .../abs__progress_primary_holo_dark.9.png | Bin 1309 -> 0 bytes .../abs__progress_primary_holo_light.9.png | Bin 1309 -> 0 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 184 -> 0 bytes .../abs__progress_secondary_holo_light.9.png | Bin 184 -> 0 bytes .../drawable-xhdpi/abs__spinner_48_inner_holo.png | Bin 2769 -> 0 bytes .../drawable-xhdpi/abs__spinner_48_outer_holo.png | Bin 2432 -> 0 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 395 -> 0 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 394 -> 0 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 381 -> 0 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 381 -> 0 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 680 -> 0 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 671 -> 0 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 609 -> 0 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 602 -> 0 bytes .../abs__tab_selected_focused_holo.9.png | Bin 147 -> 0 bytes .../drawable-xhdpi/abs__tab_selected_holo.9.png | Bin 153 -> 0 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 147 -> 0 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 149 -> 0 bytes .../abs__textfield_search_default_holo_dark.9.png | Bin 126 -> 0 bytes .../abs__textfield_search_default_holo_light.9.png | Bin 126 -> 0 bytes ..._textfield_search_right_default_holo_dark.9.png | Bin 125 -> 0 bytes ...textfield_search_right_default_holo_light.9.png | Bin 127 -> 0 bytes ...textfield_search_right_selected_holo_dark.9.png | Bin 128 -> 0 bytes ...extfield_search_right_selected_holo_light.9.png | Bin 128 -> 0 bytes .../abs__textfield_search_selected_holo_dark.9.png | Bin 114 -> 0 bytes ...abs__textfield_search_selected_holo_light.9.png | Bin 126 -> 0 bytes .../abs__activated_background_holo_dark.xml | 20 - .../abs__activated_background_holo_light.xml | 20 - .../res/drawable/abs__btn_cab_done_holo_dark.xml | 24 - .../res/drawable/abs__btn_cab_done_holo_light.xml | 24 - .../res/drawable/abs__ic_clear.xml | 22 - .../res/drawable/abs__ic_clear_holo_light.xml | 22 - .../abs__ic_menu_moreoverflow_holo_dark.xml | 18 - .../abs__ic_menu_moreoverflow_holo_light.xml | 18 - .../drawable/abs__item_background_holo_dark.xml | 26 - .../drawable/abs__item_background_holo_light.xml | 26 - ...st_selector_background_transition_holo_dark.xml | 20 - ...t_selector_background_transition_holo_light.xml | 20 - .../res/drawable/abs__list_selector_holo_dark.xml | 27 - .../res/drawable/abs__list_selector_holo_light.xml | 28 - .../abs__progress_horizontal_holo_dark.xml | 32 - .../abs__progress_horizontal_holo_light.xml | 32 - .../res/drawable/abs__progress_medium_holo.xml | 34 - .../res/drawable/abs__search_dropdown_dark.xml | 22 - .../res/drawable/abs__search_dropdown_light.xml | 22 - .../res/drawable/abs__spinner_ab_holo_dark.xml | 25 - .../res/drawable/abs__spinner_ab_holo_light.xml | 25 - .../res/drawable/abs__tab_indicator_ab_holo.xml | 34 - .../abs__textfield_searchview_holo_dark.xml | 22 - .../abs__textfield_searchview_holo_light.xml | 22 - .../abs__textfield_searchview_right_holo_dark.xml | 22 - .../abs__textfield_searchview_right_holo_light.xml | 22 - .../layout-large/abs__action_mode_close_item.xml | 40 - .../layout-v14/sherlock_spinner_dropdown_item.xml | 26 - .../res/layout-v14/sherlock_spinner_item.xml | 26 - .../res/layout-xlarge/abs__screen_action_bar.xml | 50 - .../abs__screen_action_bar_overlay.xml | 49 - .../res/layout/abs__action_bar_home.xml | 38 - .../res/layout/abs__action_bar_tab.xml | 7 - .../res/layout/abs__action_bar_tab_bar_view.xml | 6 - .../res/layout/abs__action_bar_title_item.xml | 50 - .../res/layout/abs__action_menu_item_layout.xml | 56 - .../res/layout/abs__action_menu_layout.xml | 23 - .../res/layout/abs__action_mode_bar.xml | 24 - .../res/layout/abs__action_mode_close_item.xml | 31 - .../res/layout/abs__activity_chooser_view.xml | 70 - .../abs__activity_chooser_view_list_item.xml | 53 - .../res/layout/abs__dialog_title_holo.xml | 46 - .../res/layout/abs__list_menu_item_checkbox.xml | 26 - .../res/layout/abs__list_menu_item_icon.xml | 28 - .../res/layout/abs__list_menu_item_layout.xml | 59 - .../res/layout/abs__list_menu_item_radio.xml | 24 - .../res/layout/abs__popup_menu_item_layout.xml | 60 - .../res/layout/abs__screen_action_bar.xml | 57 - .../res/layout/abs__screen_action_bar_overlay.xml | 59 - .../res/layout/abs__screen_simple.xml | 38 - .../abs__screen_simple_overlay_action_mode.xml | 38 - .../abs__search_dropdown_item_icons_2line.xml | 89 - .../res/layout/abs__search_view.xml | 159 -- .../res/layout/abs__simple_dropdown_hint.xml | 29 - .../res/layout/sherlock_spinner_dropdown_item.xml | 26 - .../res/layout/sherlock_spinner_item.xml | 26 - .../res/values-land/abs__dimens.xml | 33 - .../res/values-large-hdpi-1024x600/abs__dimens.xml | 33 - .../abs__dimens.xml | 33 - .../abs__dimens.xml | 33 - .../res/values-large-mdpi-1024x600/abs__dimens.xml | 36 - .../res/values-large/abs__dimens.xml | 29 - .../res/values-sw600dp/abs__bools.xml | 19 - .../res/values-sw600dp/abs__dimens.xml | 38 - .../res/values-v11/abs__themes.xml | 12 - .../res/values-v14/abs__styles.xml | 123 -- .../res/values-v14/abs__themes.xml | 34 - .../res/values-w360dp/abs__dimens.xml | 22 - .../res/values-w480dp/abs__bools.xml | 22 - .../res/values-w480dp/abs__config.xml | 29 - .../res/values-w500dp/abs__dimens.xml | 22 - .../res/values-w600dp/abs__dimens.xml | 22 - .../res/values-xlarge/abs__dimens.xml | 45 - .../ActionBarSherlock/res/values/abs__attrs.xml | 432 ----- .../ActionBarSherlock/res/values/abs__bools.xml | 22 - .../ActionBarSherlock/res/values/abs__colors.xml | 27 - .../ActionBarSherlock/res/values/abs__config.xml | 43 - .../ActionBarSherlock/res/values/abs__dimens.xml | 67 - .../ActionBarSherlock/res/values/abs__ids.xml | 26 - .../ActionBarSherlock/res/values/abs__strings.xml | 53 - .../ActionBarSherlock/res/values/abs__styles.xml | 412 ----- .../ActionBarSherlock/res/values/abs__themes.xml | 239 --- .../src/android/support/v4/app/Watson.java | 144 -- .../com/actionbarsherlock/ActionBarSherlock.java | 794 --------- .../src/com/actionbarsherlock/app/ActionBar.java | 956 ---------- .../actionbarsherlock/app/SherlockActivity.java | 270 --- .../app/SherlockDialogFragment.java | 68 - .../app/SherlockExpandableListActivity.java | 259 --- .../actionbarsherlock/app/SherlockFragment.java | 68 - .../app/SherlockFragmentActivity.java | 303 ---- .../app/SherlockListActivity.java | 270 --- .../app/SherlockListFragment.java | 68 - .../app/SherlockPreferenceActivity.java | 270 --- .../internal/ActionBarSherlockCompat.java | 1203 ------------- .../internal/ActionBarSherlockNative.java | 336 ---- .../internal/ResourcesCompat.java | 95 - .../internal/app/ActionBarImpl.java | 1026 ----------- .../internal/app/ActionBarWrapper.java | 468 ----- .../nineoldandroids/animation/Animator.java | 278 --- .../animation/AnimatorListenerAdapter.java | 54 - .../nineoldandroids/animation/AnimatorSet.java | 1111 ------------ .../nineoldandroids/animation/FloatEvaluator.java | 42 - .../animation/FloatKeyframeSet.java | 136 -- .../nineoldandroids/animation/IntEvaluator.java | 42 - .../nineoldandroids/animation/IntKeyframeSet.java | 135 -- .../nineoldandroids/animation/Keyframe.java | 361 ---- .../nineoldandroids/animation/KeyframeSet.java | 227 --- .../nineoldandroids/animation/ObjectAnimator.java | 491 ------ .../animation/PropertyValuesHolder.java | 1012 ----------- .../nineoldandroids/animation/TypeEvaluator.java | 44 - .../nineoldandroids/animation/ValueAnimator.java | 1265 ------------- .../nineoldandroids/view/NineViewGroup.java | 79 - .../view/animation/AnimatorProxy.java | 212 --- .../nineoldandroids/widget/NineFrameLayout.java | 57 - .../widget/NineHorizontalScrollView.java | 41 - .../nineoldandroids/widget/NineLinearLayout.java | 57 - .../internal/view/ActionProviderWrapper.java | 40 - .../internal/view/StandaloneActionMode.java | 148 -- .../view/View_HasStateListenerSupport.java | 6 - .../view/View_OnAttachStateChangeListener.java | 8 - .../internal/view/menu/ActionMenu.java | 264 --- .../internal/view/menu/ActionMenuItem.java | 278 --- .../internal/view/menu/ActionMenuItemView.java | 295 ---- .../internal/view/menu/ActionMenuPresenter.java | 714 -------- .../internal/view/menu/ActionMenuView.java | 575 ------ .../internal/view/menu/BaseMenuPresenter.java | 231 --- .../internal/view/menu/ListMenuItemView.java | 278 --- .../internal/view/menu/MenuBuilder.java | 1335 -------------- .../internal/view/menu/MenuItemImpl.java | 647 ------- .../internal/view/menu/MenuItemWrapper.java | 310 ---- .../internal/view/menu/MenuPopupHelper.java | 376 ---- .../internal/view/menu/MenuPresenter.java | 148 -- .../internal/view/menu/MenuView.java | 120 -- .../internal/view/menu/MenuWrapper.java | 185 -- .../internal/view/menu/SubMenuBuilder.java | 134 -- .../internal/view/menu/SubMenuWrapper.java | 72 - .../internal/widget/AbsActionBarView.java | 291 --- .../internal/widget/ActionBarContainer.java | 258 --- .../internal/widget/ActionBarContextView.java | 518 ------ .../internal/widget/ActionBarView.java | 1548 ---------------- .../internal/widget/CapitalizingButton.java | 40 - .../internal/widget/CapitalizingTextView.java | 50 - .../widget/CollapsibleActionViewWrapper.java | 30 - .../internal/widget/FakeDialogPhoneWindow.java | 64 - .../internal/widget/IcsAbsSpinner.java | 479 ----- .../internal/widget/IcsAdapterView.java | 1160 ------------ .../internal/widget/IcsColorDrawable.java | 41 - .../internal/widget/IcsLinearLayout.java | 410 ----- .../internal/widget/IcsListPopupWindow.java | 644 ------- .../internal/widget/IcsProgressBar.java | 1193 ------------- .../internal/widget/IcsSpinner.java | 703 -------- .../actionbarsherlock/internal/widget/IcsView.java | 21 - .../internal/widget/ScrollingTabContainerView.java | 546 ------ .../src/com/actionbarsherlock/view/ActionMode.java | 224 --- .../com/actionbarsherlock/view/ActionProvider.java | 170 -- .../view/CollapsibleActionView.java | 39 - .../src/com/actionbarsherlock/view/Menu.java | 447 ----- .../com/actionbarsherlock/view/MenuInflater.java | 495 ------ .../src/com/actionbarsherlock/view/MenuItem.java | 598 ------- .../src/com/actionbarsherlock/view/SubMenu.java | 110 -- .../src/com/actionbarsherlock/view/Window.java | 65 - .../widget/ActivityChooserModel.java | 1104 ------------ .../widget/ActivityChooserView.java | 827 --------- .../com/actionbarsherlock/widget/SearchView.java | 1811 ------------------- .../widget/ShareActionProvider.java | 316 ---- .../widget/SuggestionsAdapter.java | 733 -------- .../internal/ManifestParsingTest.java | 37 - APG/build.xml | 92 - ...arcodescanner-android-integration-supportv4.jar | Bin 8878 -> 0 bytes APG/libs/htmlcleaner-2.2.jar | Bin 107942 -> 0 bytes APG/libs/htmlspanner-0.2-fork.jar | Bin 31039 -> 0 bytes APG/libs/sc-bzip2-1.47.0.2.jar | Bin 26285 -> 0 bytes APG/libs/sc-light-jdk15on-1.47.0.2.jar | Bin 1482715 -> 0 bytes APG/libs/scpg-jdk15on-1.47.0.2.jar | Bin 278984 -> 0 bytes APG/libs/scprov-jdk15on-1.47.0.2.jar | Bin 987827 -> 0 bytes APG/pom.xml | 67 - APG/proguard-project.txt | 20 - APG/project.properties | 12 - APG/res/anim/push_left_in.xml | 20 - APG/res/anim/push_left_out.xml | 20 - APG/res/anim/push_right_in.xml | 20 - APG/res/anim/push_right_out.xml | 20 - APG/res/drawable-finger/btn_circle.xml | 32 - .../drawable-hdpi-finger/btn_circle_disable.png | Bin 2631 -> 0 bytes .../btn_circle_disable_focused.png | Bin 3001 -> 0 bytes APG/res/drawable-hdpi-finger/btn_circle_normal.png | Bin 1974 -> 0 bytes .../drawable-hdpi-finger/btn_circle_pressed.png | Bin 2624 -> 0 bytes .../drawable-hdpi-finger/btn_circle_selected.png | Bin 2554 -> 0 bytes .../drawable-hdpi-finger/ic_btn_round_minus.png | Bin 536 -> 0 bytes APG/res/drawable-hdpi-finger/ic_btn_round_plus.png | Bin 1316 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 144 -> 0 bytes .../drawable-hdpi/dashboard_decrypt_default.png | Bin 4996 -> 0 bytes .../drawable-hdpi/dashboard_decrypt_pressed.png | Bin 5032 -> 0 bytes .../drawable-hdpi/dashboard_encrypt_default.png | Bin 4926 -> 0 bytes .../drawable-hdpi/dashboard_encrypt_pressed.png | Bin 4967 -> 0 bytes APG/res/drawable-hdpi/dashboard_help_default.png | Bin 8607 -> 0 bytes APG/res/drawable-hdpi/dashboard_help_pressed.png | Bin 8343 -> 0 bytes .../dashboard_manage_keys_default.png | Bin 7306 -> 0 bytes .../dashboard_manage_keys_pressed.png | Bin 7147 -> 0 bytes .../drawable-hdpi/dashboard_my_keys_default.png | Bin 5438 -> 0 bytes .../drawable-hdpi/dashboard_my_keys_pressed.png | Bin 5414 -> 0 bytes .../dashboard_scan_qrcode_default.png | Bin 4241 -> 0 bytes .../dashboard_scan_qrcode_pressed.png | Bin 4418 -> 0 bytes APG/res/drawable-hdpi/encrypted.png | Bin 3561 -> 0 bytes APG/res/drawable-hdpi/encrypted_large.png | Bin 5244 -> 0 bytes APG/res/drawable-hdpi/encrypted_small.png | Bin 2187 -> 0 bytes APG/res/drawable-hdpi/ic_menu_about.png | Bin 1764 -> 0 bytes APG/res/drawable-hdpi/ic_menu_decrypt.png | Bin 1387 -> 0 bytes APG/res/drawable-hdpi/ic_menu_encrypt.png | Bin 1384 -> 0 bytes APG/res/drawable-hdpi/ic_menu_filebrowser.png | Bin 1443 -> 0 bytes APG/res/drawable-hdpi/ic_menu_scan_qrcode.png | Bin 1762 -> 0 bytes APG/res/drawable-hdpi/ic_menu_search.png | Bin 1218 -> 0 bytes APG/res/drawable-hdpi/ic_menu_search_list.png | Bin 1190 -> 0 bytes APG/res/drawable-hdpi/ic_menu_settings.png | Bin 1227 -> 0 bytes APG/res/drawable-hdpi/ic_next.png | Bin 1722 -> 0 bytes APG/res/drawable-hdpi/ic_previous.png | Bin 1712 -> 0 bytes APG/res/drawable-hdpi/icon.png | Bin 5308 -> 0 bytes APG/res/drawable-hdpi/key.png | Bin 3675 -> 0 bytes APG/res/drawable-hdpi/key_large.png | Bin 5550 -> 0 bytes APG/res/drawable-hdpi/key_small.png | Bin 2088 -> 0 bytes APG/res/drawable-hdpi/overlay_error.png | Bin 1986 -> 0 bytes APG/res/drawable-hdpi/overlay_ok.png | Bin 1702 -> 0 bytes APG/res/drawable-hdpi/signed.png | Bin 3858 -> 0 bytes APG/res/drawable-hdpi/signed_large.png | Bin 5928 -> 0 bytes APG/res/drawable-hdpi/signed_small.png | Bin 2219 -> 0 bytes APG/res/drawable-ldpi/encrypted.png | Bin 1513 -> 0 bytes APG/res/drawable-ldpi/encrypted_large.png | Bin 2486 -> 0 bytes APG/res/drawable-ldpi/encrypted_small.png | Bin 1176 -> 0 bytes APG/res/drawable-ldpi/ic_next.png | Bin 916 -> 0 bytes APG/res/drawable-ldpi/ic_previous.png | Bin 922 -> 0 bytes APG/res/drawable-ldpi/icon.png | Bin 2027 -> 0 bytes APG/res/drawable-ldpi/key.png | Bin 1484 -> 0 bytes APG/res/drawable-ldpi/key_large.png | Bin 2462 -> 0 bytes APG/res/drawable-ldpi/key_small.png | Bin 1074 -> 0 bytes APG/res/drawable-ldpi/overlay_error.png | Bin 1192 -> 0 bytes APG/res/drawable-ldpi/overlay_ok.png | Bin 1038 -> 0 bytes APG/res/drawable-ldpi/signed.png | Bin 1576 -> 0 bytes APG/res/drawable-ldpi/signed_large.png | Bin 2611 -> 0 bytes APG/res/drawable-ldpi/signed_small.png | Bin 1149 -> 0 bytes .../drawable-mdpi-finger/btn_circle_disable.png | Bin 938 -> 0 bytes .../btn_circle_disable_focused.png | Bin 1436 -> 0 bytes APG/res/drawable-mdpi-finger/btn_circle_normal.png | Bin 1249 -> 0 bytes .../drawable-mdpi-finger/btn_circle_pressed.png | Bin 1613 -> 0 bytes .../drawable-mdpi-finger/btn_circle_selected.png | Bin 1645 -> 0 bytes .../drawable-mdpi-finger/ic_btn_round_minus.png | Bin 288 -> 0 bytes APG/res/drawable-mdpi-finger/ic_btn_round_plus.png | Bin 526 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 134 -> 0 bytes APG/res/drawable-mdpi/encrypted.png | Bin 2486 -> 0 bytes APG/res/drawable-mdpi/encrypted_large.png | Bin 3561 -> 0 bytes APG/res/drawable-mdpi/encrypted_small.png | Bin 1513 -> 0 bytes APG/res/drawable-mdpi/ic_menu_about.png | Bin 1441 -> 0 bytes APG/res/drawable-mdpi/ic_menu_decrypt.png | Bin 1269 -> 0 bytes APG/res/drawable-mdpi/ic_menu_encrypt.png | Bin 1263 -> 0 bytes APG/res/drawable-mdpi/ic_menu_filebrowser.png | Bin 1307 -> 0 bytes APG/res/drawable-mdpi/ic_menu_scan_qrcode.png | Bin 1460 -> 0 bytes APG/res/drawable-mdpi/ic_menu_search.png | Bin 858 -> 0 bytes APG/res/drawable-mdpi/ic_menu_search_list.png | Bin 863 -> 0 bytes APG/res/drawable-mdpi/ic_menu_settings.png | Bin 866 -> 0 bytes APG/res/drawable-mdpi/ic_next.png | Bin 1360 -> 0 bytes APG/res/drawable-mdpi/ic_previous.png | Bin 1352 -> 0 bytes APG/res/drawable-mdpi/icon.png | Bin 3035 -> 0 bytes APG/res/drawable-mdpi/key.png | Bin 2462 -> 0 bytes APG/res/drawable-mdpi/key_large.png | Bin 3675 -> 0 bytes APG/res/drawable-mdpi/key_small.png | Bin 1484 -> 0 bytes APG/res/drawable-mdpi/overlay_error.png | Bin 1539 -> 0 bytes APG/res/drawable-mdpi/overlay_ok.png | Bin 1305 -> 0 bytes APG/res/drawable-mdpi/signed.png | Bin 2611 -> 0 bytes APG/res/drawable-mdpi/signed_large.png | Bin 3858 -> 0 bytes APG/res/drawable-mdpi/signed_small.png | Bin 1576 -> 0 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 166 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_about.png | Bin 2257 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_decrypt.png | Bin 1542 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_encrypt.png | Bin 1542 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_filebrowser.png | Bin 1604 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_scan_qrcode.png | Bin 2048 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_search.png | Bin 1629 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_search_list.png | Bin 1571 -> 0 bytes APG/res/drawable-xhdpi/ic_menu_settings.png | Bin 1622 -> 0 bytes APG/res/drawable-xhdpi/icon.png | Bin 8165 -> 0 bytes APG/res/drawable/btn_circle_disable.png | Bin 938 -> 0 bytes APG/res/drawable/btn_circle_disable_focused.png | Bin 1436 -> 0 bytes APG/res/drawable/btn_circle_normal.png | Bin 1249 -> 0 bytes APG/res/drawable/btn_circle_pressed.png | Bin 1613 -> 0 bytes APG/res/drawable/btn_circle_selected.png | Bin 1645 -> 0 bytes APG/res/drawable/dashboard_decrypt.xml | 28 - APG/res/drawable/dashboard_encrypt.xml | 28 - APG/res/drawable/dashboard_help.xml | 28 - APG/res/drawable/dashboard_manage_keys.xml | 28 - APG/res/drawable/dashboard_my_keys.xml | 28 - APG/res/drawable/dashboard_scan_qrcode.xml | 28 - APG/res/drawable/encrypted.png | Bin 2486 -> 0 bytes APG/res/drawable/encrypted_large.png | Bin 3561 -> 0 bytes APG/res/drawable/encrypted_small.png | Bin 1513 -> 0 bytes APG/res/drawable/ic_btn_round_minus.png | Bin 288 -> 0 bytes APG/res/drawable/ic_btn_round_plus.png | Bin 526 -> 0 bytes APG/res/drawable/ic_launcher_folder.png | Bin 2235 -> 0 bytes APG/res/drawable/ic_launcher_folder_small.png | Bin 1522 -> 0 bytes APG/res/drawable/ic_next.png | Bin 1360 -> 0 bytes APG/res/drawable/ic_previous.png | Bin 1352 -> 0 bytes APG/res/drawable/key.png | Bin 2462 -> 0 bytes APG/res/drawable/key_large.png | Bin 3675 -> 0 bytes APG/res/drawable/key_small.png | Bin 1484 -> 0 bytes APG/res/drawable/overlay_error.png | Bin 1539 -> 0 bytes APG/res/drawable/overlay_ok.png | Bin 1305 -> 0 bytes APG/res/drawable/section_header.xml | 11 - APG/res/drawable/signed.png | Bin 2611 -> 0 bytes APG/res/drawable/signed_large.png | Bin 3858 -> 0 bytes APG/res/drawable/signed_small.png | Bin 1576 -> 0 bytes APG/res/layout/account_item.xml | 35 - APG/res/layout/create_key.xml | 73 - APG/res/layout/decrypt.xml | 181 -- APG/res/layout/edit_key.xml | 59 - APG/res/layout/edit_key_key_item.xml | 134 -- APG/res/layout/edit_key_section.xml | 57 - APG/res/layout/edit_key_user_id_item.xml | 107 -- APG/res/layout/encrypt.xml | 306 ---- APG/res/layout/file_dialog.xml | 49 - APG/res/layout/filter_info.xml | 38 - APG/res/layout/general.xml | 59 - APG/res/layout/help_activity.xml | 14 - APG/res/layout/help_fragment_about.xml | 62 - APG/res/layout/import_keys.xml | 63 - APG/res/layout/key_list_child_item.xml | 96 - APG/res/layout/key_list_group_item.xml | 40 - APG/res/layout/key_list_public_activity.xml | 25 - APG/res/layout/key_list_secret_activity.xml | 25 - APG/res/layout/key_server_editor.xml | 52 - APG/res/layout/key_server_export_layout.xml | 34 - APG/res/layout/key_server_preference.xml | 90 - APG/res/layout/key_server_query_layout.xml | 53 - APG/res/layout/key_server_query_result_item.xml | 97 - APG/res/layout/key_server_query_result_user_id.xml | 26 - APG/res/layout/main.xml | 124 -- APG/res/layout/passphrase.xml | 40 - APG/res/layout/passphrase_repeat.xml | 62 - APG/res/layout/select_key_item.xml | 82 - APG/res/layout/select_public_key_activity.xml | 13 - APG/res/layout/select_secret_key_activity.xml | 13 - APG/res/layout/share_nfc_beam.xml | 14 - APG/res/layout/sign_key_layout.xml | 40 - APG/res/menu/nfc_beam.xml | 10 - APG/res/raw/help_about.html | 31 - APG/res/raw/help_changelog.html | 87 - APG/res/raw/help_nfc_beam.html | 16 - APG/res/raw/help_start.html | 27 - APG/res/raw/nfc_beam_share.html | 15 - APG/res/values/arrays.xml | 47 - APG/res/values/colors.xml | 7 - APG/res/values/static_strings.xml | 7 - APG/res/values/strings.xml | 361 ---- APG/res/values/styles.xml | 52 - APG/res/xml/apg_preferences.xml | 63 - APG/res/xml/searchable_public_keys.xml | 22 - APG/res/xml/searchable_secret_keys.xml | 22 - .../org/thialfihar/android/apg/ApgApplication.java | 48 - APG/src/org/thialfihar/android/apg/Constants.java | 69 - APG/src/org/thialfihar/android/apg/Id.java | 231 --- .../apg/compatibility/ClipboardReflection.java | 96 - .../compatibility/DialogFragmentWorkaround.java | 66 - .../apg/compatibility/ListFragmentWorkaround.java | 37 - .../thialfihar/android/apg/helper/FileHelper.java | 118 -- .../thialfihar/android/apg/helper/OtherHelper.java | 167 -- .../android/apg/helper/PGPConversionHelper.java | 140 -- .../thialfihar/android/apg/helper/PGPHelper.java | 407 ----- .../org/thialfihar/android/apg/helper/PGPMain.java | 1856 ------------------- .../thialfihar/android/apg/helper/Preferences.java | 172 -- .../android/apg/provider/ApgContract.java | 220 --- .../android/apg/provider/ApgDatabase.java | 104 -- .../android/apg/provider/ApgProvider.java | 852 --------- .../android/apg/provider/ApgProviderExternal.java | 31 - .../android/apg/provider/ApgProviderInternal.java | 29 - .../apg/provider/ApgServiceBlobContract.java | 40 - .../apg/provider/ApgServiceBlobDatabase.java | 47 - .../apg/provider/ApgServiceBlobProvider.java | 151 -- .../android/apg/provider/ProviderHelper.java | 663 ------- .../android/apg/service/ApgApiService.java | 320 ---- .../android/apg/service/ApgIntentService.java | 884 ---------- .../apg/service/ApgIntentServiceHandler.java | 109 -- .../android/apg/service/ApgKeyService.java | 137 -- .../android/apg/service/IApgApiService.aidl | 146 -- .../android/apg/service/IApgKeyService.aidl | 32 - .../apg/service/PassphraseCacheService.java | 230 --- .../apg/service/handler/IApgDecryptHandler.aidl | 27 - .../apg/service/handler/IApgEncryptHandler.aidl | 28 - .../handler/IApgGetDecryptionKeyIdHandler.aidl | 25 - .../service/handler/IApgGetKeyringsHandler.aidl | 28 - .../apg/service/handler/IApgSignHandler.aidl | 28 - .../apg/service/handler/IApgVerifyHandler.aidl | 26 - .../thialfihar/android/apg/ui/DecryptActivity.java | 946 ---------- .../thialfihar/android/apg/ui/EditKeyActivity.java | 593 ------- .../thialfihar/android/apg/ui/EncryptActivity.java | 1105 ------------ .../thialfihar/android/apg/ui/HelpActivity.java | 175 -- .../android/apg/ui/HelpFragmentAbout.java | 106 -- .../android/apg/ui/HelpFragmentHtml.java | 109 -- .../android/apg/ui/ImportKeysActivity.java | 473 ----- .../android/apg/ui/ImportKeysListFragment.java | 148 -- .../thialfihar/android/apg/ui/KeyListActivity.java | 314 ---- .../thialfihar/android/apg/ui/KeyListFragment.java | 88 - .../android/apg/ui/KeyListPublicActivity.java | 121 -- .../android/apg/ui/KeyListPublicFragment.java | 220 --- .../android/apg/ui/KeyListSecretActivity.java | 124 -- .../android/apg/ui/KeyListSecretFragment.java | 146 -- .../android/apg/ui/KeyServerQueryActivity.java | 353 ---- .../android/apg/ui/KeyServerUploadActivity.java | 146 -- .../thialfihar/android/apg/ui/MainActivity.java | 101 -- .../android/apg/ui/PreferencesActivity.java | 240 --- .../apg/ui/PreferencesKeyServerActivity.java | 162 -- .../android/apg/ui/SelectPublicKeyActivity.java | 164 -- .../android/apg/ui/SelectPublicKeyFragment.java | 232 --- .../android/apg/ui/SelectSecretKeyActivity.java | 132 -- .../android/apg/ui/SelectSecretKeyFragment.java | 154 -- .../thialfihar/android/apg/ui/ShareActivity.java | 74 - .../android/apg/ui/ShareNfcBeamActivity.java | 245 --- .../thialfihar/android/apg/ui/SignKeyActivity.java | 313 ---- .../apg/ui/dialog/DeleteFileDialogFragment.java | 119 -- .../apg/ui/dialog/DeleteKeyDialogFragment.java | 137 -- .../android/apg/ui/dialog/FileDialogFragment.java | 194 -- .../ui/dialog/LookupUnknownKeyDialogFragment.java | 136 -- .../apg/ui/dialog/PassphraseDialogFragment.java | 275 --- .../apg/ui/dialog/ProgressDialogFragment.java | 123 -- .../apg/ui/dialog/SetPassphraseDialogFragment.java | 190 -- .../android/apg/ui/widget/DashboardLayout.java | 186 -- .../thialfihar/android/apg/ui/widget/Editor.java | 25 - .../apg/ui/widget/ExpandableListFragment.java | 530 ------ .../apg/ui/widget/ImportKeysListLoader.java | 170 -- .../apg/ui/widget/IntegerListPreference.java | 95 - .../android/apg/ui/widget/KeyEditor.java | 236 --- .../android/apg/ui/widget/KeyListAdapter.java | 264 --- .../android/apg/ui/widget/KeyServerEditor.java | 78 - .../android/apg/ui/widget/SectionView.java | 327 ---- .../apg/ui/widget/SelectKeyCursorAdapter.java | 142 -- .../android/apg/ui/widget/UserIdEditor.java | 196 --- .../org/thialfihar/android/apg/util/Choice.java | 45 - .../thialfihar/android/apg/util/HkpKeyServer.java | 257 --- .../org/thialfihar/android/apg/util/InputData.java | 40 - .../android/apg/util/IterableIterator.java | 31 - .../org/thialfihar/android/apg/util/KeyServer.java | 97 - APG/src/org/thialfihar/android/apg/util/Log.java | 83 - .../android/apg/util/PositionAwareInputStream.java | 81 - .../org/thialfihar/android/apg/util/Primes.java | 188 -- .../android/apg/util/ProgressDialogUpdater.java | 25 - OpenPGP-Keychain-API-Demo/.gitignore | 23 + OpenPGP-Keychain-API-Demo/AndroidManifest.xml | 44 + OpenPGP-Keychain-API-Demo/build.xml | 83 + OpenPGP-Keychain-API-Demo/proguard-project.txt | 20 + OpenPGP-Keychain-API-Demo/project.properties | 12 + .../res/drawable-hdpi/icon.png | Bin 0 -> 5308 bytes .../res/drawable-ldpi/icon.png | Bin 0 -> 2027 bytes .../res/drawable-mdpi/icon.png | Bin 0 -> 3035 bytes .../res/drawable-xhdpi/icon.png | Bin 0 -> 8165 bytes OpenPGP-Keychain-API-Demo/res/layout/aidl_demo.xml | 72 + .../res/layout/aidl_demo2.xml | 40 + .../res/layout/content_provider_demo.xml | 33 + .../res/layout/intent_demo.xml | 93 + .../res/xml/base_preference.xml | 23 + .../keychain/demo/AidlDemoActivity.java | 216 +++ .../keychain/demo/AidlDemoActivity2.java | 168 ++ .../keychain/demo/BaseActivity.java | 92 + .../keychain/demo/ContentProviderDemoActivity.java | 68 + .../keychain/demo/IntentDemoActivity.java | 111 ++ OpenPGP-Keychain-API-Lib/.gitignore | 23 + OpenPGP-Keychain-API-Lib/AndroidManifest.xml | 11 + OpenPGP-Keychain-API-Lib/build.xml | 83 + .../libs/android-support-v4.jar | Bin 0 -> 271754 bytes OpenPGP-Keychain-API-Lib/proguard-project.txt | 20 + OpenPGP-Keychain-API-Lib/project.properties | 15 + OpenPGP-Keychain-API-Lib/res/.readme | 1 + .../keychain/integration/Constants.java | 25 + .../integration/KeychainContentProviderHelper.java | 266 +++ .../keychain/integration/KeychainData.java | 116 ++ .../keychain/integration/KeychainIntentHelper.java | 451 +++++ .../integration/KeychainIntentHelperSupportV4.java | 43 + .../integration/KeychainIntentHelperV30.java | 46 + .../integration/KeychainServiceHelper.java | 102 ++ .../keychain/integration/KeychainUtil.java | 73 + .../keychain/service/IKeychainApiService.aidl | 146 ++ .../keychain/service/IKeychainKeyService.aidl | 32 + .../service/handler/IKeychainDecryptHandler.aidl | 27 + .../service/handler/IKeychainEncryptHandler.aidl | 28 + .../IKeychainGetDecryptionKeyIdHandler.aidl | 25 + .../handler/IKeychainGetKeyringsHandler.aidl | 28 + .../service/handler/IKeychainSignHandler.aidl | 28 + .../service/handler/IKeychainVerifyHandler.aidl | 26 + OpenPGP-Keychain/.gitignore | 24 + OpenPGP-Keychain/AndroidManifest.xml | 447 +++++ .../android-libs/ActionBarSherlock/.gitignore | 23 + .../ActionBarSherlock/AndroidManifest.xml | 6 + .../android-libs/ActionBarSherlock/README.md | 15 + .../android-libs/ActionBarSherlock/build.xml | 92 + .../ActionBarSherlock/libs/android-support-v4.jar | Bin 0 -> 271754 bytes .../android-libs/ActionBarSherlock/pom.xml | 148 ++ .../ActionBarSherlock/proguard-project.txt | 20 + .../ActionBarSherlock/project.properties | 12 + .../abs__primary_text_disable_only_holo_dark.xml | 20 + .../abs__primary_text_disable_only_holo_light.xml | 21 + .../res/color/abs__primary_text_holo_dark.xml | 24 + .../res/color/abs__primary_text_holo_light.xml | 26 + .../abs__ab_bottom_solid_dark_holo.9.png | Bin 0 -> 144 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 0 -> 138 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 144 bytes .../abs__ab_bottom_transparent_dark_holo.9.png | Bin 0 -> 135 bytes .../abs__ab_bottom_transparent_light_holo.9.png | Bin 0 -> 134 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 0 -> 2863 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 0 -> 2859 bytes .../drawable-hdpi/abs__ab_solid_dark_holo.9.png | Bin 0 -> 146 bytes .../drawable-hdpi/abs__ab_solid_light_holo.9.png | Bin 0 -> 145 bytes .../drawable-hdpi/abs__ab_solid_shadow_holo.9.png | Bin 0 -> 192 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 0 -> 146 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 0 -> 146 bytes .../abs__ab_stacked_transparent_dark_holo.9.png | Bin 0 -> 139 bytes .../abs__ab_stacked_transparent_light_holo.9.png | Bin 0 -> 133 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 0 -> 155 bytes .../abs__ab_transparent_light_holo.9.png | Bin 0 -> 145 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 0 -> 104 bytes .../abs__btn_cab_done_default_holo_light.9.png | Bin 0 -> 102 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 0 -> 112 bytes .../abs__btn_cab_done_focused_holo_light.9.png | Bin 0 -> 108 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 0 -> 110 bytes .../abs__btn_cab_done_pressed_holo_light.9.png | Bin 0 -> 108 bytes .../abs__cab_background_bottom_holo_dark.9.png | Bin 0 -> 149 bytes .../abs__cab_background_bottom_holo_light.9.png | Bin 0 -> 145 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 0 -> 147 bytes .../abs__cab_background_top_holo_light.9.png | Bin 0 -> 147 bytes .../drawable-hdpi/abs__dialog_full_holo_dark.9.png | Bin 0 -> 1414 bytes .../abs__dialog_full_holo_light.9.png | Bin 0 -> 1537 bytes .../drawable-hdpi/abs__ic_ab_back_holo_dark.png | Bin 0 -> 602 bytes .../drawable-hdpi/abs__ic_ab_back_holo_light.png | Bin 0 -> 546 bytes .../drawable-hdpi/abs__ic_cab_done_holo_dark.png | Bin 0 -> 713 bytes .../drawable-hdpi/abs__ic_cab_done_holo_light.png | Bin 0 -> 737 bytes .../res/drawable-hdpi/abs__ic_clear_disabled.png | Bin 0 -> 1774 bytes .../res/drawable-hdpi/abs__ic_clear_normal.png | Bin 0 -> 1945 bytes ...bs__ic_clear_search_api_disabled_holo_light.png | Bin 0 -> 1504 bytes .../abs__ic_clear_search_api_holo_light.png | Bin 0 -> 1540 bytes .../res/drawable-hdpi/abs__ic_go.png | Bin 0 -> 1415 bytes .../abs__ic_go_search_api_holo_light.png | Bin 0 -> 1252 bytes .../abs__ic_menu_moreoverflow_normal_holo_dark.png | Bin 0 -> 144 bytes ...abs__ic_menu_moreoverflow_normal_holo_light.png | Bin 0 -> 148 bytes .../drawable-hdpi/abs__ic_menu_share_holo_dark.png | Bin 0 -> 467 bytes .../abs__ic_menu_share_holo_light.png | Bin 0 -> 505 bytes .../res/drawable-hdpi/abs__ic_search.png | Bin 0 -> 2280 bytes .../abs__ic_search_api_holo_light.png | Bin 0 -> 2271 bytes .../res/drawable-hdpi/abs__ic_voice_search.png | Bin 0 -> 2070 bytes .../abs__ic_voice_search_api_holo_light.png | Bin 0 -> 1833 bytes .../drawable-hdpi/abs__list_activated_holo.9.png | Bin 0 -> 154 bytes .../abs__list_divider_holo_dark.9.png | Bin 0 -> 78 bytes .../abs__list_divider_holo_light.9.png | Bin 0 -> 76 bytes .../res/drawable-hdpi/abs__list_focused_holo.9.png | Bin 0 -> 159 bytes .../drawable-hdpi/abs__list_longpressed_holo.9.png | Bin 0 -> 154 bytes .../abs__list_pressed_holo_dark.9.png | Bin 0 -> 159 bytes .../abs__list_pressed_holo_light.9.png | Bin 0 -> 159 bytes .../abs__list_selector_disabled_holo_dark.9.png | Bin 0 -> 189 bytes .../abs__list_selector_disabled_holo_light.9.png | Bin 0 -> 189 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 0 -> 922 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 0 -> 1061 bytes .../drawable-hdpi/abs__progress_bg_holo_dark.9.png | Bin 0 -> 178 bytes .../abs__progress_bg_holo_light.9.png | Bin 0 -> 174 bytes .../abs__progress_primary_holo_dark.9.png | Bin 0 -> 917 bytes .../abs__progress_primary_holo_light.9.png | Bin 0 -> 917 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 0 -> 188 bytes .../abs__progress_secondary_holo_light.9.png | Bin 0 -> 188 bytes .../drawable-hdpi/abs__spinner_48_inner_holo.png | Bin 0 -> 2081 bytes .../drawable-hdpi/abs__spinner_48_outer_holo.png | Bin 0 -> 1811 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 0 -> 311 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 0 -> 312 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 0 -> 306 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 0 -> 306 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 0 -> 524 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 0 -> 523 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 0 -> 464 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 0 -> 458 bytes .../abs__tab_selected_focused_holo.9.png | Bin 0 -> 147 bytes .../res/drawable-hdpi/abs__tab_selected_holo.9.png | Bin 0 -> 148 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 0 -> 147 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 0 -> 145 bytes .../abs__textfield_search_default_holo_dark.9.png | Bin 0 -> 110 bytes .../abs__textfield_search_default_holo_light.9.png | Bin 0 -> 105 bytes ..._textfield_search_right_default_holo_dark.9.png | Bin 0 -> 108 bytes ...textfield_search_right_default_holo_light.9.png | Bin 0 -> 103 bytes ...textfield_search_right_selected_holo_dark.9.png | Bin 0 -> 114 bytes ...extfield_search_right_selected_holo_light.9.png | Bin 0 -> 111 bytes .../abs__textfield_search_selected_holo_dark.9.png | Bin 0 -> 114 bytes ...abs__textfield_search_selected_holo_light.9.png | Bin 0 -> 112 bytes .../abs__ab_bottom_solid_dark_holo.9.png | Bin 0 -> 134 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 0 -> 129 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 134 bytes .../abs__ab_bottom_transparent_dark_holo.9.png | Bin 0 -> 123 bytes .../abs__ab_bottom_transparent_light_holo.9.png | Bin 0 -> 123 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 0 -> 2849 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 0 -> 191 bytes .../drawable-mdpi/abs__ab_solid_dark_holo.9.png | Bin 0 -> 133 bytes .../drawable-mdpi/abs__ab_solid_light_holo.9.png | Bin 0 -> 133 bytes .../drawable-mdpi/abs__ab_solid_shadow_holo.9.png | Bin 0 -> 168 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 0 -> 134 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 0 -> 133 bytes .../abs__ab_stacked_transparent_dark_holo.9.png | Bin 0 -> 127 bytes .../abs__ab_stacked_transparent_light_holo.9.png | Bin 0 -> 123 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 0 -> 139 bytes .../abs__ab_transparent_light_holo.9.png | Bin 0 -> 133 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 0 -> 101 bytes .../abs__btn_cab_done_default_holo_light.9.png | Bin 0 -> 99 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 0 -> 109 bytes .../abs__btn_cab_done_focused_holo_light.9.png | Bin 0 -> 105 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 0 -> 107 bytes .../abs__btn_cab_done_pressed_holo_light.9.png | Bin 0 -> 105 bytes .../abs__cab_background_bottom_holo_dark.9.png | Bin 0 -> 127 bytes .../abs__cab_background_bottom_holo_light.9.png | Bin 0 -> 124 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 0 -> 130 bytes .../abs__cab_background_top_holo_light.9.png | Bin 0 -> 128 bytes .../drawable-mdpi/abs__dialog_full_holo_dark.9.png | Bin 0 -> 882 bytes .../abs__dialog_full_holo_light.9.png | Bin 0 -> 1003 bytes .../drawable-mdpi/abs__ic_ab_back_holo_dark.png | Bin 0 -> 466 bytes .../drawable-mdpi/abs__ic_ab_back_holo_light.png | Bin 0 -> 438 bytes .../drawable-mdpi/abs__ic_cab_done_holo_dark.png | Bin 0 -> 566 bytes .../drawable-mdpi/abs__ic_cab_done_holo_light.png | Bin 0 -> 552 bytes .../res/drawable-mdpi/abs__ic_clear_disabled.png | Bin 0 -> 1775 bytes .../res/drawable-mdpi/abs__ic_clear_normal.png | Bin 0 -> 1869 bytes ...bs__ic_clear_search_api_disabled_holo_light.png | Bin 0 -> 740 bytes .../abs__ic_clear_search_api_holo_light.png | Bin 0 -> 743 bytes .../res/drawable-mdpi/abs__ic_go.png | Bin 0 -> 1538 bytes .../abs__ic_go_search_api_holo_light.png | Bin 0 -> 570 bytes .../abs__ic_menu_moreoverflow_normal_holo_dark.png | Bin 0 -> 122 bytes ...abs__ic_menu_moreoverflow_normal_holo_light.png | Bin 0 -> 131 bytes .../drawable-mdpi/abs__ic_menu_share_holo_dark.png | Bin 0 -> 332 bytes .../abs__ic_menu_share_holo_light.png | Bin 0 -> 355 bytes .../res/drawable-mdpi/abs__ic_search.png | Bin 0 -> 2280 bytes .../abs__ic_search_api_holo_light.png | Bin 0 -> 1541 bytes .../res/drawable-mdpi/abs__ic_voice_search.png | Bin 0 -> 1937 bytes .../abs__ic_voice_search_api_holo_light.png | Bin 0 -> 794 bytes .../drawable-mdpi/abs__list_activated_holo.9.png | Bin 0 -> 151 bytes .../abs__list_divider_holo_dark.9.png | Bin 0 -> 78 bytes .../abs__list_divider_holo_light.9.png | Bin 0 -> 76 bytes .../res/drawable-mdpi/abs__list_focused_holo.9.png | Bin 0 -> 158 bytes .../drawable-mdpi/abs__list_longpressed_holo.9.png | Bin 0 -> 151 bytes .../abs__list_pressed_holo_dark.9.png | Bin 0 -> 158 bytes .../abs__list_pressed_holo_light.9.png | Bin 0 -> 158 bytes .../abs__list_selector_disabled_holo_dark.9.png | Bin 0 -> 172 bytes .../abs__list_selector_disabled_holo_light.9.png | Bin 0 -> 171 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 0 -> 651 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 0 -> 720 bytes .../drawable-mdpi/abs__progress_bg_holo_dark.9.png | Bin 0 -> 165 bytes .../abs__progress_bg_holo_light.9.png | Bin 0 -> 159 bytes .../abs__progress_primary_holo_dark.9.png | Bin 0 -> 572 bytes .../abs__progress_primary_holo_light.9.png | Bin 0 -> 572 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 0 -> 170 bytes .../abs__progress_secondary_holo_light.9.png | Bin 0 -> 170 bytes .../drawable-mdpi/abs__spinner_48_inner_holo.png | Bin 0 -> 1336 bytes .../drawable-mdpi/abs__spinner_48_outer_holo.png | Bin 0 -> 1165 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 0 -> 254 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 0 -> 255 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 0 -> 249 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 0 -> 249 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 0 -> 417 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 0 -> 424 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 0 -> 370 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 0 -> 370 bytes .../abs__tab_selected_focused_holo.9.png | Bin 0 -> 148 bytes .../res/drawable-mdpi/abs__tab_selected_holo.9.png | Bin 0 -> 151 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 0 -> 150 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 0 -> 155 bytes .../abs__textfield_search_default_holo_dark.9.png | Bin 0 -> 106 bytes .../abs__textfield_search_default_holo_light.9.png | Bin 0 -> 100 bytes ..._textfield_search_right_default_holo_dark.9.png | Bin 0 -> 105 bytes ...textfield_search_right_default_holo_light.9.png | Bin 0 -> 98 bytes ...textfield_search_right_selected_holo_dark.9.png | Bin 0 -> 107 bytes ...extfield_search_right_selected_holo_light.9.png | Bin 0 -> 107 bytes .../abs__textfield_search_selected_holo_dark.9.png | Bin 0 -> 109 bytes ...abs__textfield_search_selected_holo_light.9.png | Bin 0 -> 109 bytes .../res/drawable-v11/abs__progress_medium_holo.xml | 34 + .../abs__ab_bottom_solid_dark_holo.9.png | Bin 0 -> 165 bytes .../abs__ab_bottom_solid_inverse_holo.9.png | Bin 0 -> 157 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 166 bytes .../abs__ab_bottom_transparent_dark_holo.9.png | Bin 0 -> 153 bytes .../abs__ab_bottom_transparent_light_holo.9.png | Bin 0 -> 152 bytes .../abs__ab_share_pack_holo_dark.9.png | Bin 0 -> 2878 bytes .../abs__ab_share_pack_holo_light.9.png | Bin 0 -> 2873 bytes .../drawable-xhdpi/abs__ab_solid_dark_holo.9.png | Bin 0 -> 163 bytes .../drawable-xhdpi/abs__ab_solid_light_holo.9.png | Bin 0 -> 163 bytes .../drawable-xhdpi/abs__ab_solid_shadow_holo.9.png | Bin 0 -> 290 bytes .../abs__ab_stacked_solid_dark_holo.9.png | Bin 0 -> 163 bytes .../abs__ab_stacked_solid_light_holo.9.png | Bin 0 -> 163 bytes .../abs__ab_stacked_transparent_dark_holo.9.png | Bin 0 -> 158 bytes .../abs__ab_stacked_transparent_light_holo.9.png | Bin 0 -> 152 bytes .../abs__ab_transparent_dark_holo.9.png | Bin 0 -> 171 bytes .../abs__ab_transparent_light_holo.9.png | Bin 0 -> 160 bytes .../abs__btn_cab_done_default_holo_dark.9.png | Bin 0 -> 109 bytes .../abs__btn_cab_done_default_holo_light.9.png | Bin 0 -> 108 bytes .../abs__btn_cab_done_focused_holo_dark.9.png | Bin 0 -> 112 bytes .../abs__btn_cab_done_focused_holo_light.9.png | Bin 0 -> 113 bytes .../abs__btn_cab_done_pressed_holo_dark.9.png | Bin 0 -> 115 bytes .../abs__btn_cab_done_pressed_holo_light.9.png | Bin 0 -> 113 bytes .../abs__cab_background_bottom_holo_dark.9.png | Bin 0 -> 166 bytes .../abs__cab_background_bottom_holo_light.9.png | Bin 0 -> 161 bytes .../abs__cab_background_top_holo_dark.9.png | Bin 0 -> 174 bytes .../abs__cab_background_top_holo_light.9.png | Bin 0 -> 161 bytes .../abs__dialog_full_holo_dark.9.png | Bin 0 -> 2159 bytes .../abs__dialog_full_holo_light.9.png | Bin 0 -> 2302 bytes .../drawable-xhdpi/abs__ic_ab_back_holo_dark.png | Bin 0 -> 741 bytes .../drawable-xhdpi/abs__ic_ab_back_holo_light.png | Bin 0 -> 661 bytes .../drawable-xhdpi/abs__ic_cab_done_holo_dark.png | Bin 0 -> 970 bytes .../drawable-xhdpi/abs__ic_cab_done_holo_light.png | Bin 0 -> 915 bytes .../res/drawable-xhdpi/abs__ic_clear_disabled.png | Bin 0 -> 2531 bytes ...bs__ic_clear_search_api_disabled_holo_light.png | Bin 0 -> 1315 bytes .../abs__ic_clear_search_api_holo_light.png | Bin 0 -> 1447 bytes .../res/drawable-xhdpi/abs__ic_go.png | Bin 0 -> 1983 bytes .../abs__ic_go_search_api_holo_light.png | Bin 0 -> 836 bytes .../abs__ic_menu_moreoverflow_normal_holo_dark.png | Bin 0 -> 167 bytes ...abs__ic_menu_moreoverflow_normal_holo_light.png | Bin 0 -> 184 bytes .../abs__ic_menu_share_holo_dark.png | Bin 0 -> 699 bytes .../abs__ic_menu_share_holo_light.png | Bin 0 -> 935 bytes .../res/drawable-xhdpi/abs__ic_search.png | Bin 0 -> 3784 bytes .../abs__ic_search_api_holo_light.png | Bin 0 -> 3037 bytes .../res/drawable-xhdpi/abs__ic_voice_search.png | Bin 0 -> 3053 bytes .../abs__ic_voice_search_api_holo_light.png | Bin 0 -> 1414 bytes .../drawable-xhdpi/abs__list_activated_holo.9.png | Bin 0 -> 158 bytes .../abs__list_divider_holo_dark.9.png | Bin 0 -> 83 bytes .../abs__list_divider_holo_light.9.png | Bin 0 -> 83 bytes .../drawable-xhdpi/abs__list_focused_holo.9.png | Bin 0 -> 163 bytes .../abs__list_longpressed_holo.9.png | Bin 0 -> 158 bytes .../abs__list_pressed_holo_dark.9.png | Bin 0 -> 163 bytes .../abs__list_pressed_holo_light.9.png | Bin 0 -> 163 bytes .../abs__list_selector_disabled_holo_dark.9.png | Bin 0 -> 190 bytes .../abs__list_selector_disabled_holo_light.9.png | Bin 0 -> 188 bytes .../abs__menu_dropdown_panel_holo_dark.9.png | Bin 0 -> 1362 bytes .../abs__menu_dropdown_panel_holo_light.9.png | Bin 0 -> 1551 bytes .../abs__progress_bg_holo_dark.9.png | Bin 0 -> 174 bytes .../abs__progress_bg_holo_light.9.png | Bin 0 -> 172 bytes .../abs__progress_primary_holo_dark.9.png | Bin 0 -> 1309 bytes .../abs__progress_primary_holo_light.9.png | Bin 0 -> 1309 bytes .../abs__progress_secondary_holo_dark.9.png | Bin 0 -> 184 bytes .../abs__progress_secondary_holo_light.9.png | Bin 0 -> 184 bytes .../drawable-xhdpi/abs__spinner_48_inner_holo.png | Bin 0 -> 2769 bytes .../drawable-xhdpi/abs__spinner_48_outer_holo.png | Bin 0 -> 2432 bytes .../abs__spinner_ab_default_holo_dark.9.png | Bin 0 -> 395 bytes .../abs__spinner_ab_default_holo_light.9.png | Bin 0 -> 394 bytes .../abs__spinner_ab_disabled_holo_dark.9.png | Bin 0 -> 381 bytes .../abs__spinner_ab_disabled_holo_light.9.png | Bin 0 -> 381 bytes .../abs__spinner_ab_focused_holo_dark.9.png | Bin 0 -> 680 bytes .../abs__spinner_ab_focused_holo_light.9.png | Bin 0 -> 671 bytes .../abs__spinner_ab_pressed_holo_dark.9.png | Bin 0 -> 609 bytes .../abs__spinner_ab_pressed_holo_light.9.png | Bin 0 -> 602 bytes .../abs__tab_selected_focused_holo.9.png | Bin 0 -> 147 bytes .../drawable-xhdpi/abs__tab_selected_holo.9.png | Bin 0 -> 153 bytes .../abs__tab_selected_pressed_holo.9.png | Bin 0 -> 147 bytes .../abs__tab_unselected_pressed_holo.9.png | Bin 0 -> 149 bytes .../abs__textfield_search_default_holo_dark.9.png | Bin 0 -> 126 bytes .../abs__textfield_search_default_holo_light.9.png | Bin 0 -> 126 bytes ..._textfield_search_right_default_holo_dark.9.png | Bin 0 -> 125 bytes ...textfield_search_right_default_holo_light.9.png | Bin 0 -> 127 bytes ...textfield_search_right_selected_holo_dark.9.png | Bin 0 -> 128 bytes ...extfield_search_right_selected_holo_light.9.png | Bin 0 -> 128 bytes .../abs__textfield_search_selected_holo_dark.9.png | Bin 0 -> 114 bytes ...abs__textfield_search_selected_holo_light.9.png | Bin 0 -> 126 bytes .../abs__activated_background_holo_dark.xml | 20 + .../abs__activated_background_holo_light.xml | 20 + .../res/drawable/abs__btn_cab_done_holo_dark.xml | 24 + .../res/drawable/abs__btn_cab_done_holo_light.xml | 24 + .../res/drawable/abs__ic_clear.xml | 22 + .../res/drawable/abs__ic_clear_holo_light.xml | 22 + .../abs__ic_menu_moreoverflow_holo_dark.xml | 18 + .../abs__ic_menu_moreoverflow_holo_light.xml | 18 + .../drawable/abs__item_background_holo_dark.xml | 26 + .../drawable/abs__item_background_holo_light.xml | 26 + ...st_selector_background_transition_holo_dark.xml | 20 + ...t_selector_background_transition_holo_light.xml | 20 + .../res/drawable/abs__list_selector_holo_dark.xml | 27 + .../res/drawable/abs__list_selector_holo_light.xml | 28 + .../abs__progress_horizontal_holo_dark.xml | 32 + .../abs__progress_horizontal_holo_light.xml | 32 + .../res/drawable/abs__progress_medium_holo.xml | 34 + .../res/drawable/abs__search_dropdown_dark.xml | 22 + .../res/drawable/abs__search_dropdown_light.xml | 22 + .../res/drawable/abs__spinner_ab_holo_dark.xml | 25 + .../res/drawable/abs__spinner_ab_holo_light.xml | 25 + .../res/drawable/abs__tab_indicator_ab_holo.xml | 34 + .../abs__textfield_searchview_holo_dark.xml | 22 + .../abs__textfield_searchview_holo_light.xml | 22 + .../abs__textfield_searchview_right_holo_dark.xml | 22 + .../abs__textfield_searchview_right_holo_light.xml | 22 + .../layout-large/abs__action_mode_close_item.xml | 40 + .../layout-v14/sherlock_spinner_dropdown_item.xml | 26 + .../res/layout-v14/sherlock_spinner_item.xml | 26 + .../res/layout-xlarge/abs__screen_action_bar.xml | 50 + .../abs__screen_action_bar_overlay.xml | 49 + .../res/layout/abs__action_bar_home.xml | 38 + .../res/layout/abs__action_bar_tab.xml | 7 + .../res/layout/abs__action_bar_tab_bar_view.xml | 6 + .../res/layout/abs__action_bar_title_item.xml | 50 + .../res/layout/abs__action_menu_item_layout.xml | 56 + .../res/layout/abs__action_menu_layout.xml | 23 + .../res/layout/abs__action_mode_bar.xml | 24 + .../res/layout/abs__action_mode_close_item.xml | 31 + .../res/layout/abs__activity_chooser_view.xml | 70 + .../abs__activity_chooser_view_list_item.xml | 53 + .../res/layout/abs__dialog_title_holo.xml | 46 + .../res/layout/abs__list_menu_item_checkbox.xml | 26 + .../res/layout/abs__list_menu_item_icon.xml | 28 + .../res/layout/abs__list_menu_item_layout.xml | 59 + .../res/layout/abs__list_menu_item_radio.xml | 24 + .../res/layout/abs__popup_menu_item_layout.xml | 60 + .../res/layout/abs__screen_action_bar.xml | 57 + .../res/layout/abs__screen_action_bar_overlay.xml | 59 + .../res/layout/abs__screen_simple.xml | 38 + .../abs__screen_simple_overlay_action_mode.xml | 38 + .../abs__search_dropdown_item_icons_2line.xml | 89 + .../res/layout/abs__search_view.xml | 159 ++ .../res/layout/abs__simple_dropdown_hint.xml | 29 + .../res/layout/sherlock_spinner_dropdown_item.xml | 26 + .../res/layout/sherlock_spinner_item.xml | 26 + .../res/values-land/abs__dimens.xml | 33 + .../res/values-large-hdpi-1024x600/abs__dimens.xml | 33 + .../abs__dimens.xml | 33 + .../abs__dimens.xml | 33 + .../res/values-large-mdpi-1024x600/abs__dimens.xml | 36 + .../res/values-large/abs__dimens.xml | 29 + .../res/values-sw600dp/abs__bools.xml | 19 + .../res/values-sw600dp/abs__dimens.xml | 38 + .../res/values-v11/abs__themes.xml | 12 + .../res/values-v14/abs__styles.xml | 123 ++ .../res/values-v14/abs__themes.xml | 34 + .../res/values-w360dp/abs__dimens.xml | 22 + .../res/values-w480dp/abs__bools.xml | 22 + .../res/values-w480dp/abs__config.xml | 29 + .../res/values-w500dp/abs__dimens.xml | 22 + .../res/values-w600dp/abs__dimens.xml | 22 + .../res/values-xlarge/abs__dimens.xml | 45 + .../ActionBarSherlock/res/values/abs__attrs.xml | 432 +++++ .../ActionBarSherlock/res/values/abs__bools.xml | 22 + .../ActionBarSherlock/res/values/abs__colors.xml | 27 + .../ActionBarSherlock/res/values/abs__config.xml | 43 + .../ActionBarSherlock/res/values/abs__dimens.xml | 67 + .../ActionBarSherlock/res/values/abs__ids.xml | 26 + .../ActionBarSherlock/res/values/abs__strings.xml | 53 + .../ActionBarSherlock/res/values/abs__styles.xml | 412 +++++ .../ActionBarSherlock/res/values/abs__themes.xml | 239 +++ .../src/android/support/v4/app/Watson.java | 144 ++ .../com/actionbarsherlock/ActionBarSherlock.java | 794 +++++++++ .../src/com/actionbarsherlock/app/ActionBar.java | 956 ++++++++++ .../actionbarsherlock/app/SherlockActivity.java | 270 +++ .../app/SherlockDialogFragment.java | 68 + .../app/SherlockExpandableListActivity.java | 259 +++ .../actionbarsherlock/app/SherlockFragment.java | 68 + .../app/SherlockFragmentActivity.java | 303 ++++ .../app/SherlockListActivity.java | 270 +++ .../app/SherlockListFragment.java | 68 + .../app/SherlockPreferenceActivity.java | 270 +++ .../internal/ActionBarSherlockCompat.java | 1203 +++++++++++++ .../internal/ActionBarSherlockNative.java | 336 ++++ .../internal/ResourcesCompat.java | 95 + .../internal/app/ActionBarImpl.java | 1026 +++++++++++ .../internal/app/ActionBarWrapper.java | 468 +++++ .../nineoldandroids/animation/Animator.java | 278 +++ .../animation/AnimatorListenerAdapter.java | 54 + .../nineoldandroids/animation/AnimatorSet.java | 1111 ++++++++++++ .../nineoldandroids/animation/FloatEvaluator.java | 42 + .../animation/FloatKeyframeSet.java | 136 ++ .../nineoldandroids/animation/IntEvaluator.java | 42 + .../nineoldandroids/animation/IntKeyframeSet.java | 135 ++ .../nineoldandroids/animation/Keyframe.java | 361 ++++ .../nineoldandroids/animation/KeyframeSet.java | 227 +++ .../nineoldandroids/animation/ObjectAnimator.java | 491 ++++++ .../animation/PropertyValuesHolder.java | 1012 +++++++++++ .../nineoldandroids/animation/TypeEvaluator.java | 44 + .../nineoldandroids/animation/ValueAnimator.java | 1265 +++++++++++++ .../nineoldandroids/view/NineViewGroup.java | 79 + .../view/animation/AnimatorProxy.java | 212 +++ .../nineoldandroids/widget/NineFrameLayout.java | 57 + .../widget/NineHorizontalScrollView.java | 41 + .../nineoldandroids/widget/NineLinearLayout.java | 57 + .../internal/view/ActionProviderWrapper.java | 40 + .../internal/view/StandaloneActionMode.java | 148 ++ .../view/View_HasStateListenerSupport.java | 6 + .../view/View_OnAttachStateChangeListener.java | 8 + .../internal/view/menu/ActionMenu.java | 264 +++ .../internal/view/menu/ActionMenuItem.java | 278 +++ .../internal/view/menu/ActionMenuItemView.java | 295 ++++ .../internal/view/menu/ActionMenuPresenter.java | 714 ++++++++ .../internal/view/menu/ActionMenuView.java | 575 ++++++ .../internal/view/menu/BaseMenuPresenter.java | 231 +++ .../internal/view/menu/ListMenuItemView.java | 278 +++ .../internal/view/menu/MenuBuilder.java | 1335 ++++++++++++++ .../internal/view/menu/MenuItemImpl.java | 647 +++++++ .../internal/view/menu/MenuItemWrapper.java | 310 ++++ .../internal/view/menu/MenuPopupHelper.java | 376 ++++ .../internal/view/menu/MenuPresenter.java | 148 ++ .../internal/view/menu/MenuView.java | 120 ++ .../internal/view/menu/MenuWrapper.java | 185 ++ .../internal/view/menu/SubMenuBuilder.java | 134 ++ .../internal/view/menu/SubMenuWrapper.java | 72 + .../internal/widget/AbsActionBarView.java | 291 +++ .../internal/widget/ActionBarContainer.java | 258 +++ .../internal/widget/ActionBarContextView.java | 518 ++++++ .../internal/widget/ActionBarView.java | 1548 ++++++++++++++++ .../internal/widget/CapitalizingButton.java | 40 + .../internal/widget/CapitalizingTextView.java | 50 + .../widget/CollapsibleActionViewWrapper.java | 30 + .../internal/widget/FakeDialogPhoneWindow.java | 64 + .../internal/widget/IcsAbsSpinner.java | 479 +++++ .../internal/widget/IcsAdapterView.java | 1160 ++++++++++++ .../internal/widget/IcsColorDrawable.java | 41 + .../internal/widget/IcsLinearLayout.java | 410 +++++ .../internal/widget/IcsListPopupWindow.java | 644 +++++++ .../internal/widget/IcsProgressBar.java | 1193 +++++++++++++ .../internal/widget/IcsSpinner.java | 703 ++++++++ .../actionbarsherlock/internal/widget/IcsView.java | 21 + .../internal/widget/ScrollingTabContainerView.java | 546 ++++++ .../src/com/actionbarsherlock/view/ActionMode.java | 224 +++ .../com/actionbarsherlock/view/ActionProvider.java | 170 ++ .../view/CollapsibleActionView.java | 39 + .../src/com/actionbarsherlock/view/Menu.java | 447 +++++ .../com/actionbarsherlock/view/MenuInflater.java | 495 ++++++ .../src/com/actionbarsherlock/view/MenuItem.java | 598 +++++++ .../src/com/actionbarsherlock/view/SubMenu.java | 110 ++ .../src/com/actionbarsherlock/view/Window.java | 65 + .../widget/ActivityChooserModel.java | 1104 ++++++++++++ .../widget/ActivityChooserView.java | 827 +++++++++ .../com/actionbarsherlock/widget/SearchView.java | 1811 +++++++++++++++++++ .../widget/ShareActionProvider.java | 316 ++++ .../widget/SuggestionsAdapter.java | 733 ++++++++ .../internal/ManifestParsingTest.java | 37 + OpenPGP-Keychain/build.xml | 92 + ...arcodescanner-android-integration-supportv4.jar | Bin 0 -> 8878 bytes OpenPGP-Keychain/libs/htmlcleaner-2.2.jar | Bin 0 -> 107942 bytes OpenPGP-Keychain/libs/htmlspanner-0.2-fork.jar | Bin 0 -> 31039 bytes OpenPGP-Keychain/libs/sc-bzip2-1.47.0.2.jar | Bin 0 -> 26285 bytes .../libs/sc-light-jdk15on-1.47.0.2.jar | Bin 0 -> 1482715 bytes OpenPGP-Keychain/libs/scpg-jdk15on-1.47.0.2.jar | Bin 0 -> 278984 bytes OpenPGP-Keychain/libs/scprov-jdk15on-1.47.0.2.jar | Bin 0 -> 987827 bytes OpenPGP-Keychain/pom.xml | 67 + OpenPGP-Keychain/proguard-project.txt | 20 + OpenPGP-Keychain/project.properties | 12 + OpenPGP-Keychain/res/anim/push_left_in.xml | 20 + OpenPGP-Keychain/res/anim/push_left_out.xml | 20 + OpenPGP-Keychain/res/anim/push_right_in.xml | 20 + OpenPGP-Keychain/res/anim/push_right_out.xml | 20 + .../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 .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 144 bytes .../drawable-hdpi/dashboard_decrypt_default.png | Bin 0 -> 4996 bytes .../drawable-hdpi/dashboard_decrypt_pressed.png | Bin 0 -> 5032 bytes .../drawable-hdpi/dashboard_encrypt_default.png | Bin 0 -> 4926 bytes .../drawable-hdpi/dashboard_encrypt_pressed.png | Bin 0 -> 4967 bytes .../res/drawable-hdpi/dashboard_help_default.png | Bin 0 -> 8607 bytes .../res/drawable-hdpi/dashboard_help_pressed.png | Bin 0 -> 8343 bytes .../dashboard_manage_keys_default.png | Bin 0 -> 7306 bytes .../dashboard_manage_keys_pressed.png | Bin 0 -> 7147 bytes .../drawable-hdpi/dashboard_my_keys_default.png | Bin 0 -> 5438 bytes .../drawable-hdpi/dashboard_my_keys_pressed.png | Bin 0 -> 5414 bytes .../dashboard_scan_qrcode_default.png | Bin 0 -> 4241 bytes .../dashboard_scan_qrcode_pressed.png | Bin 0 -> 4418 bytes OpenPGP-Keychain/res/drawable-hdpi/encrypted.png | Bin 0 -> 3561 bytes .../res/drawable-hdpi/encrypted_large.png | Bin 0 -> 5244 bytes .../res/drawable-hdpi/encrypted_small.png | Bin 0 -> 2187 bytes .../res/drawable-hdpi/ic_menu_about.png | Bin 0 -> 1764 bytes .../res/drawable-hdpi/ic_menu_decrypt.png | Bin 0 -> 1387 bytes .../res/drawable-hdpi/ic_menu_encrypt.png | Bin 0 -> 1384 bytes .../res/drawable-hdpi/ic_menu_filebrowser.png | Bin 0 -> 1443 bytes .../res/drawable-hdpi/ic_menu_scan_qrcode.png | Bin 0 -> 1762 bytes .../res/drawable-hdpi/ic_menu_search.png | Bin 0 -> 1218 bytes .../res/drawable-hdpi/ic_menu_search_list.png | Bin 0 -> 1190 bytes .../res/drawable-hdpi/ic_menu_settings.png | Bin 0 -> 1227 bytes OpenPGP-Keychain/res/drawable-hdpi/ic_next.png | Bin 0 -> 1722 bytes OpenPGP-Keychain/res/drawable-hdpi/ic_previous.png | Bin 0 -> 1712 bytes OpenPGP-Keychain/res/drawable-hdpi/icon.png | Bin 0 -> 5308 bytes OpenPGP-Keychain/res/drawable-hdpi/key.png | Bin 0 -> 3675 bytes OpenPGP-Keychain/res/drawable-hdpi/key_large.png | Bin 0 -> 5550 bytes OpenPGP-Keychain/res/drawable-hdpi/key_small.png | Bin 0 -> 2088 bytes .../res/drawable-hdpi/overlay_error.png | Bin 0 -> 1986 bytes OpenPGP-Keychain/res/drawable-hdpi/overlay_ok.png | Bin 0 -> 1702 bytes OpenPGP-Keychain/res/drawable-hdpi/signed.png | Bin 0 -> 3858 bytes .../res/drawable-hdpi/signed_large.png | Bin 0 -> 5928 bytes .../res/drawable-hdpi/signed_small.png | Bin 0 -> 2219 bytes OpenPGP-Keychain/res/drawable-ldpi/encrypted.png | Bin 0 -> 1513 bytes .../res/drawable-ldpi/encrypted_large.png | Bin 0 -> 2486 bytes .../res/drawable-ldpi/encrypted_small.png | Bin 0 -> 1176 bytes OpenPGP-Keychain/res/drawable-ldpi/ic_next.png | Bin 0 -> 916 bytes OpenPGP-Keychain/res/drawable-ldpi/ic_previous.png | Bin 0 -> 922 bytes OpenPGP-Keychain/res/drawable-ldpi/icon.png | Bin 0 -> 2027 bytes OpenPGP-Keychain/res/drawable-ldpi/key.png | Bin 0 -> 1484 bytes OpenPGP-Keychain/res/drawable-ldpi/key_large.png | Bin 0 -> 2462 bytes OpenPGP-Keychain/res/drawable-ldpi/key_small.png | Bin 0 -> 1074 bytes .../res/drawable-ldpi/overlay_error.png | Bin 0 -> 1192 bytes OpenPGP-Keychain/res/drawable-ldpi/overlay_ok.png | Bin 0 -> 1038 bytes OpenPGP-Keychain/res/drawable-ldpi/signed.png | Bin 0 -> 1576 bytes .../res/drawable-ldpi/signed_large.png | Bin 0 -> 2611 bytes .../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 .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 134 bytes OpenPGP-Keychain/res/drawable-mdpi/encrypted.png | Bin 0 -> 2486 bytes .../res/drawable-mdpi/encrypted_large.png | Bin 0 -> 3561 bytes .../res/drawable-mdpi/encrypted_small.png | Bin 0 -> 1513 bytes .../res/drawable-mdpi/ic_menu_about.png | Bin 0 -> 1441 bytes .../res/drawable-mdpi/ic_menu_decrypt.png | Bin 0 -> 1269 bytes .../res/drawable-mdpi/ic_menu_encrypt.png | Bin 0 -> 1263 bytes .../res/drawable-mdpi/ic_menu_filebrowser.png | Bin 0 -> 1307 bytes .../res/drawable-mdpi/ic_menu_scan_qrcode.png | Bin 0 -> 1460 bytes .../res/drawable-mdpi/ic_menu_search.png | Bin 0 -> 858 bytes .../res/drawable-mdpi/ic_menu_search_list.png | Bin 0 -> 863 bytes .../res/drawable-mdpi/ic_menu_settings.png | Bin 0 -> 866 bytes OpenPGP-Keychain/res/drawable-mdpi/ic_next.png | Bin 0 -> 1360 bytes OpenPGP-Keychain/res/drawable-mdpi/ic_previous.png | Bin 0 -> 1352 bytes OpenPGP-Keychain/res/drawable-mdpi/icon.png | Bin 0 -> 3035 bytes OpenPGP-Keychain/res/drawable-mdpi/key.png | Bin 0 -> 2462 bytes OpenPGP-Keychain/res/drawable-mdpi/key_large.png | Bin 0 -> 3675 bytes OpenPGP-Keychain/res/drawable-mdpi/key_small.png | Bin 0 -> 1484 bytes .../res/drawable-mdpi/overlay_error.png | Bin 0 -> 1539 bytes OpenPGP-Keychain/res/drawable-mdpi/overlay_ok.png | Bin 0 -> 1305 bytes OpenPGP-Keychain/res/drawable-mdpi/signed.png | Bin 0 -> 2611 bytes .../res/drawable-mdpi/signed_large.png | Bin 0 -> 3858 bytes .../res/drawable-mdpi/signed_small.png | Bin 0 -> 1576 bytes .../abs__ab_bottom_solid_light_holo.9.png | Bin 0 -> 166 bytes .../res/drawable-xhdpi/ic_menu_about.png | Bin 0 -> 2257 bytes .../res/drawable-xhdpi/ic_menu_decrypt.png | Bin 0 -> 1542 bytes .../res/drawable-xhdpi/ic_menu_encrypt.png | Bin 0 -> 1542 bytes .../res/drawable-xhdpi/ic_menu_filebrowser.png | Bin 0 -> 1604 bytes .../res/drawable-xhdpi/ic_menu_scan_qrcode.png | Bin 0 -> 2048 bytes .../res/drawable-xhdpi/ic_menu_search.png | Bin 0 -> 1629 bytes .../res/drawable-xhdpi/ic_menu_search_list.png | Bin 0 -> 1571 bytes .../res/drawable-xhdpi/ic_menu_settings.png | Bin 0 -> 1622 bytes OpenPGP-Keychain/res/drawable-xhdpi/icon.png | Bin 0 -> 8165 bytes .../res/drawable/btn_circle_disable.png | Bin 0 -> 938 bytes .../res/drawable/btn_circle_disable_focused.png | Bin 0 -> 1436 bytes .../res/drawable/btn_circle_normal.png | Bin 0 -> 1249 bytes .../res/drawable/btn_circle_pressed.png | Bin 0 -> 1613 bytes .../res/drawable/btn_circle_selected.png | Bin 0 -> 1645 bytes .../res/drawable/dashboard_decrypt.xml | 28 + .../res/drawable/dashboard_encrypt.xml | 28 + OpenPGP-Keychain/res/drawable/dashboard_help.xml | 28 + .../res/drawable/dashboard_manage_keys.xml | 28 + .../res/drawable/dashboard_my_keys.xml | 28 + .../res/drawable/dashboard_scan_qrcode.xml | 28 + OpenPGP-Keychain/res/drawable/encrypted.png | Bin 0 -> 2486 bytes OpenPGP-Keychain/res/drawable/encrypted_large.png | Bin 0 -> 3561 bytes OpenPGP-Keychain/res/drawable/encrypted_small.png | Bin 0 -> 1513 bytes .../res/drawable/ic_btn_round_minus.png | Bin 0 -> 288 bytes .../res/drawable/ic_btn_round_plus.png | Bin 0 -> 526 bytes .../res/drawable/ic_launcher_folder.png | Bin 0 -> 2235 bytes .../res/drawable/ic_launcher_folder_small.png | Bin 0 -> 1522 bytes OpenPGP-Keychain/res/drawable/ic_next.png | Bin 0 -> 1360 bytes OpenPGP-Keychain/res/drawable/ic_previous.png | Bin 0 -> 1352 bytes OpenPGP-Keychain/res/drawable/key.png | Bin 0 -> 2462 bytes OpenPGP-Keychain/res/drawable/key_large.png | Bin 0 -> 3675 bytes OpenPGP-Keychain/res/drawable/key_small.png | Bin 0 -> 1484 bytes OpenPGP-Keychain/res/drawable/overlay_error.png | Bin 0 -> 1539 bytes OpenPGP-Keychain/res/drawable/overlay_ok.png | Bin 0 -> 1305 bytes OpenPGP-Keychain/res/drawable/section_header.xml | 11 + OpenPGP-Keychain/res/drawable/signed.png | Bin 0 -> 2611 bytes OpenPGP-Keychain/res/drawable/signed_large.png | Bin 0 -> 3858 bytes OpenPGP-Keychain/res/drawable/signed_small.png | Bin 0 -> 1576 bytes OpenPGP-Keychain/res/layout/account_item.xml | 35 + OpenPGP-Keychain/res/layout/create_key.xml | 72 + OpenPGP-Keychain/res/layout/decrypt.xml | 181 ++ OpenPGP-Keychain/res/layout/edit_key.xml | 59 + OpenPGP-Keychain/res/layout/edit_key_key_item.xml | 134 ++ OpenPGP-Keychain/res/layout/edit_key_section.xml | 57 + .../res/layout/edit_key_user_id_item.xml | 107 ++ OpenPGP-Keychain/res/layout/encrypt.xml | 306 ++++ OpenPGP-Keychain/res/layout/file_dialog.xml | 49 + OpenPGP-Keychain/res/layout/filter_info.xml | 38 + OpenPGP-Keychain/res/layout/general.xml | 59 + OpenPGP-Keychain/res/layout/help_activity.xml | 14 + .../res/layout/help_fragment_about.xml | 62 + OpenPGP-Keychain/res/layout/import_keys.xml | 63 + .../res/layout/key_list_child_item.xml | 96 + .../res/layout/key_list_group_item.xml | 40 + .../res/layout/key_list_public_activity.xml | 25 + .../res/layout/key_list_secret_activity.xml | 25 + OpenPGP-Keychain/res/layout/key_server_editor.xml | 52 + .../res/layout/key_server_export_layout.xml | 34 + .../res/layout/key_server_preference.xml | 90 + .../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 + OpenPGP-Keychain/res/layout/main.xml | 70 + OpenPGP-Keychain/res/layout/passphrase.xml | 40 + OpenPGP-Keychain/res/layout/passphrase_repeat.xml | 62 + OpenPGP-Keychain/res/layout/select_key_item.xml | 82 + .../res/layout/select_public_key_activity.xml | 13 + .../res/layout/select_secret_key_activity.xml | 13 + OpenPGP-Keychain/res/layout/share_nfc_beam.xml | 14 + OpenPGP-Keychain/res/layout/sign_key_layout.xml | 40 + OpenPGP-Keychain/res/menu/nfc_beam.xml | 10 + OpenPGP-Keychain/res/raw/help_about.html | 32 + OpenPGP-Keychain/res/raw/help_changelog.html | 87 + OpenPGP-Keychain/res/raw/help_nfc_beam.html | 16 + OpenPGP-Keychain/res/raw/help_start.html | 27 + OpenPGP-Keychain/res/raw/nfc_beam_share.html | 15 + OpenPGP-Keychain/res/values/arrays.xml | 47 + OpenPGP-Keychain/res/values/colors.xml | 7 + OpenPGP-Keychain/res/values/static_strings.xml | 7 + OpenPGP-Keychain/res/values/strings.xml | 361 ++++ OpenPGP-Keychain/res/values/styles.xml | 52 + OpenPGP-Keychain/res/xml/preferences.xml | 63 + .../res/xml/searchable_public_keys.xml | 22 + .../res/xml/searchable_secret_keys.xml | 22 + .../org/sufficientlysecure/keychain/Constants.java | 57 + .../src/org/sufficientlysecure/keychain/Id.java | 231 +++ .../keychain/KeychainApplication.java | 48 + .../compatibility/ClipboardReflection.java | 97 + .../compatibility/DialogFragmentWorkaround.java | 66 + .../compatibility/ListFragmentWorkaround.java | 37 + .../keychain/helper/FileHelper.java | 118 ++ .../keychain/helper/OtherHelper.java | 167 ++ .../keychain/helper/PgpConversionHelper.java | 140 ++ .../keychain/helper/PgpHelper.java | 407 +++++ .../keychain/helper/PgpMain.java | 1857 ++++++++++++++++++++ .../keychain/helper/Preferences.java | 172 ++ .../keychain/provider/KeychainContract.java | 220 +++ .../keychain/provider/KeychainDatabase.java | 104 ++ .../keychain/provider/KeychainProvider.java | 852 +++++++++ .../provider/KeychainProviderExternal.java | 31 + .../provider/KeychainProviderInternal.java | 29 + .../provider/KeychainServiceBlobContract.java | 40 + .../provider/KeychainServiceBlobDatabase.java | 47 + .../provider/KeychainServiceBlobProvider.java | 151 ++ .../keychain/provider/ProviderHelper.java | 663 +++++++ .../keychain/service/IKeychainApiService.aidl | 146 ++ .../keychain/service/IKeychainKeyService.aidl | 32 + .../keychain/service/KeychainApiService.java | 323 ++++ .../keychain/service/KeychainIntentService.java | 884 ++++++++++ .../service/KeychainIntentServiceHandler.java | 109 ++ .../keychain/service/KeychainKeyService.java | 138 ++ .../keychain/service/PassphraseCacheService.java | 230 +++ .../service/handler/IKeychainDecryptHandler.aidl | 27 + .../service/handler/IKeychainEncryptHandler.aidl | 28 + .../IKeychainGetDecryptionKeyIdHandler.aidl | 25 + .../handler/IKeychainGetKeyringsHandler.aidl | 28 + .../service/handler/IKeychainSignHandler.aidl | 28 + .../service/handler/IKeychainVerifyHandler.aidl | 26 + .../keychain/ui/DecryptActivity.java | 946 ++++++++++ .../keychain/ui/EditKeyActivity.java | 593 +++++++ .../keychain/ui/EncryptActivity.java | 1105 ++++++++++++ .../keychain/ui/HelpActivity.java | 175 ++ .../keychain/ui/HelpFragmentAbout.java | 106 ++ .../keychain/ui/HelpFragmentHtml.java | 109 ++ .../keychain/ui/ImportKeysActivity.java | 473 +++++ .../keychain/ui/ImportKeysListFragment.java | 148 ++ .../keychain/ui/KeyListActivity.java | 314 ++++ .../keychain/ui/KeyListFragment.java | 88 + .../keychain/ui/KeyListPublicActivity.java | 121 ++ .../keychain/ui/KeyListPublicFragment.java | 220 +++ .../keychain/ui/KeyListSecretActivity.java | 124 ++ .../keychain/ui/KeyListSecretFragment.java | 146 ++ .../keychain/ui/KeyServerQueryActivity.java | 353 ++++ .../keychain/ui/KeyServerUploadActivity.java | 146 ++ .../keychain/ui/MainActivity.java | 101 ++ .../keychain/ui/PreferencesActivity.java | 240 +++ .../keychain/ui/PreferencesKeyServerActivity.java | 162 ++ .../keychain/ui/SelectPublicKeyActivity.java | 164 ++ .../keychain/ui/SelectPublicKeyFragment.java | 232 +++ .../keychain/ui/SelectSecretKeyActivity.java | 131 ++ .../keychain/ui/SelectSecretKeyFragment.java | 154 ++ .../keychain/ui/ShareActivity.java | 74 + .../keychain/ui/ShareNfcBeamActivity.java | 245 +++ .../keychain/ui/SignKeyActivity.java | 313 ++++ .../ui/dialog/DeleteFileDialogFragment.java | 119 ++ .../ui/dialog/DeleteKeyDialogFragment.java | 137 ++ .../keychain/ui/dialog/FileDialogFragment.java | 194 ++ .../ui/dialog/LookupUnknownKeyDialogFragment.java | 136 ++ .../ui/dialog/PassphraseDialogFragment.java | 275 +++ .../keychain/ui/dialog/ProgressDialogFragment.java | 123 ++ .../ui/dialog/SetPassphraseDialogFragment.java | 190 ++ .../keychain/ui/widget/DashboardLayout.java | 186 ++ .../keychain/ui/widget/Editor.java | 25 + .../keychain/ui/widget/ExpandableListFragment.java | 530 ++++++ .../keychain/ui/widget/ImportKeysListLoader.java | 170 ++ .../keychain/ui/widget/IntegerListPreference.java | 95 + .../keychain/ui/widget/KeyEditor.java | 236 +++ .../keychain/ui/widget/KeyListAdapter.java | 264 +++ .../keychain/ui/widget/KeyServerEditor.java | 78 + .../keychain/ui/widget/SectionView.java | 327 ++++ .../keychain/ui/widget/SelectKeyCursorAdapter.java | 142 ++ .../keychain/ui/widget/UserIdEditor.java | 196 +++ .../sufficientlysecure/keychain/util/Choice.java | 45 + .../keychain/util/HkpKeyServer.java | 257 +++ .../keychain/util/InputData.java | 40 + .../keychain/util/IterableIterator.java | 31 + .../keychain/util/KeyServer.java | 97 + .../org/sufficientlysecure/keychain/util/Log.java | 83 + .../keychain/util/PositionAwareInputStream.java | 81 + .../sufficientlysecure/keychain/util/Primes.java | 188 ++ .../keychain/util/ProgressDialogUpdater.java | 25 + 1504 files changed, 63020 insertions(+), 63075 deletions(-) delete mode 100644 APG-API-Demo/.gitignore delete mode 100644 APG-API-Demo/AndroidManifest.xml delete mode 100644 APG-API-Demo/build.xml delete mode 100644 APG-API-Demo/proguard-project.txt delete mode 100644 APG-API-Demo/project.properties delete mode 100644 APG-API-Demo/res/drawable-hdpi/icon.png delete mode 100644 APG-API-Demo/res/drawable-ldpi/icon.png delete mode 100644 APG-API-Demo/res/drawable-mdpi/icon.png delete mode 100644 APG-API-Demo/res/drawable-xhdpi/icon.png delete mode 100644 APG-API-Demo/res/layout/aidl_demo.xml delete mode 100644 APG-API-Demo/res/layout/aidl_demo2.xml delete mode 100644 APG-API-Demo/res/layout/content_provider_demo.xml delete mode 100644 APG-API-Demo/res/layout/intent_demo.xml delete mode 100644 APG-API-Demo/res/xml/base_preference.xml delete mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/AidlDemoActivity.java delete mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/AidlDemoActivity2.java delete mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/BaseActivity.java delete mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/ContentProviderDemoActivity.java delete mode 100644 APG-API-Demo/src/org/thialfihar/android/apg/demo/IntentDemoActivity.java delete mode 100644 APG-API-Lib/.gitignore delete mode 100644 APG-API-Lib/AndroidManifest.xml delete mode 100644 APG-API-Lib/build.xml delete mode 100644 APG-API-Lib/libs/android-support-v4.jar delete mode 100644 APG-API-Lib/proguard-project.txt delete mode 100644 APG-API-Lib/project.properties delete mode 100644 APG-API-Lib/res/.readme delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgContentProviderHelper.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgData.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgIntentHelper.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgIntentHelperSupportV4.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgIntentHelperV30.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgServiceHelper.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/ApgUtil.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/integration/Constants.java delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/IApgApiService.aidl delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/IApgKeyService.aidl delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/handler/IApgDecryptHandler.aidl delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/handler/IApgEncryptHandler.aidl delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/handler/IApgGetDecryptionKeyIdHandler.aidl delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/handler/IApgGetKeyringsHandler.aidl delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/handler/IApgSignHandler.aidl delete mode 100644 APG-API-Lib/src/org/thialfihar/android/apg/service/handler/IApgVerifyHandler.aidl delete mode 100644 APG/.gitignore delete mode 100644 APG/AndroidManifest.xml delete mode 100644 APG/android-libs/ActionBarSherlock/.gitignore delete mode 100644 APG/android-libs/ActionBarSherlock/AndroidManifest.xml delete mode 100644 APG/android-libs/ActionBarSherlock/README.md delete mode 100644 APG/android-libs/ActionBarSherlock/build.xml delete mode 100644 APG/android-libs/ActionBarSherlock/libs/android-support-v4.jar delete mode 100644 APG/android-libs/ActionBarSherlock/pom.xml delete mode 100644 APG/android-libs/ActionBarSherlock/proguard-project.txt delete mode 100644 APG/android-libs/ActionBarSherlock/project.properties delete mode 100644 APG/android-libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_disabled.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_normal.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_go.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_search.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_activated_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_focused_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_inner_holo.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_disabled.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_normal.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_go.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_search.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_longpressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_outer_holo.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-v11/abs__progress_medium_holo.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_disabled.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_activated_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_focused_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_longpressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_inner_holo.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__ic_clear.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__ic_clear_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__search_dropdown_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__search_dropdown_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_dark.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_light.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_bar_home.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_bar_tab.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_menu_layout.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_mode_bar.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__screen_action_bar.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__screen_simple.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__search_dropdown_item_icons_2line.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__search_view.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/abs__simple_dropdown_hint.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/layout/sherlock_spinner_item.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-land/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-large/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-sw600dp/abs__bools.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-v11/abs__themes.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-v14/abs__styles.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-v14/abs__themes.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-w360dp/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-w480dp/abs__bools.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-w480dp/abs__config.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-w500dp/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-w600dp/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values-xlarge/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__attrs.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__bools.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__colors.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__config.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__dimens.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__ids.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__strings.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__styles.xml delete mode 100644 APG/android-libs/ActionBarSherlock/res/values/abs__themes.xml delete mode 100644 APG/android-libs/ActionBarSherlock/src/android/support/v4/app/Watson.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/SearchView.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java delete mode 100644 APG/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/SuggestionsAdapter.java delete mode 100644 APG/android-libs/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java delete mode 100644 APG/build.xml delete mode 100644 APG/libs/barcodescanner-android-integration-supportv4.jar delete mode 100644 APG/libs/htmlcleaner-2.2.jar delete mode 100644 APG/libs/htmlspanner-0.2-fork.jar delete mode 100644 APG/libs/sc-bzip2-1.47.0.2.jar delete mode 100644 APG/libs/sc-light-jdk15on-1.47.0.2.jar delete mode 100644 APG/libs/scpg-jdk15on-1.47.0.2.jar delete mode 100644 APG/libs/scprov-jdk15on-1.47.0.2.jar delete mode 100644 APG/pom.xml delete mode 100644 APG/proguard-project.txt delete mode 100644 APG/project.properties delete mode 100644 APG/res/anim/push_left_in.xml delete mode 100644 APG/res/anim/push_left_out.xml delete mode 100644 APG/res/anim/push_right_in.xml delete mode 100644 APG/res/anim/push_right_out.xml delete mode 100644 APG/res/drawable-finger/btn_circle.xml delete mode 100644 APG/res/drawable-hdpi-finger/btn_circle_disable.png delete mode 100644 APG/res/drawable-hdpi-finger/btn_circle_disable_focused.png delete mode 100644 APG/res/drawable-hdpi-finger/btn_circle_normal.png delete mode 100644 APG/res/drawable-hdpi-finger/btn_circle_pressed.png delete mode 100644 APG/res/drawable-hdpi-finger/btn_circle_selected.png delete mode 100644 APG/res/drawable-hdpi-finger/ic_btn_round_minus.png delete mode 100644 APG/res/drawable-hdpi-finger/ic_btn_round_plus.png delete mode 100644 APG/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 APG/res/drawable-hdpi/dashboard_decrypt_default.png delete mode 100644 APG/res/drawable-hdpi/dashboard_decrypt_pressed.png delete mode 100644 APG/res/drawable-hdpi/dashboard_encrypt_default.png delete mode 100644 APG/res/drawable-hdpi/dashboard_encrypt_pressed.png delete mode 100644 APG/res/drawable-hdpi/dashboard_help_default.png delete mode 100644 APG/res/drawable-hdpi/dashboard_help_pressed.png delete mode 100644 APG/res/drawable-hdpi/dashboard_manage_keys_default.png delete mode 100644 APG/res/drawable-hdpi/dashboard_manage_keys_pressed.png delete mode 100644 APG/res/drawable-hdpi/dashboard_my_keys_default.png delete mode 100644 APG/res/drawable-hdpi/dashboard_my_keys_pressed.png delete mode 100644 APG/res/drawable-hdpi/dashboard_scan_qrcode_default.png delete mode 100644 APG/res/drawable-hdpi/dashboard_scan_qrcode_pressed.png delete mode 100644 APG/res/drawable-hdpi/encrypted.png delete mode 100644 APG/res/drawable-hdpi/encrypted_large.png delete mode 100644 APG/res/drawable-hdpi/encrypted_small.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_about.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_decrypt.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_encrypt.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_filebrowser.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_scan_qrcode.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_search.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_search_list.png delete mode 100644 APG/res/drawable-hdpi/ic_menu_settings.png delete mode 100644 APG/res/drawable-hdpi/ic_next.png delete mode 100644 APG/res/drawable-hdpi/ic_previous.png delete mode 100644 APG/res/drawable-hdpi/icon.png delete mode 100644 APG/res/drawable-hdpi/key.png delete mode 100644 APG/res/drawable-hdpi/key_large.png delete mode 100644 APG/res/drawable-hdpi/key_small.png delete mode 100644 APG/res/drawable-hdpi/overlay_error.png delete mode 100644 APG/res/drawable-hdpi/overlay_ok.png delete mode 100644 APG/res/drawable-hdpi/signed.png delete mode 100644 APG/res/drawable-hdpi/signed_large.png delete mode 100644 APG/res/drawable-hdpi/signed_small.png delete mode 100644 APG/res/drawable-ldpi/encrypted.png delete mode 100644 APG/res/drawable-ldpi/encrypted_large.png delete mode 100644 APG/res/drawable-ldpi/encrypted_small.png delete mode 100644 APG/res/drawable-ldpi/ic_next.png delete mode 100644 APG/res/drawable-ldpi/ic_previous.png delete mode 100644 APG/res/drawable-ldpi/icon.png delete mode 100644 APG/res/drawable-ldpi/key.png delete mode 100644 APG/res/drawable-ldpi/key_large.png delete mode 100644 APG/res/drawable-ldpi/key_small.png delete mode 100644 APG/res/drawable-ldpi/overlay_error.png delete mode 100644 APG/res/drawable-ldpi/overlay_ok.png delete mode 100644 APG/res/drawable-ldpi/signed.png delete mode 100644 APG/res/drawable-ldpi/signed_large.png delete mode 100644 APG/res/drawable-ldpi/signed_small.png delete mode 100644 APG/res/drawable-mdpi-finger/btn_circle_disable.png delete mode 100644 APG/res/drawable-mdpi-finger/btn_circle_disable_focused.png delete mode 100644 APG/res/drawable-mdpi-finger/btn_circle_normal.png delete mode 100644 APG/res/drawable-mdpi-finger/btn_circle_pressed.png delete mode 100644 APG/res/drawable-mdpi-finger/btn_circle_selected.png delete mode 100644 APG/res/drawable-mdpi-finger/ic_btn_round_minus.png delete mode 100644 APG/res/drawable-mdpi-finger/ic_btn_round_plus.png delete mode 100644 APG/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 APG/res/drawable-mdpi/encrypted.png delete mode 100644 APG/res/drawable-mdpi/encrypted_large.png delete mode 100644 APG/res/drawable-mdpi/encrypted_small.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_about.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_decrypt.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_encrypt.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_filebrowser.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_scan_qrcode.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_search.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_search_list.png delete mode 100644 APG/res/drawable-mdpi/ic_menu_settings.png delete mode 100644 APG/res/drawable-mdpi/ic_next.png delete mode 100644 APG/res/drawable-mdpi/ic_previous.png delete mode 100644 APG/res/drawable-mdpi/icon.png delete mode 100644 APG/res/drawable-mdpi/key.png delete mode 100644 APG/res/drawable-mdpi/key_large.png delete mode 100644 APG/res/drawable-mdpi/key_small.png delete mode 100644 APG/res/drawable-mdpi/overlay_error.png delete mode 100644 APG/res/drawable-mdpi/overlay_ok.png delete mode 100644 APG/res/drawable-mdpi/signed.png delete mode 100644 APG/res/drawable-mdpi/signed_large.png delete mode 100644 APG/res/drawable-mdpi/signed_small.png delete mode 100644 APG/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_about.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_decrypt.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_encrypt.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_filebrowser.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_scan_qrcode.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_search.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_search_list.png delete mode 100644 APG/res/drawable-xhdpi/ic_menu_settings.png delete mode 100644 APG/res/drawable-xhdpi/icon.png delete mode 100644 APG/res/drawable/btn_circle_disable.png delete mode 100644 APG/res/drawable/btn_circle_disable_focused.png delete mode 100644 APG/res/drawable/btn_circle_normal.png delete mode 100644 APG/res/drawable/btn_circle_pressed.png delete mode 100644 APG/res/drawable/btn_circle_selected.png delete mode 100644 APG/res/drawable/dashboard_decrypt.xml delete mode 100644 APG/res/drawable/dashboard_encrypt.xml delete mode 100644 APG/res/drawable/dashboard_help.xml delete mode 100644 APG/res/drawable/dashboard_manage_keys.xml delete mode 100644 APG/res/drawable/dashboard_my_keys.xml delete mode 100644 APG/res/drawable/dashboard_scan_qrcode.xml delete mode 100644 APG/res/drawable/encrypted.png delete mode 100644 APG/res/drawable/encrypted_large.png delete mode 100644 APG/res/drawable/encrypted_small.png delete mode 100644 APG/res/drawable/ic_btn_round_minus.png delete mode 100644 APG/res/drawable/ic_btn_round_plus.png delete mode 100644 APG/res/drawable/ic_launcher_folder.png delete mode 100644 APG/res/drawable/ic_launcher_folder_small.png delete mode 100644 APG/res/drawable/ic_next.png delete mode 100644 APG/res/drawable/ic_previous.png delete mode 100644 APG/res/drawable/key.png delete mode 100644 APG/res/drawable/key_large.png delete mode 100644 APG/res/drawable/key_small.png delete mode 100644 APG/res/drawable/overlay_error.png delete mode 100644 APG/res/drawable/overlay_ok.png delete mode 100644 APG/res/drawable/section_header.xml delete mode 100644 APG/res/drawable/signed.png delete mode 100644 APG/res/drawable/signed_large.png delete mode 100644 APG/res/drawable/signed_small.png delete mode 100644 APG/res/layout/account_item.xml delete mode 100644 APG/res/layout/create_key.xml delete mode 100644 APG/res/layout/decrypt.xml delete mode 100644 APG/res/layout/edit_key.xml delete mode 100644 APG/res/layout/edit_key_key_item.xml delete mode 100644 APG/res/layout/edit_key_section.xml delete mode 100644 APG/res/layout/edit_key_user_id_item.xml delete mode 100644 APG/res/layout/encrypt.xml delete mode 100644 APG/res/layout/file_dialog.xml delete mode 100644 APG/res/layout/filter_info.xml delete mode 100644 APG/res/layout/general.xml delete mode 100644 APG/res/layout/help_activity.xml delete mode 100644 APG/res/layout/help_fragment_about.xml delete mode 100644 APG/res/layout/import_keys.xml delete mode 100644 APG/res/layout/key_list_child_item.xml delete mode 100644 APG/res/layout/key_list_group_item.xml delete mode 100644 APG/res/layout/key_list_public_activity.xml delete mode 100644 APG/res/layout/key_list_secret_activity.xml delete mode 100644 APG/res/layout/key_server_editor.xml delete mode 100644 APG/res/layout/key_server_export_layout.xml delete mode 100644 APG/res/layout/key_server_preference.xml delete mode 100644 APG/res/layout/key_server_query_layout.xml delete mode 100644 APG/res/layout/key_server_query_result_item.xml delete mode 100644 APG/res/layout/key_server_query_result_user_id.xml delete mode 100644 APG/res/layout/main.xml delete mode 100644 APG/res/layout/passphrase.xml delete mode 100644 APG/res/layout/passphrase_repeat.xml delete mode 100644 APG/res/layout/select_key_item.xml delete mode 100644 APG/res/layout/select_public_key_activity.xml delete mode 100644 APG/res/layout/select_secret_key_activity.xml delete mode 100644 APG/res/layout/share_nfc_beam.xml delete mode 100644 APG/res/layout/sign_key_layout.xml delete mode 100644 APG/res/menu/nfc_beam.xml delete mode 100644 APG/res/raw/help_about.html delete mode 100644 APG/res/raw/help_changelog.html delete mode 100644 APG/res/raw/help_nfc_beam.html delete mode 100644 APG/res/raw/help_start.html delete mode 100644 APG/res/raw/nfc_beam_share.html delete mode 100644 APG/res/values/arrays.xml delete mode 100644 APG/res/values/colors.xml delete mode 100644 APG/res/values/static_strings.xml delete mode 100644 APG/res/values/strings.xml delete mode 100644 APG/res/values/styles.xml delete mode 100644 APG/res/xml/apg_preferences.xml delete mode 100644 APG/res/xml/searchable_public_keys.xml delete mode 100644 APG/res/xml/searchable_secret_keys.xml delete mode 100644 APG/src/org/thialfihar/android/apg/ApgApplication.java delete mode 100644 APG/src/org/thialfihar/android/apg/Constants.java delete mode 100644 APG/src/org/thialfihar/android/apg/Id.java delete mode 100644 APG/src/org/thialfihar/android/apg/compatibility/ClipboardReflection.java delete mode 100644 APG/src/org/thialfihar/android/apg/compatibility/DialogFragmentWorkaround.java delete mode 100644 APG/src/org/thialfihar/android/apg/compatibility/ListFragmentWorkaround.java delete mode 100644 APG/src/org/thialfihar/android/apg/helper/FileHelper.java delete mode 100644 APG/src/org/thialfihar/android/apg/helper/OtherHelper.java delete mode 100644 APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java delete mode 100644 APG/src/org/thialfihar/android/apg/helper/PGPHelper.java delete mode 100644 APG/src/org/thialfihar/android/apg/helper/PGPMain.java delete mode 100644 APG/src/org/thialfihar/android/apg/helper/Preferences.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgContract.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgProvider.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java delete mode 100644 APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java delete mode 100644 APG/src/org/thialfihar/android/apg/service/ApgApiService.java delete mode 100644 APG/src/org/thialfihar/android/apg/service/ApgIntentService.java delete mode 100644 APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java delete mode 100644 APG/src/org/thialfihar/android/apg/service/ApgKeyService.java delete mode 100644 APG/src/org/thialfihar/android/apg/service/IApgApiService.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/service/IApgKeyService.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java delete mode 100644 APG/src/org/thialfihar/android/apg/service/handler/IApgDecryptHandler.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/service/handler/IApgEncryptHandler.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/service/handler/IApgGetDecryptionKeyIdHandler.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/service/handler/IApgGetKeyringsHandler.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/service/handler/IApgSignHandler.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/service/handler/IApgVerifyHandler.aidl delete mode 100644 APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/HelpActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/ImportKeysActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/ImportKeysListFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/MainActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/ShareActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/ShareNfcBeamActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/Editor.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/ImportKeysListLoader.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java delete mode 100644 APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/Choice.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/InputData.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/IterableIterator.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/KeyServer.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/Log.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/Primes.java delete mode 100644 APG/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java create mode 100644 OpenPGP-Keychain-API-Demo/.gitignore create mode 100644 OpenPGP-Keychain-API-Demo/AndroidManifest.xml create mode 100644 OpenPGP-Keychain-API-Demo/build.xml create mode 100644 OpenPGP-Keychain-API-Demo/proguard-project.txt create mode 100644 OpenPGP-Keychain-API-Demo/project.properties create mode 100644 OpenPGP-Keychain-API-Demo/res/drawable-hdpi/icon.png create mode 100644 OpenPGP-Keychain-API-Demo/res/drawable-ldpi/icon.png create mode 100644 OpenPGP-Keychain-API-Demo/res/drawable-mdpi/icon.png create mode 100644 OpenPGP-Keychain-API-Demo/res/drawable-xhdpi/icon.png create mode 100644 OpenPGP-Keychain-API-Demo/res/layout/aidl_demo.xml create mode 100644 OpenPGP-Keychain-API-Demo/res/layout/aidl_demo2.xml create mode 100644 OpenPGP-Keychain-API-Demo/res/layout/content_provider_demo.xml create mode 100644 OpenPGP-Keychain-API-Demo/res/layout/intent_demo.xml create mode 100644 OpenPGP-Keychain-API-Demo/res/xml/base_preference.xml create mode 100644 OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/AidlDemoActivity.java create mode 100644 OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/AidlDemoActivity2.java create mode 100644 OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/BaseActivity.java create mode 100644 OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/ContentProviderDemoActivity.java create mode 100644 OpenPGP-Keychain-API-Demo/src/org/sufficientlysecure/keychain/demo/IntentDemoActivity.java create mode 100644 OpenPGP-Keychain-API-Lib/.gitignore create mode 100644 OpenPGP-Keychain-API-Lib/AndroidManifest.xml create mode 100644 OpenPGP-Keychain-API-Lib/build.xml create mode 100644 OpenPGP-Keychain-API-Lib/libs/android-support-v4.jar create mode 100644 OpenPGP-Keychain-API-Lib/proguard-project.txt create mode 100644 OpenPGP-Keychain-API-Lib/project.properties create mode 100644 OpenPGP-Keychain-API-Lib/res/.readme create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/Constants.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/KeychainContentProviderHelper.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/KeychainData.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/KeychainIntentHelper.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/KeychainIntentHelperSupportV4.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/KeychainIntentHelperV30.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/KeychainServiceHelper.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/integration/KeychainUtil.java create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/IKeychainApiService.aidl create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/handler/IKeychainDecryptHandler.aidl create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/handler/IKeychainEncryptHandler.aidl create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetDecryptionKeyIdHandler.aidl create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/handler/IKeychainSignHandler.aidl create mode 100644 OpenPGP-Keychain-API-Lib/src/org/sufficientlysecure/keychain/service/handler/IKeychainVerifyHandler.aidl create mode 100644 OpenPGP-Keychain/.gitignore create mode 100644 OpenPGP-Keychain/AndroidManifest.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/.gitignore create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/AndroidManifest.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/README.md create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/build.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/libs/android-support-v4.jar create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/pom.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/proguard-project.txt create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/project.properties create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/color/abs__primary_text_disable_only_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/color/abs__primary_text_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/color/abs__primary_text_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_inverse_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_bottom_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_share_pack_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_solid_shadow_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_stacked_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ab_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_focused_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__btn_cab_done_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_bottom_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__cab_background_top_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__dialog_full_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_ab_back_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_cab_done_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_disabled.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_normal.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_disabled_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_clear_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_go.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_go_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_moreoverflow_normal_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_menu_share_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_search.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__ic_voice_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_activated_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_divider_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_focused_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_longpressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__list_selector_disabled_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__menu_dropdown_panel_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_bg_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_primary_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__progress_secondary_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_inner_holo.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_48_outer_holo.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_disabled_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_focused_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__spinner_ab_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_focused_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_selected_pressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__tab_unselected_pressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_right_selected_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-hdpi/abs__textfield_search_selected_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_inverse_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_bottom_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_share_pack_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_solid_shadow_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_stacked_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ab_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_focused_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__btn_cab_done_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_bottom_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__cab_background_top_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__dialog_full_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_ab_back_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_cab_done_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_disabled.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_normal.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_disabled_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_clear_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_go.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_go_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_moreoverflow_normal_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_menu_share_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_search.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__ic_voice_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_activated_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_divider_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_focused_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_longpressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__list_selector_disabled_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__menu_dropdown_panel_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_bg_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_primary_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__progress_secondary_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_inner_holo.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_48_outer_holo.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_disabled_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_focused_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__spinner_ab_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_focused_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_selected_pressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__tab_unselected_pressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_right_selected_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-mdpi/abs__textfield_search_selected_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-v11/abs__progress_medium_holo.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_inverse_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_bottom_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_share_pack_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_solid_shadow_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_stacked_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_dark_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ab_transparent_light_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_focused_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__btn_cab_done_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_bottom_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__cab_background_top_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__dialog_full_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_ab_back_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_cab_done_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_disabled.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_disabled_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_clear_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_go_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_moreoverflow_normal_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_dark.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_menu_share_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__ic_voice_search_api_holo_light.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_activated_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_divider_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_focused_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_longpressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__list_selector_disabled_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__menu_dropdown_panel_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_bg_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_primary_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__progress_secondary_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_inner_holo.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_48_outer_holo.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_disabled_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_focused_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__spinner_ab_pressed_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_focused_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_selected_pressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__tab_unselected_pressed_holo.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_default_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_right_selected_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_dark.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable-xhdpi/abs__textfield_search_selected_holo_light.9.png create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__activated_background_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__btn_cab_done_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__ic_clear.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__ic_clear_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__ic_menu_moreoverflow_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__item_background_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__item_background_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_background_transition_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__list_selector_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__progress_horizontal_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__progress_medium_holo.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__search_dropdown_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__search_dropdown_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__spinner_ab_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__tab_indicator_ab_holo.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_dark.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/drawable/abs__textfield_searchview_right_holo_light.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout-large/abs__action_mode_close_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_dropdown_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout-v14/sherlock_spinner_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout-xlarge/abs__screen_action_bar_overlay.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_bar_home.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_bar_tab.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_bar_tab_bar_view.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_bar_title_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_menu_item_layout.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_menu_layout.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_mode_bar.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__action_mode_close_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__activity_chooser_view.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__activity_chooser_view_list_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__dialog_title_holo.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_checkbox.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_icon.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_layout.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__list_menu_item_radio.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__popup_menu_item_layout.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__screen_action_bar.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__screen_action_bar_overlay.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__screen_simple.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__screen_simple_overlay_action_mode.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__search_dropdown_item_icons_2line.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__search_view.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/abs__simple_dropdown_hint.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/sherlock_spinner_dropdown_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/layout/sherlock_spinner_item.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-land/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-large-hdpi-1024x600/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-large-land-hdpi-1024x600/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-large-land-mdpi-1024x600/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-large-mdpi-1024x600/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-large/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-sw600dp/abs__bools.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-sw600dp/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-v11/abs__themes.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-v14/abs__styles.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-v14/abs__themes.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-w360dp/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-w480dp/abs__bools.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-w480dp/abs__config.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-w500dp/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-w600dp/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values-xlarge/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__attrs.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__bools.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__colors.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__config.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__dimens.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__ids.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__strings.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__styles.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/res/values/abs__themes.xml create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/android/support/v4/app/Watson.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/ActionBarSherlock.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/ActionBar.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockActivity.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockDialogFragment.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockExpandableListActivity.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragment.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockFragmentActivity.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListActivity.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockListFragment.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/app/SherlockPreferenceActivity.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockCompat.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ActionBarSherlockNative.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/ResourcesCompat.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarImpl.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/app/ActionBarWrapper.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Animator.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorListenerAdapter.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/AnimatorSet.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatEvaluator.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/FloatKeyframeSet.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntEvaluator.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/IntKeyframeSet.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/Keyframe.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/KeyframeSet.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ObjectAnimator.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/PropertyValuesHolder.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/TypeEvaluator.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/animation/ValueAnimator.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/NineViewGroup.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/view/animation/AnimatorProxy.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineFrameLayout.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineHorizontalScrollView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/nineoldandroids/widget/NineLinearLayout.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/ActionProviderWrapper.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/StandaloneActionMode.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_HasStateListenerSupport.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/View_OnAttachStateChangeListener.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenu.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItem.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuItemView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuPresenter.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ActionMenuView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/BaseMenuPresenter.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/ListMenuItemView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuBuilder.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemImpl.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuItemWrapper.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPopupHelper.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuPresenter.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/MenuWrapper.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuBuilder.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/view/menu/SubMenuWrapper.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/AbsActionBarView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContainer.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarContextView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ActionBarView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingButton.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CapitalizingTextView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/CollapsibleActionViewWrapper.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/FakeDialogPhoneWindow.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAbsSpinner.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsAdapterView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsColorDrawable.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsLinearLayout.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsListPopupWindow.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsProgressBar.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsSpinner.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/IcsView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionMode.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/ActionProvider.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/CollapsibleActionView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/Menu.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuInflater.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/MenuItem.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/SubMenu.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/view/Window.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserModel.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ActivityChooserView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/SearchView.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/ShareActionProvider.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/src/com/actionbarsherlock/widget/SuggestionsAdapter.java create mode 100644 OpenPGP-Keychain/android-libs/ActionBarSherlock/test/com/actionbarsherlock/internal/ManifestParsingTest.java create mode 100644 OpenPGP-Keychain/build.xml create mode 100644 OpenPGP-Keychain/libs/barcodescanner-android-integration-supportv4.jar create mode 100644 OpenPGP-Keychain/libs/htmlcleaner-2.2.jar create mode 100644 OpenPGP-Keychain/libs/htmlspanner-0.2-fork.jar create mode 100644 OpenPGP-Keychain/libs/sc-bzip2-1.47.0.2.jar create mode 100644 OpenPGP-Keychain/libs/sc-light-jdk15on-1.47.0.2.jar create mode 100644 OpenPGP-Keychain/libs/scpg-jdk15on-1.47.0.2.jar create mode 100644 OpenPGP-Keychain/libs/scprov-jdk15on-1.47.0.2.jar create mode 100644 OpenPGP-Keychain/pom.xml create mode 100644 OpenPGP-Keychain/proguard-project.txt create mode 100644 OpenPGP-Keychain/project.properties create mode 100644 OpenPGP-Keychain/res/anim/push_left_in.xml create mode 100644 OpenPGP-Keychain/res/anim/push_left_out.xml create mode 100644 OpenPGP-Keychain/res/anim/push_right_in.xml create mode 100644 OpenPGP-Keychain/res/anim/push_right_out.xml create mode 100644 OpenPGP-Keychain/res/drawable-finger/btn_circle.xml create mode 100644 OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable_focused.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_normal.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_selected.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_minus.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_plus.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_scan_qrcode_default.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/dashboard_scan_qrcode_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/encrypted.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/encrypted_large.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/encrypted_small.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_about.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_decrypt.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_encrypt.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_scan_qrcode.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_search.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_search_list.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_menu_settings.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_next.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/ic_previous.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/icon.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/key.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/key_large.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/key_small.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/overlay_error.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/overlay_ok.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/signed.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/signed_large.png create mode 100644 OpenPGP-Keychain/res/drawable-hdpi/signed_small.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/encrypted.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/encrypted_large.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/encrypted_small.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/ic_next.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/ic_previous.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/icon.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/key.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/key_large.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/key_small.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/overlay_error.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/overlay_ok.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/signed.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/signed_large.png create mode 100644 OpenPGP-Keychain/res/drawable-ldpi/signed_small.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable_focused.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_normal.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_selected.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_minus.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_plus.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/encrypted.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/encrypted_large.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/encrypted_small.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_about.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_decrypt.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_encrypt.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_scan_qrcode.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_search.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_search_list.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_menu_settings.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_next.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/ic_previous.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/icon.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/key.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/key_large.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/key_small.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/overlay_error.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/overlay_ok.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/signed.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/signed_large.png create mode 100644 OpenPGP-Keychain/res/drawable-mdpi/signed_small.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/abs__ab_bottom_solid_light_holo.9.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_about.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_decrypt.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_encrypt.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_scan_qrcode.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_search.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_search_list.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_settings.png create mode 100644 OpenPGP-Keychain/res/drawable-xhdpi/icon.png create mode 100644 OpenPGP-Keychain/res/drawable/btn_circle_disable.png create mode 100644 OpenPGP-Keychain/res/drawable/btn_circle_disable_focused.png create mode 100644 OpenPGP-Keychain/res/drawable/btn_circle_normal.png create mode 100644 OpenPGP-Keychain/res/drawable/btn_circle_pressed.png create mode 100644 OpenPGP-Keychain/res/drawable/btn_circle_selected.png create mode 100644 OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml create mode 100644 OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml create mode 100644 OpenPGP-Keychain/res/drawable/dashboard_help.xml create mode 100644 OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml create mode 100644 OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml create mode 100644 OpenPGP-Keychain/res/drawable/dashboard_scan_qrcode.xml create mode 100644 OpenPGP-Keychain/res/drawable/encrypted.png create mode 100644 OpenPGP-Keychain/res/drawable/encrypted_large.png create mode 100644 OpenPGP-Keychain/res/drawable/encrypted_small.png create mode 100644 OpenPGP-Keychain/res/drawable/ic_btn_round_minus.png create mode 100644 OpenPGP-Keychain/res/drawable/ic_btn_round_plus.png create mode 100644 OpenPGP-Keychain/res/drawable/ic_launcher_folder.png create mode 100644 OpenPGP-Keychain/res/drawable/ic_launcher_folder_small.png create mode 100644 OpenPGP-Keychain/res/drawable/ic_next.png create mode 100644 OpenPGP-Keychain/res/drawable/ic_previous.png create mode 100644 OpenPGP-Keychain/res/drawable/key.png create mode 100644 OpenPGP-Keychain/res/drawable/key_large.png create mode 100644 OpenPGP-Keychain/res/drawable/key_small.png create mode 100644 OpenPGP-Keychain/res/drawable/overlay_error.png create mode 100644 OpenPGP-Keychain/res/drawable/overlay_ok.png create mode 100644 OpenPGP-Keychain/res/drawable/section_header.xml create mode 100644 OpenPGP-Keychain/res/drawable/signed.png create mode 100644 OpenPGP-Keychain/res/drawable/signed_large.png create mode 100644 OpenPGP-Keychain/res/drawable/signed_small.png create mode 100644 OpenPGP-Keychain/res/layout/account_item.xml create mode 100644 OpenPGP-Keychain/res/layout/create_key.xml create mode 100644 OpenPGP-Keychain/res/layout/decrypt.xml create mode 100644 OpenPGP-Keychain/res/layout/edit_key.xml create mode 100644 OpenPGP-Keychain/res/layout/edit_key_key_item.xml create mode 100644 OpenPGP-Keychain/res/layout/edit_key_section.xml create mode 100644 OpenPGP-Keychain/res/layout/edit_key_user_id_item.xml create mode 100644 OpenPGP-Keychain/res/layout/encrypt.xml create mode 100644 OpenPGP-Keychain/res/layout/file_dialog.xml create mode 100644 OpenPGP-Keychain/res/layout/filter_info.xml create mode 100644 OpenPGP-Keychain/res/layout/general.xml create mode 100644 OpenPGP-Keychain/res/layout/help_activity.xml create mode 100644 OpenPGP-Keychain/res/layout/help_fragment_about.xml create mode 100644 OpenPGP-Keychain/res/layout/import_keys.xml create mode 100644 OpenPGP-Keychain/res/layout/key_list_child_item.xml create mode 100644 OpenPGP-Keychain/res/layout/key_list_group_item.xml create mode 100644 OpenPGP-Keychain/res/layout/key_list_public_activity.xml create mode 100644 OpenPGP-Keychain/res/layout/key_list_secret_activity.xml create mode 100644 OpenPGP-Keychain/res/layout/key_server_editor.xml create mode 100644 OpenPGP-Keychain/res/layout/key_server_export_layout.xml create mode 100644 OpenPGP-Keychain/res/layout/key_server_preference.xml create mode 100644 OpenPGP-Keychain/res/layout/key_server_query_layout.xml create mode 100644 OpenPGP-Keychain/res/layout/key_server_query_result_item.xml create mode 100644 OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml create mode 100644 OpenPGP-Keychain/res/layout/main.xml create mode 100644 OpenPGP-Keychain/res/layout/passphrase.xml create mode 100644 OpenPGP-Keychain/res/layout/passphrase_repeat.xml create mode 100644 OpenPGP-Keychain/res/layout/select_key_item.xml create mode 100644 OpenPGP-Keychain/res/layout/select_public_key_activity.xml create mode 100644 OpenPGP-Keychain/res/layout/select_secret_key_activity.xml create mode 100644 OpenPGP-Keychain/res/layout/share_nfc_beam.xml create mode 100644 OpenPGP-Keychain/res/layout/sign_key_layout.xml create mode 100644 OpenPGP-Keychain/res/menu/nfc_beam.xml create mode 100644 OpenPGP-Keychain/res/raw/help_about.html create mode 100644 OpenPGP-Keychain/res/raw/help_changelog.html create mode 100644 OpenPGP-Keychain/res/raw/help_nfc_beam.html create mode 100644 OpenPGP-Keychain/res/raw/help_start.html create mode 100644 OpenPGP-Keychain/res/raw/nfc_beam_share.html create mode 100644 OpenPGP-Keychain/res/values/arrays.xml create mode 100644 OpenPGP-Keychain/res/values/colors.xml create mode 100644 OpenPGP-Keychain/res/values/static_strings.xml create mode 100644 OpenPGP-Keychain/res/values/strings.xml create mode 100644 OpenPGP-Keychain/res/values/styles.xml create mode 100644 OpenPGP-Keychain/res/xml/preferences.xml create mode 100644 OpenPGP-Keychain/res/xml/searchable_public_keys.xml create mode 100644 OpenPGP-Keychain/res/xml/searchable_secret_keys.xml create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Constants.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/KeychainApplication.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ListFragmentWorkaround.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/FileHelper.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/Preferences.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderExternal.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderInternal.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainApiService.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainApiService.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainDecryptHandler.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainEncryptHandler.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetDecryptionKeyIdHandler.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainSignHandler.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainVerifyHandler.aidl create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentHtml.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/Editor.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ExpandableListFragment.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Choice.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/InputData.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/IterableIterator.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/KeyServer.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Log.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Primes.java create mode 100644 OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/ProgressDialogUpdater.java diff --git a/APG-API-Demo/.gitignore b/APG-API-Demo/.gitignore deleted file mode 100644 index 2e423e1a3..000000000 --- a/APG-API-Demo/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -#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/APG-API-Demo/AndroidManifest.xml b/APG-API-Demo/AndroidManifest.xml deleted file mode 100644 index 812b5d45e..000000000 --- a/APG-API-Demo/AndroidManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/APG-API-Demo/build.xml b/APG-API-Demo/build.xml deleted file mode 100644 index e72865c84..000000000 --- a/APG-API-Demo/build.xml +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/APG-API-Demo/proguard-project.txt b/APG-API-Demo/proguard-project.txt deleted file mode 100644 index f2fe1559a..000000000 --- a/APG-API-Demo/proguard-project.txt +++ /dev/null @@ -1,20 +0,0 @@ -# To enable ProGuard in your project, edit project.properties -# to define the proguard.config property as described in that file. -# -# Add project specific ProGuard rules here. -# By default, the flags in this file are appended to flags specified -# in ${sdk.dir}/tools/proguard/proguard-android.txt -# You can edit the include path and order by changing the ProGuard -# include property in project.properties. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} diff --git a/APG-API-Demo/project.properties b/APG-API-Demo/project.properties deleted file mode 100644 index ea95f1b11..000000000 --- a/APG-API-Demo/project.properties +++ /dev/null @@ -1,12 +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 -android.library.reference.1=../APG-API-Lib diff --git a/APG-API-Demo/res/drawable-hdpi/icon.png b/APG-API-Demo/res/drawable-hdpi/icon.png deleted file mode 100644 index 6b8cc3d73..000000000 Binary files a/APG-API-Demo/res/drawable-hdpi/icon.png and /dev/null differ diff --git a/APG-API-Demo/res/drawable-ldpi/icon.png b/APG-API-Demo/res/drawable-ldpi/icon.png deleted file mode 100644 index a1adf6bcb..000000000 Binary files a/APG-API-Demo/res/drawable-ldpi/icon.png and /dev/null differ diff --git a/APG-API-Demo/res/drawable-mdpi/icon.png b/APG-API-Demo/res/drawable-mdpi/icon.png deleted file mode 100644 index 6b10a2ad3..000000000 Binary files a/APG-API-Demo/res/drawable-mdpi/icon.png and /dev/null differ diff --git a/APG-API-Demo/res/drawable-xhdpi/icon.png b/APG-API-Demo/res/drawable-xhdpi/icon.png deleted file mode 100644 index 03ee31bbd..000000000 Binary files a/APG-API-Demo/res/drawable-xhdpi/icon.png and /dev/null differ diff --git a/APG-API-Demo/res/layout/aidl_demo.xml b/APG-API-Demo/res/layout/aidl_demo.xml deleted file mode 100644 index 59977869d..000000000 --- a/APG-API-Demo/res/layout/aidl_demo.xml +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - diff --git a/APG/res/menu/nfc_beam.xml b/APG/res/menu/nfc_beam.xml deleted file mode 100644 index 9cb86892d..000000000 --- a/APG/res/menu/nfc_beam.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/APG/res/raw/help_about.html b/APG/res/raw/help_about.html deleted file mode 100644 index 1b1f0b52e..000000000 --- a/APG/res/raw/help_about.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - -

https://github.com/dschuermann/apg

-

Android Privacy Guard (APG) is an OpenPGP implementation for Android.

-

License: Apache License v2

- -

Developer

-
    -
  • Dominik Schürmann (Developer v2.x)
  • -
  • Oliver Runge
  • -
  • Markus Doits
  • -
  • Senecaso (QRCode, sign key, upload key)
  • -
  • Thialfihar (Main developer v1.x)
  • -
- -

Libraries

- - - \ No newline at end of file diff --git a/APG/res/raw/help_changelog.html b/APG/res/raw/help_changelog.html deleted file mode 100644 index fa1201cc1..000000000 --- a/APG/res/raw/help_changelog.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - -

2.0

-
    -
  • Complete redesign
  • -
  • Share public keys via qr codes, nfc beam
  • -
  • Sign keys
  • -
  • Upload keys to server
  • -
  • Fixes import issues
  • -
  • New AIDL API
  • -
- -

1.08

-
    -
  • basic key server support
  • -
  • app2sd (untested, let me know if there are problems)
  • -
  • more choices for pass phrase cache: 1, 2, 4, 8, hours
  • -
  • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
  • -
  • bugfixes
  • -
  • optimizations
  • -
- -

1.0.7

-
    -
  • clear sign problem with lacking trailing newline fixed
  • -
  • more options for pass phrase cache time to live (20, 40, 60 mins)
  • -
- -

1.0.6

-
    -
  • account adding crash on Froyo fixed
  • -
  • secure file deletion
  • -
  • option to delete key file after import
  • -
  • stream encryption/decryption (gallery, etc.)
  • -
  • new options (language, force v3 signatures)
  • -
  • interface changes
  • -
  • bugfixes
  • -
- -

1.0.5

-
    -
  • German and Italian translation
  • -
  • much smaller package, due to reduced BC sources
  • -
  • new preferences GUI
  • -
  • layout adjustment for localization
  • -
  • signature bugfix
  • -
- -

1.0.4

-
    -
  • fixed another crash caused by some SDK bug with query builder
  • -
- -

1.0.3

-
    -
  • fixed crashes during encryption/signing and possibly key export
  • -
- -

1.0.2

-
    -
  • filterable key lists
  • -
  • smarter preselection of encryption keys
  • -
  • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
  • -
  • fixes and additional features (key preselection) for k9, new beta build available
  • -
- -

1.0.1

-
    -
  • GMail account listing was broken in 1.0.0, fixed again
  • -
- -

1.0.0

-
    -
  • k9mail integration, APG supporting beta build of k9mail
  • -
  • support of more file managers (including ASTRO)
  • -
  • Slovenian translation
  • -
  • new database, much faster, less memory usage
  • -
  • defined Intents and content provider for other apps
  • -
  • bugfixes
  • -
- - \ No newline at end of file diff --git a/APG/res/raw/help_nfc_beam.html b/APG/res/raw/help_nfc_beam.html deleted file mode 100644 index aa510008c..000000000 --- a/APG/res/raw/help_nfc_beam.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - -

How to receive keyrings

-
    -
  1. Go to your partners 'Manage Public Keyrings' and long press on the keyring you want to share.
  2. -
  3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
  4. -
  5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
  6. -
  7. Tap the card and the content will then load on the your device.
  8. -
- - \ No newline at end of file diff --git a/APG/res/raw/help_start.html b/APG/res/raw/help_start.html deleted file mode 100644 index db7559632..000000000 --- a/APG/res/raw/help_start.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - -

Beta software

-

This is beta software. It contains many remaining bugs!

- -

APG

-

Android Privacy Guard (APG) is a OpenPGP implementation for Android. -
This is a fork based on the original APG to introduce more features and a new user interface.

- -

WARNING

-

Be careful editing your existing keys, as they WILL be stripped of certificates right now. -

Also: key cross-certification is NOT supported, so signing with those keys will get a warning when the signature is checked.

- -

Getting started

-

Install K-9 Mail for the best integration, it supports APG for PGP/INLINE and lets you directly encrypt/decrypt emails. -
It is recommended that you install OI File Manager to be able to use the browse button for file selection in APG. -
First you need some keys. Either import them via the option menus in "Manage Keys" and "My Keys" or create them in "My Keys".

- -

I found a bug in APG!

-

Please report it in the issue tracker of APG.

- - \ No newline at end of file diff --git a/APG/res/raw/nfc_beam_share.html b/APG/res/raw/nfc_beam_share.html deleted file mode 100644 index 734e69fc5..000000000 --- a/APG/res/raw/nfc_beam_share.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - -
    -
  1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
  2. -
  3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
  4. -
  5. After it vibrates you’ll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
  6. -
  7. Tap the card and the content will then load on the other person’s device.
  8. -
- - \ No newline at end of file diff --git a/APG/res/values/arrays.xml b/APG/res/values/arrays.xml deleted file mode 100644 index 05320c609..000000000 --- a/APG/res/values/arrays.xml +++ /dev/null @@ -1,47 +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 - - - \ No newline at end of file diff --git a/APG/res/values/colors.xml b/APG/res/values/colors.xml deleted file mode 100644 index d1dc6c1f4..000000000 --- a/APG/res/values/colors.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - #31b6e7 - #cecbce - - \ No newline at end of file diff --git a/APG/res/values/static_strings.xml b/APG/res/values/static_strings.xml deleted file mode 100644 index c69cf8adf..000000000 --- a/APG/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/APG/res/values/strings.xml b/APG/res/values/strings.xml deleted file mode 100644 index c7d25ad44..000000000 --- a/APG/res/values/strings.xml +++ /dev/null @@ -1,361 +0,0 @@ - - - - - - - Mail Inbox - Manage Public Keyrings - Manage Secret Keyrings - Select Public Keyring - Select Secret Keyring - Encrypt - Decrypt - Passphrase - Create Key - Edit Keyring - Create Keyring - Preferences - Key Server Preference - Change Passphrase - Set Passphrase - "Send Mail…" - Encrypt To File - Decrypt To File - Import Keyrings - Export Keyring - Export Keyrings - Key Not Found - Getting Started - Query Key Server - Export to Key Server - Unknown Signature Key - Sign Keyring - About - Help - Share keyring with NFC - - - User IDs - Keys - General - Defaults - Advanced - - - Sign (Clipboard) - Encrypt (Clipboard) - Encrypt (Email) - Sign (Email) - Encrypt - Sign - Decrypt - Verify - Select Recipients - Reply - Encrypt Message - Decrypt Message - Encrypt File - Decrypt File - Save - Cancel - Delete - None - Okay - Clear Filter - Change Passphrase - Set Passphrase - Search - Export To Server - - - About - Delete Account - Manage Public Keyrings - Manage Secret Keyrings - Settings - Import from file - Import from QR Code - Import from NFC - Export All Keyrings - Export To File - Delete Keyring - Create Keyring - Edit Keyring - Search - Help - Query Key Server - Update from Server - Export To Server - Share with… - Share with QR Code - Share with NFC - Scan QR Code - Sign Key - Beam Settings - - - Sign - Message - File - No Passphrase - Passphrase - Again - Algorithm - ASCII Armor - Public Key(s) - Delete After Encryption - Delete After Decryption - Delete After Import - Encryption Algorithm - Hash Algorithm - Public Key - Passphrase - Passphrase Cache - Message Compression - File Compression - Force V3 Signatures - Key Servers - Key ID - Creation - Expiry - Usage - Key Size - Main User ID - Name - Comment - Email - Send Keyring to Server? - Select - 1 Selected - Selected - <unknown> - <none> - <no key> - - - <no expiry> - - can encrypt - can sign - expired - not valid - %s key server(s) - Fingerprint: - Secret Keyring: - - - 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 passphrase. - Using clipboard content. - Key saved. - Set a passphrase first. - No compatible file manager installed. - The passphrases didn\'t match. - Empty passphrases are not allowed. - Symmetric encryption. - Enter passphrase for \'%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 passphrase twice. - Select at least one encryption keyring. - Select at least one encryption keyring or a signature keyring. - 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. - Please specify which file to import keyrings 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 keyrings.\nWARNING! File will be overwritten if it exists. - Do you really want to delete the keyring \'%s\'?\nYou can\'t undo this! - Do you really want to delete the SECRET keyring \'%s\'?\nYou can\'t undo this! - Successfully added %1$s keyring(s) and updated %2$s keyring(s). - Successfully added %s keyring(s). - Successfully updated %s keyring(s). - No keyrings added or updated. - Successfully exported 1 keyring. - Successfully exported %s keyrings. - No keyrings 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. - %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 keyring %s, do you want to try finding it on a keyserver? - Successfully sent keyring to server - Successfully signed keyring - Successfully validated and imported keyring - Long press one entry in this list to show more options! - This list is empty! - Successfully sent keyring with NFC Beam! - - - deleting \'%s\' failed - file not found - no suitable secret key found - no known kind of encryption found - external storage not ready - 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 passphrase given - signature failed - no passphrase given - no signature key given - not valid encryption data - corrupt data - couldn\'t find a packet with symmetric encryption - wrong passphrase - error saving some key(s) - could not extract private key - Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN. - You need Android 4.1 alias Jelly Bean to use Androids NFC Beam feature! - NFC is not available on your device! - Nothing to import! - - - 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 keyring… - importing secret keyrings… - importing public keyrings… - exporting keyring… - exporting keyrings… - 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… - - - APG - Permissions to use APG - Encrypt/Sign/Decrypt/Create keys without user interaction, Read key details of public and secret keys (The keys themselves can NOT be read.) - Encrypt/Sign/Decrypt/Create keys (by using Intents or Remote Service) without user interaction. 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. - Import/Export actual public and secret keys - Import and export actual private and public keys directly without user uinteraction. - - - Encrypt - Decrypt - Import Public Keyrings - Import Secret Keyrings - Search Public Keyrings - Search Secret Keyrings - Filter: \"%s\" - Share keyring with… - - - fast - slow - very slow - - - - - - Manage Public Keyrings - My Secret Keyrings - Encrypt - Decrypt - Help - Import Keys - - - Start - NFC Beam - Changelog - About - Version: - - - Import keyring(s) (only locally) - Import, Sign, and upload keyring(s) - Finish - - - APG: Decrypt File - APG: Import Keyring - APG: Encrypt - APG: Decrypt - - \ No newline at end of file diff --git a/APG/res/values/styles.xml b/APG/res/values/styles.xml deleted file mode 100644 index ef3ff63f6..000000000 --- a/APG/res/values/styles.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/APG/res/xml/apg_preferences.xml b/APG/res/xml/apg_preferences.xml deleted file mode 100644 index 778f51375..000000000 --- a/APG/res/xml/apg_preferences.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/APG/res/xml/searchable_public_keys.xml b/APG/res/xml/searchable_public_keys.xml deleted file mode 100644 index e9602b121..000000000 --- a/APG/res/xml/searchable_public_keys.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/APG/res/xml/searchable_secret_keys.xml b/APG/res/xml/searchable_secret_keys.xml deleted file mode 100644 index a7e8873d6..000000000 --- a/APG/res/xml/searchable_secret_keys.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ApgApplication.java b/APG/src/org/thialfihar/android/apg/ApgApplication.java deleted file mode 100644 index f89375079..000000000 --- a/APG/src/org/thialfihar/android/apg/ApgApplication.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg; - -import java.io.File; -import java.security.Security; - -import org.spongycastle.jce.provider.BouncyCastleProvider; - -import android.app.Application; -import android.os.Environment; - -public class ApgApplication extends Application { - - static { - // Define Java Security Provider to be Bouncy Castle - Security.addProvider(new BouncyCastleProvider()); - } - - @Override - public void onCreate() { - super.onCreate(); - - // Create APG directory on sdcard if not existing - 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 - } - } - } - -} diff --git a/APG/src/org/thialfihar/android/apg/Constants.java b/APG/src/org/thialfihar/android/apg/Constants.java deleted file mode 100644 index 8a52523d7..000000000 --- a/APG/src/org/thialfihar/android/apg/Constants.java +++ /dev/null @@ -1,69 +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.thialfihar.android.apg; - -import android.os.Environment; - -public final class Constants { - - public static final boolean DEBUG = true; - - public static final String TAG = "APG"; - - public static final String PACKAGE_NAME = "org.thialfihar.android.apg"; - - public static final String NFC_MIME = "application/org.thialfihar.android.apg"; - - - public static final String PERMISSION_ACCESS_KEY_DATABASE = PACKAGE_NAME - + ".permission.ACCESS_KEY_DATABASE"; - public static final String PERMISSION_ACCESS_API = PACKAGE_NAME + ".permission.ACCESS_API"; - - /* - * TODO: - * - * this would require patching K9Mail - * - * better naming scheme would be: - * - * - x.action.DECRYPT (with action as preferred in Android) - * - * - even better and shorter (without .android.): org.apg.action.DECRYPT - */ - public static final String INTENT_PREFIX = PACKAGE_NAME + ".intent."; - - public static final class path { - public static final String APP_DIR = Environment.getExternalStorageDirectory() + "/APG"; - } - - public static final class pref { - 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"; - } - -} diff --git a/APG/src/org/thialfihar/android/apg/Id.java b/APG/src/org/thialfihar/android/apg/Id.java deleted file mode 100644 index db283da37..000000000 --- a/APG/src/org/thialfihar/android/apg/Id.java +++ /dev/null @@ -1,231 +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.thialfihar.android.apg; - -import org.spongycastle.bcpg.CompressionAlgorithmTags; - -/** - * - * TODO: - * - * - refactor ids, some are not needed and can be done with xml - * - */ -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 share_qr_code = 0x21070007; - public static final int share_nfc = 0x21070008; - public static final int signKey = 0x21070009; - - 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 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 int encrypt = 0x21070013; - public static final int encrypt_to_clipboard = 0x21070014; - public static final int decrypt = 0x21070015; - public static final int reply = 0x21070016; - public static final int cancel = 0x21070017; - public static final int save = 0x21070018; - public static final int okay = 0x21070019; - public static final int import_from_file = 0x21070020; - public static final int import_from_qr_code = 0x21070021; - public static final int import_from_nfc = 0x21070022; - } - } - - // use only lower 16 bits due to compatibility lib - public static final class message { - public static final int progress_update = 0x00006001; - public static final int done = 0x00006002; - public static final int import_keys = 0x00006003; - public static final int export_keys = 0x00006004; - public static final int import_done = 0x00006005; - public static final int export_done = 0x00006006; - public static final int create_key = 0x00006007; - public static final int edit_key = 0x00006008; - public static final int delete_done = 0x00006009; - public static final int query_done = 0x00006010; - public static final int unknown_signature_key = 0x00006011; - } - - // 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; - // } - - // use only lower 16 bits due to compatibility lib - public static final class request { - public static final int public_keys = 0x00007001; - public static final int secret_keys = 0x00007002; - public static final int filename = 0x00007003; - public static final int output_filename = 0x00007004; - public static final int key_server_preference = 0x00007005; - public static final int look_up_key_id = 0x00007006; - public static final int export_to_server = 0x00007007; - public static final int import_from_qr_code = 0x00007008; - public static final int sign_key = 0x00007009; - } - - // 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/APG/src/org/thialfihar/android/apg/compatibility/ClipboardReflection.java b/APG/src/org/thialfihar/android/apg/compatibility/ClipboardReflection.java deleted file mode 100644 index 7894d48ab..000000000 --- a/APG/src/org/thialfihar/android/apg/compatibility/ClipboardReflection.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.compatibility; - -import java.lang.reflect.Method; - -import android.content.Context; -import org.thialfihar.android.apg.util.Log; - -public class ClipboardReflection { - - private static final String clipboardLabel = "APG"; - - /** - * Wrapper around ClipboardManager based on Android version using Reflection API - * - * @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 methodSetText = clipboard.getClass() - .getMethod("setText", CharSequence.class); - methodSetText.invoke(clipboard, text); - } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) { - Class classClipData = Class.forName("android.content.ClipData"); - Method methodNewPlainText = classClipData.getMethod("newPlainText", - CharSequence.class, CharSequence.class); - Object clip = methodNewPlainText.invoke(null, clipboardLabel, text); - methodNewPlainText = clipboard.getClass() - .getMethod("setPrimaryClip", classClipData); - methodNewPlainText.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 methodGetText = clipboard.getClass().getMethod("getText"); - Object text = methodGetText.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", int.class); - 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/APG/src/org/thialfihar/android/apg/compatibility/DialogFragmentWorkaround.java b/APG/src/org/thialfihar/android/apg/compatibility/DialogFragmentWorkaround.java deleted file mode 100644 index 6f03ad30a..000000000 --- a/APG/src/org/thialfihar/android/apg/compatibility/DialogFragmentWorkaround.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.compatibility; - -import android.os.Build; -import android.os.Handler; - -/** - * Bug on Android >= 4.2 - * - * http://code.google.com/p/android/issues/detail?id=41901 - * - * DialogFragment disappears on pressing home and comming back. This also happens especially in - * FileDialogFragment after launching a file manager and coming back. - * - * Usage: - * DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - * public void run() { - * // show dialog... - * } - * }); - * - */ -public class DialogFragmentWorkaround { - public static final SDKLevel17Interface INTERFACE = ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ? new SDKLevel17Impl() - : new SDKLevelPriorLevel17Impl()); - - private static final int RUNNABLE_DELAY = 300; - - public interface SDKLevel17Interface { - // Workaround for http://code.google.com/p/android/issues/detail?id=41901 - void runnableRunDelayed(Runnable runnable); - } - - private static class SDKLevelPriorLevel17Impl implements SDKLevel17Interface { - @Override - public void runnableRunDelayed(Runnable runnable) { - runnable.run(); - } - } - - private static class SDKLevel17Impl implements SDKLevel17Interface { - @Override - public void runnableRunDelayed(Runnable runnable) { - new Handler().postDelayed(runnable, RUNNABLE_DELAY); - } - } - - // Can't instantiate this class - private DialogFragmentWorkaround() { - } -} diff --git a/APG/src/org/thialfihar/android/apg/compatibility/ListFragmentWorkaround.java b/APG/src/org/thialfihar/android/apg/compatibility/ListFragmentWorkaround.java deleted file mode 100644 index b295aa3ba..000000000 --- a/APG/src/org/thialfihar/android/apg/compatibility/ListFragmentWorkaround.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.compatibility; - -import android.view.View; -import android.widget.ListView; - -import com.actionbarsherlock.app.SherlockListFragment; - -/** - * Bug on Android >= 4.1 - * - * http://code.google.com/p/android/issues/detail?id=35885 - * - * Items are not checked in layout - */ -public class ListFragmentWorkaround extends SherlockListFragment { - - @Override - public void onListItemClick(ListView l, View v, int position, long id) { - l.setItemChecked(position, l.isItemChecked(position)); - } -} diff --git a/APG/src/org/thialfihar/android/apg/helper/FileHelper.java b/APG/src/org/thialfihar/android/apg/helper/FileHelper.java deleted file mode 100644 index c5fb2a139..000000000 --- a/APG/src/org/thialfihar/android/apg/helper/FileHelper.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.helper; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.util.Log; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Environment; -import android.widget.Toast; - -public class FileHelper { - - /** - * Checks if external storage is mounted if file is located on external storage - * - * @param file - * @return true if storage is mounted - */ - public static boolean isStorageMounted(String file) { - if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - return false; - } - } - - return true; - } - - /** - * Opens the preferred installed file manager on Android and shows a toast if no manager is - * installed. - * - * @param activity - * @param filename - * default selected file, not supported by all file managers - * @param type - * can be text/plain for example - * @param requestCode - * requestCode used to identify the result coming back from file manager to - * onActivityResult() in your activity - */ - public static void openFile(Activity activity, String filename, String type, int requestCode) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - - intent.setData(Uri.parse("file://" + filename)); - intent.setType(type); - - try { - activity.startActivityForResult(intent, requestCode); - } catch (ActivityNotFoundException e) { - // No compatible file manager was found. - Toast.makeText(activity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show(); - } - } - - /** - * Get a file path from a Uri. - * - * from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/ - * afilechooser/utils/FileUtils.java - * - * @param context - * @param uri - * @return - * - * @author paulburke - */ - public static String getPath(Context context, Uri uri) { - Log.d(Constants.TAG + " File -", - "Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment() - + ", Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme: " - + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: " - + uri.getPathSegments().toString()); - - if ("content".equalsIgnoreCase(uri.getScheme())) { - String[] projection = { "_data" }; - Cursor cursor = null; - - try { - cursor = context.getContentResolver().query(uri, projection, null, null, null); - int column_index = cursor.getColumnIndexOrThrow("_data"); - if (cursor.moveToFirst()) { - return cursor.getString(column_index); - } - } catch (Exception e) { - // Eat it - } - } - - else if ("file".equalsIgnoreCase(uri.getScheme())) { - return uri.getPath(); - } - - return null; - } -} diff --git a/APG/src/org/thialfihar/android/apg/helper/OtherHelper.java b/APG/src/org/thialfihar/android/apg/helper/OtherHelper.java deleted file mode 100644 index 005f7cf29..000000000 --- a/APG/src/org/thialfihar/android/apg/helper/OtherHelper.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.helper; - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.Iterator; -import java.util.Set; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.util.Log; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; - -import android.app.Activity; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.widget.Toast; - -public class OtherHelper { - - /** - * Return the number if days between two dates - * - * @param first - * @param second - * @return number of days - */ - public 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; - } - - /** - * Logs bundle content to debug for inspecting the content - * - * @param bundle - * @param bundleName - */ - public static void logDebugBundle(Bundle bundle, String bundleName) { - if (Constants.DEBUG) { - if (bundle != null) { - Set ks = bundle.keySet(); - Iterator iterator = ks.iterator(); - - Log.d(Constants.TAG, "Bundle " + bundleName + ":"); - Log.d(Constants.TAG, "------------------------------"); - while (iterator.hasNext()) { - String key = iterator.next(); - Object value = bundle.get(key); - - if (value != null) { - Log.d(Constants.TAG, key + " : " + value.toString()); - } else { - Log.d(Constants.TAG, key + " : null"); - } - } - Log.d(Constants.TAG, "------------------------------"); - } else { - Log.d(Constants.TAG, "Bundle " + bundleName + ": null"); - } - } - } - - /** - * Set actionbar without home button if called from another app - * - * @param activity - */ - public static void setActionBarBackButton(SherlockFragmentActivity activity) { - // set actionbar without home button if called from another app - final ActionBar actionBar = activity.getSupportActionBar(); - Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)=" - + activity.getCallingPackage()); - if (activity.getCallingPackage() != null - && activity.getCallingPackage().equals(Constants.PACKAGE_NAME)) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - } else { - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - } - } - - /** - * Check if the calling package has the needed permission to invoke an intent with specific - * restricted actions. - * - * If pkgName is null, this will also deny the use of the given action - * - * @param activity - * @param pkgName - * @param permName - * @param action - * @param restrictedActions - */ - public static void checkPackagePermissionForActions(Activity activity, String pkgName, - String permName, String action, String[] restrictedActions) { - if (action != null) { - PackageManager pkgManager = activity.getPackageManager(); - - for (int i = 0; i < restrictedActions.length; i++) { - if (restrictedActions[i].equals(action)) { - if (pkgName != null - && (pkgManager.checkPermission(permName, pkgName) == PackageManager.PERMISSION_GRANTED || pkgName - .equals(Constants.PACKAGE_NAME))) { - Log.d(Constants.TAG, pkgName + " has permission " + permName + ". Action " - + action + " was granted!"); - } else { - String error = pkgName + " does NOT have permission " + permName - + ". Action " + action + " was NOT granted!"; - Log.e(Constants.TAG, error); - Toast.makeText(activity, activity.getString(R.string.errorMessage, error), - Toast.LENGTH_LONG).show(); - - // end activity - activity.setResult(Activity.RESULT_CANCELED, null); - activity.finish(); - } - } - } - } - } - - /** - * Splits userId string into naming part and email part - * - * @param userId - * @return array with naming (0) and email (1) - */ - public static String[] splitUserId(String userId) { - String[] output = new String[2]; - - String chunks[] = userId.split(" <", 2); - userId = chunks[0]; - if (chunks.length > 1) { - output[1] = "<" + chunks[1]; - } - output[0] = userId; - - return output; - } - -} diff --git a/APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java b/APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java deleted file mode 100644 index 35bbac08e..000000000 --- a/APG/src/org/thialfihar/android/apg/helper/PGPConversionHelper.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.helper; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Iterator; - -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPObjectFactory; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.spongycastle.openpgp.PGPUtil; -import org.thialfihar.android.apg.Constants; - -import org.thialfihar.android.apg.util.Log; - -public class PGPConversionHelper { - - /** - * Convert from byte[] to PGPKeyRing - * - * @param keysBytes - * @return - */ - public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) { - PGPObjectFactory factory = new PGPObjectFactory(keysBytes); - PGPKeyRing keyRing = null; - try { - if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) { - Log.e(Constants.TAG, "No keys given!"); - } - } catch (IOException e) { - Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e); - } - - return keyRing; - } - - /** - * Convert from byte[] to ArrayList - * - * @param keysBytes - * @return - */ - public static ArrayList BytesToPGPSecretKeyList(byte[] keysBytes) { - PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes); - ArrayList keys = new ArrayList(); - - @SuppressWarnings("unchecked") - Iterator itr = keyRing.getSecretKeys(); - while (itr.hasNext()) { - keys.add(itr.next()); - } - - return keys; - } - - /** - * Convert from byte[] to PGPSecretKey - * - * Singles keys are encoded as keyRings with one single key in it by Bouncy Castle - * - * @param keysBytes - * @return - */ - public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) { - PGPSecretKey key = BytesToPGPSecretKeyList(keyBytes).get(0); - - return key; - } - - /** - * Convert from ArrayList to byte[] - * - * @param keys - * @return - */ - public static byte[] PGPSecretKeyArrayListToBytes(ArrayList keys) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - for (PGPSecretKey key : keys) { - try { - key.encode(os); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); - } - } - - return os.toByteArray(); - } - - /** - * Convert from PGPSecretKey to byte[] - * - * @param keysBytes - * @return - */ - public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) { - try { - return key.getEncoded(); - } catch (IOException e) { - Log.e(Constants.TAG, "Encoding failed", e); - - return null; - } - } - - /** - * Convert from PGPSecretKeyRing to byte[] - * - * @param keysBytes - * @return - */ - public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) { - try { - return keyRing.getEncoded(); - } catch (IOException e) { - Log.e(Constants.TAG, "Encoding failed", e); - - return null; - } - } - -} diff --git a/APG/src/org/thialfihar/android/apg/helper/PGPHelper.java b/APG/src/org/thialfihar/android/apg/helper/PGPHelper.java deleted file mode 100644 index f7bc62d4f..000000000 --- a/APG/src/org/thialfihar/android/apg/helper/PGPHelper.java +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.helper; - -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; -import java.util.Vector; - -import org.spongycastle.bcpg.sig.KeyFlags; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.openpgp.PGPSignatureSubpacketVector; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.util.IterableIterator; -import org.thialfihar.android.apg.util.Log; - -import android.content.Context; - -public class PGPHelper { - - 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(Context context, long masterKeyId) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context, - masterKeyId); - if (keyRing == null) { - Log.e(Constants.TAG, "keyRing is null!"); - return null; - } - Vector encryptKeys = getUsableEncryptKeys(keyRing); - if (encryptKeys.size() == 0) { - Log.e(Constants.TAG, "encryptKeys is null!"); - return null; - } - return encryptKeys.get(0); - } - - public static PGPSecretKey getSigningKey(Context context, long masterKeyId) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, - 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.equals("")) { - userId = context.getString(R.string.unknownUserId); - } - return userId; - } - - public static String getMainUserIdSafe(Context context, PGPSecretKey key) { - String userId = getMainUserId(key); - if (userId == null || userId.equals("")) { - userId = context.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 convertFingerprintToHex(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(Locale.US); - while (chunk.length() < 2) { - chunk = "0" + chunk; - } - fingerPrint += chunk; - } - - return fingerPrint; - - } - - public static String getFingerPrint(Context context, long keyId) { - PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); - // if it is no public key get it from your own keys... - if (key == null) { - PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); - if (secretKey == null) { - Log.e(Constants.TAG, "Key could not be found!"); - return null; - } - key = secretKey.getPublicKey(); - } - - return convertFingerprintToHex(key.getFingerprint()); - } - - public static String getSmallFingerPrint(long keyId) { - String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US); - 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); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/helper/PGPMain.java b/APG/src/org/thialfihar/android/apg/helper/PGPMain.java deleted file mode 100644 index 5afc935ec..000000000 --- a/APG/src/org/thialfihar/android/apg/helper/PGPMain.java +++ /dev/null @@ -1,1856 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.helper; - -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.PGPPublicKeyRingCollection; -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.spongycastle.openpgp.operator.PBEDataDecryptorFactory; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; -import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; -import org.spongycastle.openpgp.operator.PGPDigestCalculator; -import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; -import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; -import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.util.HkpKeyServer; -import org.thialfihar.android.apg.util.InputData; -import org.thialfihar.android.apg.util.PositionAwareInputStream; -import org.thialfihar.android.apg.util.Primes; -import org.thialfihar.android.apg.util.ProgressDialogUpdater; -import org.thialfihar.android.apg.util.KeyServer.AddKeyException; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; -import android.os.Environment; -import org.thialfihar.android.apg.util.Log; - -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.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.regex.Pattern; - -/** - * TODO: - * - * - Separate this file into different helpers - * - */ -public class PGPMain { - - static { - // register spongy castle provider - Security.addProvider(new BouncyCastleProvider()); - } - - // Not BC due to the use of Spongy Castle for Android - public static final String BOUNCY_CASTLE_PROVIDER_NAME = "SC"; - - 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 String mEditPassPhrase = null; - - public static class ApgGeneralException extends Exception { - static final long serialVersionUID = 0xf812773342L; - - public ApgGeneralException(String message) { - super(message); - } - } - - public static class NoAsymmetricEncryptionException extends Exception { - static final long serialVersionUID = 0xf812773343L; - - public NoAsymmetricEncryptionException() { - super(); - } - } - - public static void setEditPassPhrase(String passPhrase) { - mEditPassPhrase = passPhrase; - } - - public static String getEditPassPhrase() { - return mEditPassPhrase; - } - - public static void updateProgress(ProgressDialogUpdater progress, int message, int current, - int total) { - if (progress != null) { - progress.setProgress(message, current, total); - } - } - - public static void updateProgress(ProgressDialogUpdater progress, int current, int total) { - if (progress != null) { - progress.setProgress(current, total); - } - } - - /** - * Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key - * when this key is the new masterkey. If a masterkey is supplied in the parameters - * PGPSecretKeyRing contains the masterkey and the new key as a subkey (certified by the - * masterkey). - * - * @param context - * @param algorithmChoice - * @param keySize - * @param passPhrase - * @param masterSecretKey - * @return - * @throws NoSuchAlgorithmException - * @throws PGPException - * @throws NoSuchProviderException - * @throws ApgGeneralException - * @throws InvalidAlgorithmParameterException - */ - public static PGPSecretKeyRing createKey(Context context, int algorithmChoice, int keySize, - String passPhrase, PGPSecretKey masterSecretKey) throws NoSuchAlgorithmException, - PGPException, NoSuchProviderException, ApgGeneralException, - InvalidAlgorithmParameterException { - - if (keySize < 512) { - throw new ApgGeneralException(context.getString(R.string.error_keySizeMinimum512bit)); - } - - if (passPhrase == null) { - passPhrase = ""; - } - - int algorithm = 0; - KeyPairGenerator keyGen = null; - - switch (algorithmChoice) { - case Id.choice.algorithm.dsa: { - keyGen = KeyPairGenerator.getInstance("DSA", BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - algorithm = PGPPublicKey.DSA; - break; - } - - case Id.choice.algorithm.elgamal: { - if (masterSecretKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_masterKeyMustNotBeElGamal)); - } - keyGen = KeyPairGenerator.getInstance("ElGamal", BOUNCY_CASTLE_PROVIDER_NAME); - 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", BOUNCY_CASTLE_PROVIDER_NAME); - keyGen.initialize(keySize, new SecureRandom()); - - algorithm = PGPPublicKey.RSA_GENERAL; - break; - } - - default: { - throw new ApgGeneralException(context.getString(R.string.error_unknownAlgorithmChoice)); - } - } - - // build new key pair - PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - - // Build key encrypter and decrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - passPhrase.toCharArray()); - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray()); - - PGPKeyRingGenerator ringGen = null; - PGPContentSignerBuilder certificationSignerBuilder = null; - if (masterSecretKey == null) { - certificationSignerBuilder = new JcaPGPContentSignerBuilder(keyPair.getPublicKey() - .getAlgorithm(), HashAlgorithmTags.SHA1); - - // build keyRing with only this one master key in it! - ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, keyPair, "", - sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); - } else { - PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); - PGPPrivateKey masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); - - certificationSignerBuilder = new JcaPGPContentSignerBuilder(masterKeyPair - .getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // build keyRing with master key and new key as subkey (certified by masterkey) - ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, - "", sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); - - ringGen.addSubKey(keyPair); - } - - PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing(); - - return secKeyRing; - } - - public static void buildSecretKey(Context context, ArrayList userIds, - ArrayList keys, ArrayList keysUsages, long masterKeyId, - String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) - throws ApgGeneralException, NoSuchProviderException, PGPException, - NoSuchAlgorithmException, SignatureException, IOException { - - Log.d(Constants.TAG, "userIds: " + userIds.toString()); - - updateProgress(progress, R.string.progress_buildingKey, 0, 100); - - if (oldPassPhrase == null) { - oldPassPhrase = ""; - } - if (newPassPhrase == null) { - newPassPhrase = ""; - } - - updateProgress(progress, R.string.progress_preparingMasterKey, 10, 100); - - int usageId = keysUsages.get(0); - 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); - - // this removes all userIds and certifications previously attached to the masterPublicKey - PGPPublicKey tmpKey = masterKey.getPublicKey(); - PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(), - tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime()); - - // already done by code above: - // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - // // Somehow, the PGPPublicKey already has an empty certification attached to it when the - // // keyRing is generated the first time, we remove that when it exists, before adding the - // new - // // ones - // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey, - // ""); - // if (masterPublicKeyRmCert != null) { - // masterPublicKey = masterPublicKeyRmCert; - // } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); - PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); - - updateProgress(progress, R.string.progress_certifyingMasterKey, 20, 100); - - for (String userId : userIds) { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(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 (APG 1) - - 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 (APG 1) - // if (keyEditor.getExpiryDate() != null) { - // GregorianCalendar creationDate = new GregorianCalendar(); - // creationDate.setTime(getCreationDate(masterKey)); - // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); - // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); - // if (numDays <= 0) { - // throw new GeneralException( - // context.getString(R.string.error_expiryMustComeAfterCreation)); - // } - // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); - // } - - updateProgress(progress, R.string.progress_buildingMasterKeyRing, 30, 100); - - // define hashing and signing algos - PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( - HashAlgorithmTags.SHA1); - PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( - masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - - // Build key encrypter based on passphrase - PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - newPassPhrase.toCharArray()); - - PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, - masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), - unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - - updateProgress(progress, R.string.progress_addingSubKeys, 40, 100); - - for (int i = 1; i < keys.size(); ++i) { - updateProgress(progress, 40 + 50 * (i - 1) / (keys.size() - 1), 100); - - PGPSecretKey subKey = keys.get(i); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - - // TODO: now used without algorithm and creation time?! (APG 1) - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - keyFlags = 0; - - usageId = keysUsages.get(i); - 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 (APG 1) - // if (keyEditor.getExpiryDate() != null) { - // GregorianCalendar creationDate = new GregorianCalendar(); - // creationDate.setTime(getCreationDate(masterKey)); - // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); - // long numDays = Utils.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(); - - updateProgress(progress, R.string.progress_savingKeyRing, 90, 100); - - ProviderHelper.saveKeyRing(context, secretKeyRing); - ProviderHelper.saveKeyRing(context, publicKeyRing); - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - /** - * TODO: implement Id.return_value.updated as status when key already existed - * - * @param context - * @param keyring - * @return - */ - public static int storeKeyRingInCache(Context context, PGPKeyRing keyring) { - int status = Integer.MIN_VALUE; // out of bounds value (Id.return_value.*) - try { - if (keyring instanceof PGPSecretKeyRing) { - PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; - boolean save = true; - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); - PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey( - keyDecryptor); - 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) { - ProviderHelper.saveKeyRing(context, secretKeyRing); - // TODO: remove status returns, use exceptions! - status = Id.return_value.ok; - } - } else if (keyring instanceof PGPPublicKeyRing) { - PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; - ProviderHelper.saveKeyRing(context, publicKeyRing); - // TODO: remove status returns, use exceptions! - status = Id.return_value.ok; - } - } catch (IOException 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(Context context, InputData data, - ProgressDialogUpdater progress) throws ApgGeneralException, FileNotFoundException, - PGPException, IOException { - Bundle returnData = new Bundle(); - - updateProgress(progress, R.string.progress_importingSecretKeys, 0, 100); - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new ApgGeneralException(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 { - - // read all available blocks... (asc files can contain many blocks with BEGIN END) - while (bufferedInput.available() > 0) { - InputStream in = PGPUtil.getDecoderStream(bufferedInput); - PGPObjectFactory objectFactory = new PGPObjectFactory(in); - - // go through all objects in this block - Object obj; - while ((obj = objectFactory.nextObject()) != null) { - Log.d(Constants.TAG, "Found class: " + obj.getClass()); - - if (obj instanceof PGPKeyRing) { - PGPKeyRing keyring = (PGPKeyRing) obj; - - int status = Integer.MIN_VALUE; // out of bounds value - - status = storeKeyRingInCache(context, keyring); - - if (status == Id.return_value.error) { - throw new ApgGeneralException( - 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; - } - - updateProgress(progress, - (int) (100 * progressIn.position() / data.getSize()), 100); - } else { - Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); - } - } - } - } catch (Exception e) { - Log.e(Constants.TAG, "Exception on parsing key file!", e); - } - - returnData.putInt(ApgIntentService.RESULT_IMPORT_ADDED, newKeys); - returnData.putInt(ApgIntentService.RESULT_IMPORT_UPDATED, oldKeys); - returnData.putInt(ApgIntentService.RESULT_IMPORT_BAD, badKeys); - - updateProgress(progress, R.string.progress_done, 100, 100); - - return returnData; - } - - public static Bundle exportKeyRings(Context context, ArrayList keyRingMasterKeyIds, - int keyType, OutputStream outStream, ProgressDialogUpdater progress) - throws ApgGeneralException, FileNotFoundException, PGPException, IOException { - Bundle returnData = new Bundle(); - - if (keyRingMasterKeyIds.size() == 1) { - updateProgress(progress, R.string.progress_exportingKey, 0, 100); - } else { - updateProgress(progress, R.string.progress_exportingKeys, 0, 100); - } - - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new ApgGeneralException(context.getString(R.string.error_externalStorageNotReady)); - } - - // export public keyrings... - ArmoredOutputStream outPub = new ArmoredOutputStream(outStream); - outPub.setHeader("Version", getFullVersion(context)); - - int numKeys = 0; - for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { - // double the needed time if exporting both public and secret parts - if (keyType == Id.type.secret_key) { - updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); - } else { - updateProgress(progress, i * 100 / keyRingMasterKeyIds.size(), 100); - } - - PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId( - context, keyRingMasterKeyIds.get(i)); - - if (publicKeyRing != null) { - publicKeyRing.encode(outPub); - } - ++numKeys; - } - outPub.close(); - - // if we export secret keyrings, append all secret parts after the public parts - if (keyType == Id.type.secret_key) { - ArmoredOutputStream outSec = new ArmoredOutputStream(outStream); - outSec.setHeader("Version", getFullVersion(context)); - - for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { - updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); - - PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - context, keyRingMasterKeyIds.get(i)); - - if (secretKeyRing != null) { - secretKeyRing.encode(outSec); - } - } - outSec.close(); - } - - returnData.putInt(ApgIntentService.RESULT_EXPORT, numKeys); - - updateProgress(progress, R.string.progress_done, 100, 100); - - return returnData; - } - - /** - * Encrypt and Sign data - * - * @param context - * @param progress - * @param data - * @param outStream - * @param useAsciiArmor - * @param compression - * @param encryptionKeyIds - * @param symmetricEncryptionAlgorithm - * @param encryptionPassphrase - * @param signatureKeyId - * @param signatureHashAlgorithm - * @param signatureForceV3 - * @param signaturePassphrase - * @throws IOException - * @throws ApgGeneralException - * @throws PGPException - * @throws NoSuchProviderException - * @throws NoSuchAlgorithmException - * @throws SignatureException - */ - public static void encryptAndSign(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean useAsciiArmor, int compression, - long encryptionKeyIds[], String encryptionPassphrase, int symmetricEncryptionAlgorithm, - long signatureKeyId, int signatureHashAlgorithm, boolean signatureForceV3, - String signaturePassphrase) throws IOException, ApgGeneralException, PGPException, - NoSuchProviderException, NoSuchAlgorithmException, SignatureException { - - if (encryptionKeyIds == null) { - encryptionKeyIds = new long[0]; - } - - ArmoredOutputStream armorOut = null; - OutputStream out = null; - OutputStream encryptOut = null; - if (useAsciiArmor) { - 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 && encryptionPassphrase == null) { - throw new ApgGeneralException( - context.getString(R.string.error_noEncryptionKeysOrPassPhrase)); - } - - if (signatureKeyId != Id.key.none) { - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PGPHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassphrase == null) { - throw new ApgGeneralException( - context.getString(R.string.error_noSignaturePassPhrase)); - } - - updateProgress(progress, R.string.progress_extractingSignatureKey, 0, 100); - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - } - updateProgress(progress, R.string.progress_preparingStreams, 5, 100); - - // encrypt and compress input file content - JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder( - symmetricEncryptionAlgorithm).setProvider(BOUNCY_CASTLE_PROVIDER_NAME) - .setWithIntegrityPacket(true); - - PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(encryptorBuilder); - - if (encryptionKeyIds.length == 0) { - // Symmetric encryption - Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption"); - - JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator( - encryptionPassphrase.toCharArray()); - cPk.addMethod(symmetricEncryptionGenerator); - } else { - // Asymmetric encryption - for (int i = 0; i < encryptionKeyIds.length; ++i) { - PGPPublicKey key = PGPHelper.getEncryptPublicKey(context, encryptionKeyIds[i]); - if (key != null) { - - JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = new JcePublicKeyKeyEncryptionMethodGenerator( - key); - cPk.addMethod(pubKeyEncryptionGenerator); - } - } - } - encryptOut = cPk.open(out, new byte[1 << 16]); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - if (signatureKeyId != Id.key.none) { - updateProgress(progress, R.string.progress_preparingSignature, 10, 100); - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (signatureForceV3) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); - - String userId = PGPHelper.getMainUserId(PGPHelper.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 != Id.key.none) { - if (signatureForceV3) { - 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]); - updateProgress(progress, 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 != Id.key.none) { - if (signatureForceV3) { - signatureV3Generator.update(buffer, 0, n); - } else { - signatureGenerator.update(buffer, 0, n); - } - } - done += n; - if (data.getSize() != 0) { - updateProgress(progress, (int) (20 + (95 - 20) * done / data.getSize()), 100); - } - } - - literalGen.close(); - - if (signatureKeyId != Id.key.none) { - updateProgress(progress, R.string.progress_generatingSignature, 95, 100); - if (signatureForceV3) { - signatureV3Generator.generate().encode(pOut); - } else { - signatureGenerator.generate().encode(pOut); - } - } - if (compressGen != null) { - compressGen.close(); - } - encryptOut.close(); - if (useAsciiArmor) { - armorOut.close(); - } - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - public static void signText(Context context, ProgressDialogUpdater progress, InputData data, - OutputStream outStream, long signatureKeyId, String signaturePassphrase, - int signatureHashAlgorithm, boolean forceV3Signature) throws ApgGeneralException, - PGPException, IOException, NoSuchAlgorithmException, SignatureException { - - ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream); - armorOut.setHeader("Version", getFullVersion(context)); - - PGPSecretKey signingKey = null; - PGPSecretKeyRing signingKeyRing = null; - PGPPrivateKey signaturePrivateKey = null; - - if (signatureKeyId == 0) { - armorOut.close(); - throw new ApgGeneralException(context.getString(R.string.error_noSignatureKey)); - } - - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PGPHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - armorOut.close(); - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassphrase == null) { - armorOut.close(); - throw new ApgGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); - } - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - armorOut.close(); - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - updateProgress(progress, R.string.progress_preparingStreams, 0, 100); - - updateProgress(progress, R.string.progress_preparingSignature, 30, 100); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey - .getPublicKey().getAlgorithm(), signatureHashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (forceV3Signature) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - - updateProgress(progress, R.string.progress_signing, 40, 100); - - armorOut.beginClearText(signatureHashAlgorithm); - - 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(); - - updateProgress(progress, R.string.progress_done, 100, 100); - } - - public static void generateSignature(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean armored, boolean binary, - long signatureKeyId, String signaturePassPhrase, int hashAlgorithm, - boolean forceV3Signature) throws ApgGeneralException, PGPException, IOException, - NoSuchAlgorithmException, SignatureException { - - OutputStream out = null; - - // Ascii Armor (Base64) - ArmoredOutputStream armorOut = 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 ApgGeneralException(context.getString(R.string.error_noSignatureKey)); - } - - signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); - signingKey = PGPHelper.getSigningKey(context, signatureKeyId); - if (signingKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - if (signaturePassPhrase == null) { - throw new ApgGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray()); - signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - updateProgress(progress, R.string.progress_preparingStreams, 0, 100); - - updateProgress(progress, R.string.progress_preparingSignature, 30, 100); - - PGPSignatureGenerator signatureGenerator = null; - PGPV3SignatureGenerator signatureV3Generator = null; - - int type = PGPSignature.CANONICAL_TEXT_DOCUMENT; - if (binary) { - type = PGPSignature.BINARY_DOCUMENT; - } - - // content signer based on signing key algorithm and choosen hash algorithm - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey - .getPublicKey().getAlgorithm(), hashAlgorithm) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - if (forceV3Signature) { - signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); - signatureV3Generator.init(type, signaturePrivateKey); - } else { - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(type, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - String userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signingKeyRing)); - spGen.setSignerUserID(false, userId); - signatureGenerator.setHashedSubpackets(spGen.generate()); - } - - updateProgress(progress, 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 PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId, - String passphrase) throws ApgGeneralException, NoSuchAlgorithmException, - NoSuchProviderException, PGPException, SignatureException { - if (passphrase == null || passphrase.length() <= 0) { - throw new ApgGeneralException("Unable to obtain passphrase"); - } else { - PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByKeyId(context, pubKeyId); - - PGPSecretKey signingKey = PGPHelper.getSigningKey(context, masterKeyId); - if (signingKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_signatureFailed)); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - - // TODO: SHA256 fixed? - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( - contentSignerBuilder); - - signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey); - - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - - PGPSignatureSubpacketVector packetVector = spGen.generate(); - signatureGenerator.setHashedSubpackets(packetVector); - - PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), - signatureGenerator.generate()); - pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey); - - return pubring; - } - } - - public static long getDecryptionKeyId(Context context, InputStream inputStream) - throws ApgGeneralException, NoAsymmetricEncryptionException, IOException { - InputStream in = PGPUtil.getDecoderStream(inputStream); - 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 ApgGeneralException(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 = ProviderHelper.getPGPSecretKeyByKeyId(context, 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, InputStream inputStream) - throws ApgGeneralException, IOException { - InputStream in = PGPUtil.getDecoderStream(inputStream); - 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 ApgGeneralException(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 decryptAndVerify(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, String passphrase, boolean assumeSymmetric) - throws IOException, ApgGeneralException, 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 ApgGeneralException(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 ApgGeneralException( - context.getString(R.string.error_noSymmetricEncryptionPacket)); - } - - updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); - - PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(); - PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( - digestCalcProvider).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( - passphrase.toCharArray()); - - clear = pbe.getDataStream(decryptorFactory); - - 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 = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); - if (secretKey != null) { - pbe = encData; - break; - } - } - } - - if (secretKey == null) { - throw new ApgGeneralException(context.getString(R.string.error_noSecretKeyFound)); - } - - currentProgress += 5; - updateProgress(progress, R.string.progress_extractingKey, currentProgress, 100); - PGPPrivateKey privateKey = null; - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - privateKey = secretKey.extractPrivateKey(keyDecryptor); - } catch (PGPException e) { - throw new PGPException(context.getString(R.string.error_wrongPassPhrase)); - } - if (privateKey == null) { - throw new ApgGeneralException( - context.getString(R.string.error_couldNotExtractPrivateKey)); - } - currentProgress += 5; - updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); - - PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey); - - clear = pbe.getDataStream(decryptorFactory); - - 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(ApgIntentService.RESULT_SIGNATURE, true); - PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; - for (int i = 0; i < sigList.size(); ++i) { - signature = sigList.get(i); - signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); - if (signatureKeyId == 0) { - signatureKeyId = signature.getKeyID(); - } - if (signatureKey == null) { - signature = null; - } else { - signatureIndex = i; - signatureKeyId = signature.getKeyID(); - String userId = null; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId( - context, signatureKeyId); - if (signKeyRing != null) { - userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signKeyRing)); - } - returnData.putString(ApgIntentService.RESULT_SIGNATURE_USER_ID, userId); - break; - } - } - - returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - - if (signature != null) { - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - signature.init(contentVerifierBuilderProvider, signatureKey); - } else { - returnData.putBoolean(ApgIntentService.RESULT_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(ApgIntentService.RESULT_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)); - } - updateProgress(progress, 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(ApgIntentService.RESULT_SIGNATURE_SUCCESS, true); - } else { - returnData.putBoolean(ApgIntentService.RESULT_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 - } - - updateProgress(progress, R.string.progress_done, 100, 100); - return returnData; - } - - public static Bundle verifyText(Context context, ProgressDialogUpdater progress, - InputData data, OutputStream outStream, boolean lookupUnknownKey) throws IOException, - ApgGeneralException, PGPException, SignatureException { - Bundle returnData = new Bundle(); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream()); - - updateProgress(progress, 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(ApgIntentService.RESULT_SIGNATURE, true); - - updateProgress(progress, R.string.progress_processingSignature, 60, 100); - PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); - - PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); - if (sigList == null) { - throw new ApgGeneralException(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 = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); - if (signatureKeyId == 0) { - signatureKeyId = signature.getKeyID(); - } - // if key is not known and we want to lookup unknown ones... - if (signatureKey == null && lookupUnknownKey) { - - returnData = new Bundle(); - returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_LOOKUP_KEY, true); - - // return directly now, decrypt will be done again after importing unknown key - return returnData; - } - - if (signatureKey == null) { - signature = null; - } else { - signatureKeyId = signature.getKeyID(); - String userId = null; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context, - signatureKeyId); - if (signKeyRing != null) { - userId = PGPHelper.getMainUserId(PGPHelper.getMasterKey(signKeyRing)); - } - returnData.putString(ApgIntentService.RESULT_SIGNATURE_USER_ID, userId); - break; - } - } - - returnData.putLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); - - if (signature == null) { - returnData.putBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN, true); - if (progress != null) - progress.setProgress(R.string.progress_done, 100, 100); - return returnData; - } - - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); - - signature.init(contentVerifierBuilderProvider, signatureKey); - - 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(ApgIntentService.RESULT_SIGNATURE_SUCCESS, signature.verify()); - - updateProgress(progress, 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(Constants.PACKAGE_NAME, 0); - if (pi.versionCode % 100 == 99) { - return true; - } else { - return false; - } - } catch (NameNotFoundException e) { - // impossible! - return false; - } - } - - public static String getVersion(Context context) { - String version = null; - try { - PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); - version = pi.versionName; - return version; - } catch (NameNotFoundException e) { - Log.e(Constants.TAG, "Version could not be retrieved!", e); - return "0.0.0"; - } - } - - public static String getFullVersion(Context context) { - return "APG v" + getVersion(context); - } - - /** - * Generate a random filename - * - * @param length - * @return - */ - public static String generateRandomFilename(int length) { - SecureRandom random = new SecureRandom(); - - 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; - } - - /** - * Go once through stream to get length of stream. The length is later used to display progress - * when encrypting/decrypting - * - * @param in - * @return - * @throws IOException - */ - public 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; - } - - /** - * Deletes file securely by overwriting it with random data before deleting it. - * - * TODO: Does this really help on flash storage? - * - * @param context - * @param progress - * @param file - * @throws FileNotFoundException - * @throws IOException - */ - public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file) - 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/APG/src/org/thialfihar/android/apg/helper/Preferences.java b/APG/src/org/thialfihar/android/apg/helper/Preferences.java deleted file mode 100644 index 14adf357a..000000000 --- a/APG/src/org/thialfihar/android/apg/helper/Preferences.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.helper; - -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; - -import android.content.Context; -import android.content.SharedPreferences; - -import java.util.Vector; - -/** - * Singleton Implementation of a Preference Helper - */ -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 long 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 (long) 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 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/APG/src/org/thialfihar/android/apg/provider/ApgContract.java b/APG/src/org/thialfihar/android/apg/provider/ApgContract.java deleted file mode 100644 index 3f01f4093..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgContract.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.provider; - -import org.thialfihar.android.apg.Constants; - -import android.net.Uri; -import android.provider.BaseColumns; - -public class ApgContract { - - interface KeyRingsColumns { - String MASTER_KEY_ID = "master_key_id"; // not a database id - String TYPE = "type"; // see KeyTypes - String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob - } - - interface KeysColumns { - String KEY_ID = "key_id"; // not a database id - String TYPE = "type"; // see KeyTypes - String IS_MASTER_KEY = "is_master_key"; - String ALGORITHM = "algorithm"; - String KEY_SIZE = "key_size"; - String CAN_SIGN = "can_sign"; - String CAN_ENCRYPT = "can_encrypt"; - String IS_REVOKED = "is_revoked"; - String CREATION = "creation"; - String EXPIRY = "expiry"; - String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID - String KEY_DATA = "key_data"; // PGPPublicKey / PGPSecretKey blob - String RANK = "rank"; - } - - interface UserIdsColumns { - String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID - String USER_ID = "user_id"; // not a database id - String RANK = "rank"; - } - - public static final class KeyTypes { - public static final int PUBLIC = 0; - public static final int SECRET = 1; - } - - public static final String CONTENT_AUTHORITY_EXTERNAL = Constants.PACKAGE_NAME; - public static final String CONTENT_AUTHORITY_INTERNAL = Constants.PACKAGE_NAME + ".internal"; - - private static final Uri BASE_CONTENT_URI_INTERNAL = Uri.parse("content://" - + CONTENT_AUTHORITY_INTERNAL); - - public static final String BASE_KEY_RINGS = "key_rings"; - public static final String BASE_DATA = "data"; - - public static final String PATH_PUBLIC = "public"; - public static final String PATH_SECRET = "secret"; - - public static final String PATH_BY_MASTER_KEY_ID = "master_key_id"; - public static final String PATH_BY_KEY_ID = "key_id"; - public static final String PATH_BY_EMAILS = "emails"; - public static final String PATH_BY_LIKE_EMAIL = "like_email"; - - public static final String PATH_USER_IDS = "user_ids"; - public static final String PATH_KEYS = "keys"; - - public static class KeyRings implements KeyRingsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_KEY_RINGS).build(); - - /** Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring"; - - /** Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring"; - - public static Uri buildPublicKeyRingsUri() { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build(); - } - - public static Uri buildPublicKeyRingsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId).build(); - } - - public static Uri buildPublicKeyRingsByMasterKeyIdUri(String masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC) - .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); - } - - public static Uri buildPublicKeyRingsByKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_KEY_ID) - .appendPath(keyId).build(); - } - - public static Uri buildPublicKeyRingsByEmailsUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_EMAILS) - .appendPath(emails).build(); - } - - public static Uri buildPublicKeyRingsByLikeEmailUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_LIKE_EMAIL) - .appendPath(emails).build(); - } - - public static Uri buildSecretKeyRingsUri() { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build(); - } - - public static Uri buildSecretKeyRingsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId).build(); - } - - public static Uri buildSecretKeyRingsByMasterKeyIdUri(String masterKeyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET) - .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); - } - - public static Uri buildSecretKeyRingsByKeyIdUri(String keyId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_KEY_ID) - .appendPath(keyId).build(); - } - - public static Uri buildSecretKeyRingsByEmailsUri(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS) - .appendPath(emails).build(); - } - - public static Uri buildSecretKeyRingsByLikeEmails(String emails) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_LIKE_EMAIL) - .appendPath(emails).build(); - } - } - - public static class Keys implements KeysColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_KEY_RINGS).build(); - - /** Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key"; - - /** Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key"; - - public static Uri buildPublicKeysUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).build(); - } - - public static Uri buildPublicKeysUri(String keyRingRowId, String keyRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).appendPath(keyRowId).build(); - } - - public static Uri buildSecretKeysUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).build(); - } - - public static Uri buildSecretKeysUri(String keyRingRowId, String keyRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_KEYS).appendPath(keyRowId).build(); - } - } - - public static class UserIds implements UserIdsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_KEY_RINGS).build(); - - /** Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; - - /** Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; - - public static Uri buildPublicUserIdsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).build(); - } - - public static Uri buildPublicUserIdsUri(String keyRingRowId, String userIdRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); - } - - public static Uri buildSecretUserIdsUri(String keyRingRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).build(); - } - - public static Uri buildSecretUserIdsUri(String keyRingRowId, String userIdRowId) { - return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) - .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); - } - } - - public static class DataStream { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_DATA).build(); - - public static Uri buildDataStreamUri(String streamFilename) { - return CONTENT_URI.buildUpon().appendPath(streamFilename).build(); - } - } - - private ApgContract() { - } -} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java b/APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java deleted file mode 100644 index e25fce3f0..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgDatabase.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.provider; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.provider.ApgContract.KeyRingsColumns; -import org.thialfihar.android.apg.provider.ApgContract.KeysColumns; -import org.thialfihar.android.apg.provider.ApgContract.UserIdsColumns; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.provider.BaseColumns; - -import org.thialfihar.android.apg.util.Log; - -public class ApgDatabase extends SQLiteOpenHelper { - private static final String DATABASE_NAME = "apg.db"; - private static final int DATABASE_VERSION = 3; - - public interface Tables { - String KEY_RINGS = "key_rings"; - String KEYS = "keys"; - String USER_IDS = "user_ids"; - } - - private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS - + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + KeyRingsColumns.MASTER_KEY_ID + " INT64, " + KeyRingsColumns.TYPE + " INTEGER, " - + KeyRingsColumns.KEY_RING_DATA + " BLOB)"; - - private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " (" - + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KeysColumns.KEY_ID - + " INT64, " + KeysColumns.TYPE + " INTEGER, " + KeysColumns.IS_MASTER_KEY - + " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.KEY_SIZE - + " INTEGER, " + KeysColumns.CAN_SIGN + " INTEGER, " + KeysColumns.CAN_ENCRYPT - + " INTEGER, " + KeysColumns.IS_REVOKED + " INTEGER, " + KeysColumns.CREATION - + " INTEGER, " + KeysColumns.EXPIRY + " INTEGER, " + KeysColumns.KEY_DATA + " BLOB," - + KeysColumns.RANK + " INTEGER, " + KeysColumns.KEY_RING_ROW_ID - + " INTEGER NOT NULL, FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " - + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)"; - - private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS - + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, " - + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; - - ApgDatabase(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - Log.w(Constants.TAG, "Creating database..."); - - db.execSQL(CREATE_KEY_RINGS); - db.execSQL(CREATE_KEYS); - db.execSQL(CREATE_USER_IDS); - } - - @Override - public void onOpen(SQLiteDatabase db) { - super.onOpen(db); - if (!db.isReadOnly()) { - // Enable foreign key constraints - db.execSQL("PRAGMA foreign_keys=ON;"); - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); - - // Upgrade from oldVersion through all methods to newest one - for (int version = oldVersion; version < newVersion; ++version) { - Log.w(Constants.TAG, "Upgrading database to version " + version); - - switch (version) { - - default: - break; - - } - } - } - -} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgProvider.java b/APG/src/org/thialfihar/android/apg/provider/ApgProvider.java deleted file mode 100644 index 88cb3df21..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgProvider.java +++ /dev/null @@ -1,852 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.provider; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.Arrays; -import java.util.HashMap; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.KeyRingsColumns; -import org.thialfihar.android.apg.provider.ApgContract.KeyTypes; -import org.thialfihar.android.apg.provider.ApgContract.KeysColumns; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIdsColumns; -import org.thialfihar.android.apg.provider.ApgDatabase.Tables; -import org.thialfihar.android.apg.util.Log; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.UriMatcher; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteConstraintException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteQueryBuilder; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.provider.BaseColumns; -import android.text.TextUtils; - -public class ApgProvider extends ContentProvider { - private static final int PUBLIC_KEY_RING = 101; - private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102; - private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103; - private static final int PUBLIC_KEY_RING_BY_KEY_ID = 104; - private static final int PUBLIC_KEY_RING_BY_EMAILS = 105; - private static final int PUBLIC_KEY_RING_BY_LIKE_EMAIL = 106; - - private static final int PUBLIC_KEY_RING_KEY = 111; - private static final int PUBLIC_KEY_RING_KEY_BY_ROW_ID = 112; - - private static final int PUBLIC_KEY_RING_USER_ID = 121; - private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122; - - private static final int SECRET_KEY_RING = 201; - private static final int SECRET_KEY_RING_BY_ROW_ID = 202; - private static final int SECRET_KEY_RING_BY_MASTER_KEY_ID = 203; - private static final int SECRET_KEY_RING_BY_KEY_ID = 204; - private static final int SECRET_KEY_RING_BY_EMAILS = 205; - private static final int SECRET_KEY_RING_BY_LIKE_EMAIL = 206; - - private static final int SECRET_KEY_RING_KEY = 211; - private static final int SECRET_KEY_RING_KEY_BY_ROW_ID = 212; - - private static final int SECRET_KEY_RING_USER_ID = 221; - private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; - - private static final int DATA_STREAM = 301; - - protected boolean mInternalProvider; - protected UriMatcher mUriMatcher; - - /** - * Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by - * this {@link ContentProvider}. - */ - protected UriMatcher buildUriMatcher(boolean internalProvider) { - final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); - - String authority; - if (internalProvider) { - authority = ApgContract.CONTENT_AUTHORITY_INTERNAL; - } else { - authority = ApgContract.CONTENT_AUTHORITY_EXTERNAL; - } - - /** - * public key rings - * - *
-         * key_rings/public
-         * key_rings/public/#
-         * key_rings/public/master_key_id/_
-         * key_rings/public/key_id/_
-         * key_rings/public/emails/_
-         * key_rings/public/like_email/_
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC, - PUBLIC_KEY_RING); - matcher.addURI(authority, - ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/#", - PUBLIC_KEY_RING_BY_ROW_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_MASTER_KEY_ID + "/*", PUBLIC_KEY_RING_BY_MASTER_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_KEY_ID + "/*", PUBLIC_KEY_RING_BY_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_EMAILS + "/*", PUBLIC_KEY_RING_BY_EMAILS); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_EMAILS, PUBLIC_KEY_RING_BY_EMAILS); // without emails - // specified - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC + "/" - + ApgContract.PATH_BY_LIKE_EMAIL + "/*", PUBLIC_KEY_RING_BY_LIKE_EMAIL); - - /** - * public keys - * - *
-         * key_rings/public/#/keys
-         * key_rings/public/#/keys/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_KEYS, PUBLIC_KEY_RING_KEY); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_KEYS + "/#", PUBLIC_KEY_RING_KEY_BY_ROW_ID); - - /** - * public user ids - * - *
-         * key_rings/public/#/user_ids
-         * key_rings/public/#/user_ids/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_USER_IDS, PUBLIC_KEY_RING_USER_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_PUBLIC - + "/#/" + ApgContract.PATH_USER_IDS + "/#", PUBLIC_KEY_RING_USER_ID_BY_ROW_ID); - - /** - * secret key rings - * - *
-         * key_rings/secret
-         * key_rings/secret/#
-         * key_rings/secret/master_key_id/_
-         * key_rings/secret/key_id/_
-         * key_rings/secret/emails/_
-         * key_rings/secret/like_email/_
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET, - SECRET_KEY_RING); - matcher.addURI(authority, - ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/#", - SECRET_KEY_RING_BY_ROW_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_MASTER_KEY_ID + "/*", SECRET_KEY_RING_BY_MASTER_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_KEY_ID + "/*", SECRET_KEY_RING_BY_KEY_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_EMAILS + "/*", SECRET_KEY_RING_BY_EMAILS); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_EMAILS, SECRET_KEY_RING_BY_EMAILS); // without emails - // specified - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET + "/" - + ApgContract.PATH_BY_LIKE_EMAIL + "/*", SECRET_KEY_RING_BY_LIKE_EMAIL); - - /** - * secret keys - * - *
-         * key_rings/secret/#/keys
-         * key_rings/secret/#/keys/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_KEYS, SECRET_KEY_RING_KEY); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_KEYS + "/#", SECRET_KEY_RING_KEY_BY_ROW_ID); - - /** - * secret user ids - * - *
-         * key_rings/secret/#/user_ids
-         * key_rings/secret/#/user_ids/#
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_USER_IDS, SECRET_KEY_RING_USER_ID); - matcher.addURI(authority, ApgContract.BASE_KEY_RINGS + "/" + ApgContract.PATH_SECRET - + "/#/" + ApgContract.PATH_USER_IDS + "/#", SECRET_KEY_RING_USER_ID_BY_ROW_ID); - - /** - * data stream - * - *
-         * data / _
-         * 
- */ - matcher.addURI(authority, ApgContract.BASE_DATA + "/*", DATA_STREAM); - - return matcher; - } - - private ApgDatabase mApgDatabase; - - /** {@inheritDoc} */ - @Override - public boolean onCreate() { - mUriMatcher = buildUriMatcher(mInternalProvider); - mApgDatabase = new ApgDatabase(getContext()); - return true; - } - - /** {@inheritDoc} */ - @Override - public String getType(Uri uri) { - final int match = mUriMatcher.match(uri); - switch (match) { - case PUBLIC_KEY_RING: - case PUBLIC_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_BY_LIKE_EMAIL: - case SECRET_KEY_RING: - case SECRET_KEY_RING_BY_EMAILS: - case SECRET_KEY_RING_BY_LIKE_EMAIL: - return KeyRings.CONTENT_TYPE; - - case PUBLIC_KEY_RING_BY_ROW_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - case SECRET_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_KEY_ID: - return KeyRings.CONTENT_ITEM_TYPE; - - case PUBLIC_KEY_RING_KEY: - case SECRET_KEY_RING_KEY: - return Keys.CONTENT_TYPE; - - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - return Keys.CONTENT_ITEM_TYPE; - - case PUBLIC_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID: - return UserIds.CONTENT_TYPE; - - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - return UserIds.CONTENT_ITEM_TYPE; - - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - } - - /** - * Returns type of the query (secret/public) - * - * @param uri - * @return - */ - private int getKeyType(int match) { - int type; - switch (match) { - case PUBLIC_KEY_RING: - case PUBLIC_KEY_RING_BY_ROW_ID: - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - case PUBLIC_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_BY_LIKE_EMAIL: - case PUBLIC_KEY_RING_KEY: - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case PUBLIC_KEY_RING_USER_ID: - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - type = KeyTypes.PUBLIC; - break; - - case SECRET_KEY_RING: - case SECRET_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_KEY_ID: - case SECRET_KEY_RING_BY_EMAILS: - case SECRET_KEY_RING_BY_LIKE_EMAIL: - case SECRET_KEY_RING_KEY: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - type = KeyTypes.SECRET; - break; - - default: - throw new IllegalArgumentException("Unknown match " + match); - - } - - return type; - } - - /** - * Set result of query to specific columns, don't show blob column for external content provider - * - * @return - */ - private HashMap getProjectionMapForKeyRings() { - HashMap projectionMap = new HashMap(); - - projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID); - projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." - + KeyRingsColumns.MASTER_KEY_ID); - // only give out keyRing blob when we are using the internal content provider - if (mInternalProvider) { - projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "." - + KeyRingsColumns.KEY_RING_DATA); - } - projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); - - return projectionMap; - } - - /** - * Set result of query to specific columns, don't show blob column for external content provider - * - * @return - */ - private HashMap getProjectionMapForKeys() { - HashMap projectionMap = new HashMap(); - - projectionMap.put(BaseColumns._ID, BaseColumns._ID); - projectionMap.put(KeysColumns.KEY_ID, KeysColumns.KEY_ID); - projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY); - projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM); - projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE); - projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN); - projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT); - projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED); - projectionMap.put(KeysColumns.CREATION, KeysColumns.CREATION); - projectionMap.put(KeysColumns.EXPIRY, KeysColumns.EXPIRY); - projectionMap.put(KeysColumns.KEY_RING_ROW_ID, KeysColumns.KEY_RING_ROW_ID); - // only give out keyRing blob when we are using the internal content provider - if (mInternalProvider) { - projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA); - } - projectionMap.put(KeysColumns.RANK, KeysColumns.RANK); - - return projectionMap; - } - - /** - * Builds default query for keyRings: KeyRings table is joined with UserIds - * - * @param qb - * @param match - * @param isMasterKey - * @param sortOrder - * @return - */ - private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match, String sortOrder) { - // public or secret keyring - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - // join keyrings with userIds to every keyring - qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.USER_IDS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." - + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." - + UserIdsColumns.RANK + " = '0')"); - - qb.setProjectionMap(getProjectionMapForKeyRings()); - - return qb; - } - - /** - * Builds default query for keyRings: KeyRings table is joined with Keys and UserIds - * - * @param qb - * @param match - * @param isMasterKey - * @param sortOrder - * @return - */ - private SQLiteQueryBuilder buildKeyRingQueryWithKeys(SQLiteQueryBuilder qb, int match, - String sortOrder) { - // public or secret keyring - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - // join keyrings with keys and userIds to every keyring - qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" - + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." - + KeysColumns.KEY_RING_ROW_ID + ") " + " INNER JOIN " + Tables.USER_IDS + " ON " - + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." - + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." - + UserIdsColumns.RANK + " = '0')"); - - qb.setProjectionMap(getProjectionMapForKeyRings()); - - return qb; - } - - /** {@inheritDoc} */ - @SuppressWarnings("deprecation") - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); - - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - SQLiteDatabase db = mApgDatabase.getReadableDatabase(); - - int match = mUriMatcher.match(uri); - - switch (match) { - case PUBLIC_KEY_RING: - case SECRET_KEY_RING: - qb = buildKeyRingQuery(qb, match, sortOrder); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - qb = buildKeyRingQuery(qb, match, sortOrder); - - qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - qb = buildKeyRingQuery(qb, match, sortOrder); - - qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case SECRET_KEY_RING_BY_KEY_ID: - case PUBLIC_KEY_RING_BY_KEY_ID: - qb = buildKeyRingQueryWithKeys(qb, match, sortOrder); - - qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; - } - - break; - - case SECRET_KEY_RING_BY_EMAILS: - case PUBLIC_KEY_RING_BY_EMAILS: - qb = buildKeyRingQuery(qb, match, sortOrder); - - String emails = uri.getLastPathSegment(); - String chunks[] = emails.split(" *, *"); - boolean gotCondition = false; - String emailWhere = ""; - for (int i = 0; i < chunks.length; ++i) { - if (chunks[i].length() == 0) { - continue; - } - if (i != 0) { - emailWhere += " OR "; - } - emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE "; - // match '*', so it has to be at the *end* of the user id - emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); - gotCondition = true; - } - - if (gotCondition) { - qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " - + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID - + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere - + "))"); - } - - break; - - case SECRET_KEY_RING_BY_LIKE_EMAIL: - case PUBLIC_KEY_RING_BY_LIKE_EMAIL: - qb = buildKeyRingQuery(qb, match, sortOrder); - - String likeEmail = uri.getLastPathSegment(); - - String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE " - + DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>"); - - qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " - + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID - + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere - + "))"); - - break; - - case PUBLIC_KEY_RING_KEY: - case SECRET_KEY_RING_KEY: - qb.setTables(Tables.KEYS); - qb.appendWhere(KeysColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.setProjectionMap(getProjectionMapForKeys()); - - break; - - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - qb.setTables(Tables.KEYS); - qb.appendWhere(KeysColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); - - qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.appendWhere(" AND " + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - qb.setProjectionMap(getProjectionMapForKeys()); - - break; - - case PUBLIC_KEY_RING_USER_ID: - case SECRET_KEY_RING_USER_ID: - qb.setTables(Tables.USER_IDS); - qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - break; - - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - qb.setTables(Tables.USER_IDS); - qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.appendWhere(" AND " + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - break; - - default: - throw new IllegalArgumentException("Unknown URI " + uri); - - } - - // If no sort order is specified use the default - String orderBy; - if (TextUtils.isEmpty(sortOrder)) { - orderBy = null; - } else { - orderBy = sortOrder; - } - - Cursor c = qb.query(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); - - if (Constants.DEBUG) { - Log.d(Constants.TAG, - "Query: " - + qb.buildQuery(projection, selection, selectionArgs, null, null, - orderBy, null)); - Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c)); - } - - return c; - } - - /** {@inheritDoc} */ - @Override - public Uri insert(Uri uri, ContentValues values) { - Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); - - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); - - Uri rowUri = null; - long rowId = -1; - try { - final int match = mUriMatcher.match(uri); - - switch (match) { - case PUBLIC_KEY_RING: - values.put(KeyRings.TYPE, KeyTypes.PUBLIC); - - rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); - rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); - - break; - case PUBLIC_KEY_RING_KEY: - values.put(Keys.TYPE, KeyTypes.PUBLIC); - - rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); - - break; - case PUBLIC_KEY_RING_USER_ID: - rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); - - break; - case SECRET_KEY_RING: - values.put(KeyRings.TYPE, KeyTypes.SECRET); - - rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); - rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); - - break; - case SECRET_KEY_RING_KEY: - values.put(Keys.TYPE, KeyTypes.SECRET); - - rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); - - break; - case SECRET_KEY_RING_USER_ID: - rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); - - break; - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - } catch (SQLiteConstraintException e) { - Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?"); - } - - // notify of changes in db - getContext().getContentResolver().notifyChange(uri, null); - - return rowUri; - } - - /** {@inheritDoc} */ - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - Log.v(Constants.TAG, "delete(uri=" + uri + ")"); - - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); - - int count; - final int match = mUriMatcher.match(uri); - - String defaultSelection = null; - switch (match) { - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); - // corresponding keys and userIds are deleted by ON DELETE CASCADE - count = db.delete(Tables.KEY_RINGS, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), - selectionArgs); - break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); - // corresponding keys and userIds are deleted by ON DELETE CASCADE - count = db.delete(Tables.KEY_RINGS, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), - selectionArgs); - break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - count = db.delete(Tables.KEYS, - buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); - break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), - selectionArgs); - break; - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - - // notify of changes in db - getContext().getContentResolver().notifyChange(uri, null); - - return count; - } - - /** {@inheritDoc} */ - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); - - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); - - String defaultSelection = null; - int count = 0; - try { - final int match = mUriMatcher.match(uri); - switch (match) { - case PUBLIC_KEY_RING_BY_ROW_ID: - case SECRET_KEY_RING_BY_ROW_ID: - defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); - - count = db.update( - Tables.KEY_RINGS, - values, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), - selection), selectionArgs); - break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: - case SECRET_KEY_RING_BY_MASTER_KEY_ID: - defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); - - count = db.update( - Tables.KEY_RINGS, - values, - buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), - selection), selectionArgs); - break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: - case SECRET_KEY_RING_KEY_BY_ROW_ID: - count = db - .update(Tables.KEYS, values, - buildDefaultKeysSelection(uri, getKeyType(match), selection), - selectionArgs); - break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: - case SECRET_KEY_RING_USER_ID_BY_ROW_ID: - count = db.update(Tables.USER_IDS, values, - buildDefaultUserIdsSelection(uri, selection), selectionArgs); - break; - default: - throw new UnsupportedOperationException("Unknown uri: " + uri); - } - } catch (SQLiteConstraintException e) { - Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?"); - } - - // notify of changes in db - getContext().getContentResolver().notifyChange(uri, null); - - return count; - } - - /** - * Build default selection statement for KeyRings. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType, - String selection) { - String andType = ""; - if (keyType != null) { - andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType; - } - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return defaultSelection + andType + andSelection; - } - - /** - * Build default selection statement for Keys. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultKeysSelection(Uri uri, Integer keyType, String selection) { - String rowId = uri.getLastPathSegment(); - - String foreignKeyRingRowId = uri.getPathSegments().get(2); - String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " - + foreignKeyRingRowId; - - String andType = ""; - if (keyType != null) { - andType = " AND " + KeysColumns.TYPE + "=" + keyType; - } - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andType + andSelection; - } - - /** - * Build default selection statement for UserIds. If no extra selection is specified only build - * where clause with rowId - * - * @param uri - * @param selection - * @return - */ - private String buildDefaultUserIdsSelection(Uri uri, String selection) { - String rowId = uri.getLastPathSegment(); - - String foreignKeyRingRowId = uri.getPathSegments().get(2); - String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " - + foreignKeyRingRowId; - - String andSelection = ""; - if (!TextUtils.isEmpty(selection)) { - andSelection = " AND (" + selection + ")"; - } - - return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection; - } - - @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.getLastPathSegment(); - File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName); - return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); - } -} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java b/APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java deleted file mode 100644 index 18a78e440..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgProviderExternal.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -/** - * The same content provider as ApgProviderInternal except that it does not give out keyRing and key - * blob data when querying. - * - * This provider is exported with a readPermission in AndroidManifest.xml - */ -public class ApgProviderExternal extends ApgProvider { - - public ApgProviderExternal() { - mInternalProvider = false; - } - -} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java b/APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java deleted file mode 100644 index b8b98f2c7..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgProviderInternal.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -/** - * This provider is NOT exported in AndroidManifest.xml as it also return the actual secret keys - * from the database - */ -public class ApgProviderInternal extends ApgProvider { - - public ApgProviderInternal() { - mInternalProvider = true; - } - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java deleted file mode 100644 index 5bf1dd329..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobContract.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import org.thialfihar.android.apg.Constants; - -import android.net.Uri; -import android.provider.BaseColumns; - -public class ApgServiceBlobContract { - - interface BlobsColumns { - String KEY = "key"; - } - - public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".blobs"; - - private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); - - public static class Blobs implements BlobsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI; - } - - private ApgServiceBlobContract() { - } -} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java deleted file mode 100644 index 417486e33..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobDatabase.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.provider; - -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.provider.BaseColumns; - -import org.thialfihar.android.apg.provider.ApgServiceBlobContract.BlobsColumns; - -public class ApgServiceBlobDatabase extends SQLiteOpenHelper { - private static final String DATABASE_NAME = "apg_blob.db"; - private static final int DATABASE_VERSION = 2; - - public static final String TABLE = "data"; - - public ApgServiceBlobDatabase(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE + " ( " + BaseColumns._ID - + " INTEGER PRIMARY KEY AUTOINCREMENT, " + BlobsColumns.KEY + " TEXT NOT NULL)"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - // no upgrade necessary yet - } -} diff --git a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java b/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java deleted file mode 100644 index 8053aecbf..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ApgServiceBlobProvider.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.provider; - -import org.thialfihar.android.apg.Constants; - -import android.content.ContentProvider; -import android.content.ContentUris; -import android.content.ContentValues; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.net.Uri; -import android.os.ParcelFileDescriptor; -import android.provider.BaseColumns; - -import org.thialfihar.android.apg.provider.ApgServiceBlobContract.Blobs; -import org.thialfihar.android.apg.provider.ApgServiceBlobContract.BlobsColumns; -import org.thialfihar.android.apg.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 STORE_PATH = Constants.path.APP_DIR + "/ApgBlobs"; - - private ApgServiceBlobDatabase mBlobDatabase = null; - - public ApgServiceBlobProvider() { - File dir = new File(STORE_PATH); - dir.mkdirs(); - } - - @Override - public boolean onCreate() { - mBlobDatabase = new ApgServiceBlobDatabase(getContext()); - return true; - } - - /** {@inheritDoc} */ - @Override - public Uri insert(Uri uri, ContentValues ignored) { - // 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(BlobsColumns.KEY, password); - - SQLiteDatabase db = mBlobDatabase.getWritableDatabase(); - long newRowId = db.insert(ApgServiceBlobDatabase.TABLE, null, vals); - Uri insertedUri = ContentUris.withAppendedId(Blobs.CONTENT_URI, newRowId); - - return Uri.withAppendedPath(insertedUri, password); - } - - /** {@inheritDoc} */ - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, - FileNotFoundException { - Log.d(Constants.TAG, "openFile() called with uri: " + uri.toString() + " and 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); - - Log.d(Constants.TAG, "Got id: " + id + " and key: " + key); - - // get the data - SQLiteDatabase db = mBlobDatabase.getReadableDatabase(); - Cursor result = db.query(ApgServiceBlobDatabase.TABLE, new String[] { BaseColumns._ID }, - BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?", - new String[] { id, key }, null, null, null); - - 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")) { - Log.d(Constants.TAG, "Try to open file w"); - if (!targetFile.exists()) { - try { - targetFile.createNewFile(); - } catch (IOException e) { - Log.e(Constants.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")) { - Log.d(Constants.TAG, "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; - } - - /** {@inheritDoc} */ - @Override - public String getType(Uri uri) { - return null; - } - - /** {@inheritDoc} */ - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - return null; - } - - /** {@inheritDoc} */ - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - /** {@inheritDoc} */ - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - return 0; - } - -} diff --git a/APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java b/APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java deleted file mode 100644 index 2a7ed2d19..000000000 --- a/APG/src/org/thialfihar/android/apg/provider/ProviderHelper.java +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.provider; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; - -import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.util.IterableIterator; -import org.thialfihar.android.apg.util.Log; - -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.net.Uri; -import android.os.RemoteException; - -public class ProviderHelper { - - /** - * Private helper method to get PGPKeyRing from database - * - * @param context - * @param queryUri - * @return - */ - private static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) { - Cursor cursor = context.getContentResolver().query(queryUri, - new String[] { KeyRings._ID, KeyRings.KEY_RING_DATA }, null, null, null); - - PGPKeyRing keyRing = null; - if (cursor != null && cursor.moveToFirst()) { - int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); - - byte[] data = cursor.getBlob(keyRingDataCol); - if (data != null) { - keyRing = PGPConversionHelper.BytesToPGPKeyRing(data); - } - } - - if (cursor != null) { - cursor.close(); - } - - return keyRing; - } - - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId - * - * @param context - * @param rowId - * @return - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId - * - * @param context - * @param masterKeyId - * @return - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context, - long masterKeyId) { - Uri queryUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key - * with this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) { - Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId)); - return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPPublicKey object from the database blob associated with a key with - * this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) { - PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - - return keyRing.getPublicKey(keyId); - } - - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId - * - * @param context - * @param rowId - * @return - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId - * - * @param context - * @param masterKeyId - * @return - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context, - long masterKeyId) { - Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key - * with this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) { - Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId)); - return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); - } - - /** - * Retrieves the actual PGPSecretKey object from the database blob associated with a key with - * this keyId - * - * @param context - * @param keyId - * @return - */ - public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) { - PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - - return keyRing.getSecretKey(keyId); - } - - /** - * Saves PGPPublicKeyRing with its keys and userIds in DB - * - * @param context - * @param keyRing - * @return - * @throws IOException - * @throws GeneralException - */ - @SuppressWarnings("unchecked") - public static void saveKeyRing(Context context, PGPPublicKeyRing keyRing) throws IOException { - PGPPublicKey masterKey = keyRing.getPublicKey(); - long masterKeyId = masterKey.getKeyID(); - - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - Uri deleteUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - - try { - context.getContentResolver().delete(deleteUri, null, null); - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); - } - - ContentValues values = new ContentValues(); - values.put(KeyRings.MASTER_KEY_ID, masterKeyId); - values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); - - // insert new version of this keyRing - Uri uri = KeyRings.buildPublicKeyRingsUri(); - Uri insertedUri = context.getContentResolver().insert(uri, values); - long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); - - // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); - - int rank = 0; - for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { - operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank)); - ++rank; - } - - int userIdRank = 0; - for (String userId : new IterableIterator(masterKey.getUserIDs())) { - operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank)); - ++userIdRank; - } - - try { - context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, - operations); - } catch (RemoteException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } catch (OperationApplicationException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } - } - - /** - * Saves PGPSecretKeyRing with its keys and userIds in DB - * - * @param context - * @param keyRing - * @return - * @throws IOException - * @throws GeneralException - */ - @SuppressWarnings("unchecked") - public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException { - PGPSecretKey masterKey = keyRing.getSecretKey(); - long masterKeyId = masterKey.getKeyID(); - - // delete old version of this keyRing, which also deletes all keys and userIds on cascade - Uri deleteUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); - - try { - context.getContentResolver().delete(deleteUri, null, null); - } catch (UnsupportedOperationException e) { - Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); - } - - ContentValues values = new ContentValues(); - values.put(KeyRings.MASTER_KEY_ID, masterKeyId); - values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); - - // insert new version of this keyRing - Uri uri = KeyRings.buildSecretKeyRingsUri(); - Uri insertedUri = context.getContentResolver().insert(uri, values); - long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); - - // save all keys and userIds included in keyRing object in database - ArrayList operations = new ArrayList(); - - int rank = 0; - for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { - operations.add(buildSecretKeyOperations(context, keyRingRowId, key, rank)); - ++rank; - } - - int userIdRank = 0; - for (String userId : new IterableIterator(masterKey.getUserIDs())) { - operations.add(buildSecretUserIdOperations(context, keyRingRowId, userId, userIdRank)); - ++userIdRank; - } - - try { - context.getContentResolver().applyBatch(ApgContract.CONTENT_AUTHORITY_INTERNAL, - operations); - } catch (RemoteException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } catch (OperationApplicationException e) { - Log.e(Constants.TAG, "applyBatch failed!", e); - } - } - - /** - * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildPublicKeyOperations(Context context, - long keyRingRowId, PGPPublicKey key, int rank) throws IOException { - ContentValues values = new ContentValues(); - values.put(Keys.KEY_ID, key.getKeyID()); - 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, PGPHelper.isSigningKey(key)); - values.put(Keys.CAN_ENCRYPT, PGPHelper.isEncryptionKey(key)); - values.put(Keys.IS_REVOKED, key.isRevoked()); - values.put(Keys.CREATION, PGPHelper.getCreationDate(key).getTime() / 1000); - Date expiryDate = PGPHelper.getExpiryDate(key); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); - values.put(Keys.KEY_DATA, key.getEncoded()); - values.put(Keys.RANK, rank); - - Uri uri = Keys.buildPublicKeysUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildPublicUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { - ContentValues values = new ContentValues(); - values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); - values.put(UserIds.USER_ID, userId); - values.put(UserIds.RANK, rank); - - Uri uri = UserIds.buildPublicUserIdsUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildSecretKeyOperations(Context context, - long keyRingRowId, PGPSecretKey key, int rank) throws IOException { - ContentValues values = new ContentValues(); - values.put(Keys.KEY_ID, key.getKeyID()); - 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, PGPHelper.isSigningKey(key)); - values.put(Keys.CAN_ENCRYPT, PGPHelper.isEncryptionKey(key)); - values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); - values.put(Keys.CREATION, PGPHelper.getCreationDate(key).getTime() / 1000); - Date expiryDate = PGPHelper.getExpiryDate(key); - if (expiryDate != null) { - values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); - } - values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); - values.put(Keys.KEY_DATA, key.getEncoded()); - values.put(Keys.RANK, rank); - - Uri uri = Keys.buildSecretKeysUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing - * - * @param context - * @param keyRingRowId - * @param key - * @param rank - * @return - * @throws IOException - */ - private static ContentProviderOperation buildSecretUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { - ContentValues values = new ContentValues(); - values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); - values.put(UserIds.USER_ID, userId); - values.put(UserIds.RANK, rank); - - Uri uri = UserIds.buildSecretUserIdsUri(Long.toString(keyRingRowId)); - - return ContentProviderOperation.newInsert(uri).withValues(values).build(); - } - - /** - * Private helper method - * - * @param context - * @param queryUri - * @return - */ - private static ArrayList getKeyRingsMasterKeyIds(Context context, Uri queryUri) { - Cursor cursor = context.getContentResolver().query(queryUri, - new String[] { KeyRings.MASTER_KEY_ID }, null, null, null); - - ArrayList masterKeyIds = new ArrayList(); - if (cursor != null) { - int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - if (cursor.moveToFirst()) { - do { - masterKeyIds.add(cursor.getLong(masterKeyIdCol)); - } while (cursor.moveToNext()); - } - } - - if (cursor != null) { - cursor.close(); - } - - return masterKeyIds; - } - - /** - * Retrieves ids of all SecretKeyRings - * - * @param context - * @return - */ - public static ArrayList getSecretKeyRingsMasterKeyIds(Context context) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(); - return getKeyRingsMasterKeyIds(context, queryUri); - } - - /** - * Retrieves ids of all PublicKeyRings - * - * @param context - * @return - */ - public static ArrayList getPublicKeyRingsMasterKeyIds(Context context) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(); - return getKeyRingsMasterKeyIds(context, queryUri); - } - - public static void deletePublicKeyRing(Context context, long rowId) { - ContentResolver cr = context.getContentResolver(); - cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null); - } - - public static void deleteSecretKeyRing(Context context, long rowId) { - ContentResolver cr = context.getContentResolver(); - cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); - } - - /** - * Get master key id of keyring by its row id - * - * @param context - * @param keyRingRowId - * @return - */ - public static long getPublicMasterKeyId(Context context, long keyRingRowId) { - Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyId(context, queryUri, keyRingRowId); - } - - /** - * Get master key id of keyring by its row id - * - * @param context - * @param keyRingRowId - * @return - */ - public static long getSecretMasterKeyId(Context context, long keyRingRowId) { - Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyId(context, queryUri, keyRingRowId); - } - - /** - * Private helper method to get master key id of keyring by its row id - * - * @param context - * @param queryUri - * @param keyRingRowId - * @return - */ - private static long getMasterKeyId(Context context, Uri queryUri, long keyRingRowId) { - String[] projection = new String[] { KeyRings.MASTER_KEY_ID }; - - ContentResolver cr = context.getContentResolver(); - Cursor cursor = cr.query(queryUri, projection, null, null, null); - - long masterKeyId = -1; - if (cursor != null && cursor.moveToFirst()) { - int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - - masterKeyId = cursor.getLong(masterKeyIdCol); - } - - if (cursor != null) { - cursor.close(); - } - - return masterKeyId; - } - - public static ArrayList getPublicKeyRingsAsArmoredString(Context context, - long[] masterKeyIds) { - return getKeyRingsAsArmoredString(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds); - } - - public static ArrayList getSecretKeyRingsAsArmoredString(Context context, - long[] masterKeyIds) { - return getKeyRingsAsArmoredString(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds); - } - - private static ArrayList getKeyRingsAsArmoredString(Context context, Uri uri, - long[] masterKeyIds) { - ArrayList output = new ArrayList(); - - if (masterKeyIds != null && masterKeyIds.length > 0) { - - Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds); - - if (cursor != null) { - int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); - if (cursor.moveToFirst()) { - do { - Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol)); - - // get actual keyring data blob and write it to ByteArrayOutputStream - try { - Object keyRing = null; - byte[] data = cursor.getBlob(dataCol); - if (data != null) { - keyRing = PGPConversionHelper.BytesToPGPKeyRing(data); - } - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ArmoredOutputStream aos = new ArmoredOutputStream(bos); - aos.setHeader("Version", PGPMain.getFullVersion(context)); - - if (keyRing instanceof PGPSecretKeyRing) { - aos.write(((PGPSecretKeyRing) keyRing).getEncoded()); - } else if (keyRing instanceof PGPPublicKeyRing) { - aos.write(((PGPPublicKeyRing) keyRing).getEncoded()); - } - aos.close(); - - String armoredKey = bos.toString("UTF-8"); - - Log.d(Constants.TAG, "armouredKey:" + armoredKey); - - output.add(armoredKey); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException", e); - } - } while (cursor.moveToNext()); - } - } - - if (cursor != null) { - cursor.close(); - } - - } else { - Log.e(Constants.TAG, "No master keys given!"); - } - - if (output.size() > 0) { - return output; - } else { - return null; - } - } - - public static byte[] getPublicKeyRingsAsByteArray(Context context, long[] masterKeyIds) { - return getKeyRingsAsByteArray(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds); - } - - public static byte[] getSecretKeyRingsAsByteArray(Context context, long[] masterKeyIds) { - return getKeyRingsAsByteArray(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds); - } - - private static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - if (masterKeyIds != null && masterKeyIds.length > 0) { - - Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds); - - if (cursor != null) { - int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); - int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); - if (cursor.moveToFirst()) { - do { - Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol)); - - // get actual keyring data blob and write it to ByteArrayOutputStream - try { - bos.write(cursor.getBlob(dataCol)); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException", e); - } - } while (cursor.moveToNext()); - } - } - - if (cursor != null) { - cursor.close(); - } - - } else { - Log.e(Constants.TAG, "No master keys given!"); - } - - return bos.toByteArray(); - } - - private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, Uri baseUri, - long[] masterKeyIds) { - Cursor cursor = null; - if (masterKeyIds != null && masterKeyIds.length > 0) { - - String inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN ("; - for (int i = 0; i < masterKeyIds.length; ++i) { - if (i != 0) { - inMasterKeyList += ", "; - } - inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]); - } - inMasterKeyList += ")"; - - cursor = context.getContentResolver().query(baseUri, - new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA }, - inMasterKeyList, null, null); - } - - return cursor; - } -} diff --git a/APG/src/org/thialfihar/android/apg/service/ApgApiService.java b/APG/src/org/thialfihar/android/apg/service/ApgApiService.java deleted file mode 100644 index 88f062b87..000000000 --- a/APG/src/org/thialfihar/android/apg/service/ApgApiService.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SignatureException; - -import org.spongycastle.openpgp.PGPException; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; -import org.thialfihar.android.apg.service.handler.IApgDecryptHandler; -import org.thialfihar.android.apg.service.handler.IApgEncryptHandler; -import org.thialfihar.android.apg.service.handler.IApgGetDecryptionKeyIdHandler; -import org.thialfihar.android.apg.util.InputData; -import org.thialfihar.android.apg.util.Log; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; - -public class ApgApiService extends Service { - Context mContext; - - @Override - public void onCreate() { - super.onCreate(); - mContext = this; - Log.d(Constants.TAG, "ApgApiService, onCreate()"); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.d(Constants.TAG, "ApgApiService, onDestroy()"); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - // 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 synchronized void encryptAndSignSafe(byte[] inputBytes, String inputUri, - boolean useAsciiArmor, int compression, long[] encryptionKeyIds, - String encryptionPassphrase, int symmetricEncryptionAlgorithm, long signatureKeyId, - int signatureHashAlgorithm, boolean signatureForceV3, String signaturePassphrase, - IApgEncryptHandler handler) throws RemoteException { - - try { - // TODO use inputUri - - // 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? - - // build InputData and write into OutputStream - InputStream inputStream = new ByteArrayInputStream(inputBytes); - long inputLength = inputBytes.length; - InputData input = new InputData(inputStream, inputLength); - - OutputStream output = new ByteArrayOutputStream(); - - PGPMain.encryptAndSign(mContext, null, input, output, useAsciiArmor, compression, - encryptionKeyIds, encryptionPassphrase, symmetricEncryptionAlgorithm, - signatureKeyId, signatureHashAlgorithm, signatureForceV3, signaturePassphrase); - - output.close(); - - // 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()); - // } - - byte[] outputBytes = ((ByteArrayOutputStream) output).toByteArray(); - - // return over handler on client side - handler.onSuccess(outputBytes, null); - } catch (Exception e) { - Log.e(Constants.TAG, "ApgService, Exception!", e); - - try { - handler.onException(getExceptionId(e), e.getMessage()); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - private synchronized void decryptAndVerifySafe(byte[] inputBytes, String inputUri, - String passphrase, boolean assumeSymmetric, IApgDecryptHandler handler) - throws RemoteException { - - try { - // build InputData and write into OutputStream - InputStream inputStream = new ByteArrayInputStream(inputBytes); - long inputLength = inputBytes.length; - InputData inputData = new InputData(inputStream, inputLength); - - OutputStream outputStream = new ByteArrayOutputStream(); - - Bundle outputBundle = PGPMain.decryptAndVerify(mContext, null, inputData, outputStream, - passphrase, assumeSymmetric); - - outputStream.close(); - - byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); - - // get signature informations from bundle - boolean signature = outputBundle.getBoolean(ApgIntentService.RESULT_SIGNATURE); - long signatureKeyId = outputBundle.getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); - String signatureUserId = outputBundle - .getString(ApgIntentService.RESULT_SIGNATURE_USER_ID); - boolean signatureSuccess = outputBundle - .getBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS); - boolean signatureUnknown = outputBundle - .getBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN); - - // return over handler on client side - handler.onSuccess(outputBytes, null, signature, signatureKeyId, signatureUserId, - signatureSuccess, signatureUnknown); - } catch (Exception e) { - Log.e(Constants.TAG, "ApgService, Exception!", e); - - try { - handler.onException(getExceptionId(e), e.getMessage()); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - private synchronized void getDecryptionKeySafe(byte[] inputBytes, String inputUri, - IApgGetDecryptionKeyIdHandler handler) { - - // TODO: implement inputUri - - try { - InputStream inputStream = new ByteArrayInputStream(inputBytes); - - long secretKeyId = Id.key.none; - boolean symmetric; - - try { - secretKeyId = PGPMain.getDecryptionKeyId(ApgApiService.this, inputStream); - if (secretKeyId == Id.key.none) { - throw new ApgGeneralException(getString(R.string.error_noSecretKeyFound)); - } - symmetric = false; - } catch (PGPMain.NoAsymmetricEncryptionException e) { - secretKeyId = Id.key.symmetric; - if (!PGPMain.hasSymmetricEncryption(ApgApiService.this, inputStream)) { - throw new ApgGeneralException(getString(R.string.error_noKnownEncryptionFound)); - } - symmetric = true; - } - - handler.onSuccess(secretKeyId, symmetric); - - } catch (Exception e) { - Log.e(Constants.TAG, "ApgService, Exception!", e); - - try { - handler.onException(getExceptionId(e), e.getMessage()); - } catch (Exception t) { - Log.e(Constants.TAG, "Error returning exception to client", t); - } - } - } - - /** - * This is the implementation of the interface IApgService. All methods are oneway, meaning - * asynchronous and return to the client using IApgHandler. - * - * The real PGP code is located in PGPMain. - */ - private final IApgApiService.Stub mBinder = new IApgApiService.Stub() { - - @Override - public void encryptAsymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, - int compression, long[] encryptionKeyIds, int symmetricEncryptionAlgorithm, - IApgEncryptHandler handler) throws RemoteException { - - encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, encryptionKeyIds, - null, symmetricEncryptionAlgorithm, Id.key.none, 0, false, null, handler); - } - - @Override - public void encryptSymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, - int compression, String encryptionPassphrase, int symmetricEncryptionAlgorithm, - IApgEncryptHandler handler) throws RemoteException { - - encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, null, - encryptionPassphrase, symmetricEncryptionAlgorithm, Id.key.none, 0, false, - null, handler); - } - - @Override - public void encryptAndSignAsymmetric(byte[] inputBytes, String inputUri, - boolean useAsciiArmor, int compression, long[] encryptionKeyIds, - int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, - boolean signatureForceV3, String signaturePassphrase, IApgEncryptHandler handler) - throws RemoteException { - - encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, encryptionKeyIds, - null, symmetricEncryptionAlgorithm, signatureKeyId, signatureHashAlgorithm, - signatureForceV3, signaturePassphrase, handler); - } - - @Override - public void encryptAndSignSymmetric(byte[] inputBytes, String inputUri, - boolean useAsciiArmor, int compression, String encryptionPassphrase, - int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, - boolean signatureForceV3, String signaturePassphrase, IApgEncryptHandler handler) - throws RemoteException { - - encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, null, - encryptionPassphrase, symmetricEncryptionAlgorithm, signatureKeyId, - signatureHashAlgorithm, signatureForceV3, signaturePassphrase, handler); - } - - @Override - public void decryptAndVerifyAsymmetric(byte[] inputBytes, String inputUri, - String keyPassphrase, IApgDecryptHandler handler) throws RemoteException { - - decryptAndVerifySafe(inputBytes, inputUri, keyPassphrase, false, handler); - } - - @Override - public void decryptAndVerifySymmetric(byte[] inputBytes, String inputUri, - String encryptionPassphrase, IApgDecryptHandler handler) throws RemoteException { - - decryptAndVerifySafe(inputBytes, inputUri, encryptionPassphrase, true, handler); - } - - @Override - public void getDecryptionKeyId(byte[] inputBytes, String inputUri, - IApgGetDecryptionKeyIdHandler handler) throws RemoteException { - - getDecryptionKeySafe(inputBytes, inputUri, handler); - } - - }; - - /** - * As we can not throw an exception through Android RPC, we assign identifiers to the exception - * types. - * - * @param e - * @return - */ - private int getExceptionId(Exception e) { - if (e instanceof NoSuchProviderException) { - return 0; - } else if (e instanceof NoSuchAlgorithmException) { - return 1; - } else if (e instanceof SignatureException) { - return 2; - } else if (e instanceof IOException) { - return 3; - } else if (e instanceof ApgGeneralException) { - return 4; - } else if (e instanceof PGPException) { - return 5; - } else { - return -1; - } - } - -} diff --git a/APG/src/org/thialfihar/android/apg/service/ApgIntentService.java b/APG/src/org/thialfihar/android/apg/service/ApgIntentService.java deleted file mode 100644 index 11460c7d8..000000000 --- a/APG/src/org/thialfihar/android/apg/service/ApgIntentService.java +++ /dev/null @@ -1,884 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.FileHelper; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.provider.ApgContract.DataStream; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.util.HkpKeyServer; -import org.thialfihar.android.apg.util.InputData; -import org.thialfihar.android.apg.util.KeyServer.KeyInfo; -import org.thialfihar.android.apg.util.ProgressDialogUpdater; - -import android.app.IntentService; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; - -import org.thialfihar.android.apg.util.Log; - -/** - * This Service contains all important long lasting operations for APG. It receives Intents with - * data from the activities or other apps, queues these intents, executes them, and stops itself - * after doing them. - */ -public class ApgIntentService extends IntentService implements ProgressDialogUpdater { - - /* extras that can be given by intent */ - public static final String EXTRA_MESSENGER = "messenger"; - public static final String EXTRA_ACTION = "action"; - public static final String EXTRA_DATA = "data"; - - /* possible EXTRA_ACTIONs */ - public static final int ACTION_ENCRYPT_SIGN = 10; - - public static final int ACTION_DECRYPT_VERIFY = 20; - - public static final int ACTION_SAVE_KEYRING = 30; - public static final int ACTION_GENERATE_KEY = 31; - public static final int ACTION_GENERATE_DEFAULT_RSA_KEYS = 32; - - public static final int ACTION_DELETE_FILE_SECURELY = 40; - - public static final int ACTION_IMPORT_KEYRING = 50; - public static final int ACTION_EXPORT_KEYRING = 51; - - public static final int ACTION_UPLOAD_KEYRING = 60; - public static final int ACTION_QUERY_KEYRING = 61; - - public static final int ACTION_SIGN_KEYRING = 70; - - /* keys for data bundle */ - - // encrypt, decrypt, import export - public static final String TARGET = "target"; - // possible targets: - public static final int TARGET_BYTES = 1; - public static final int TARGET_FILE = 2; - public static final int TARGET_STREAM = 3; - - // encrypt - public static final String ENCRYPT_SECRET_KEY_ID = "secretKeyId"; - public static final String ENCRYPT_USE_ASCII_AMOR = "useAsciiAmor"; - public static final String ENCRYPT_ENCRYPTION_KEYS_IDS = "encryptionKeysIds"; - public static final String ENCRYPT_COMPRESSION_ID = "compressionId"; - public static final String ENCRYPT_GENERATE_SIGNATURE = "generateSignature"; - public static final String ENCRYPT_SIGN_ONLY = "signOnly"; - public static final String ENCRYPT_MESSAGE_BYTES = "messageBytes"; - public static final String ENCRYPT_INPUT_FILE = "inputFile"; - public static final String ENCRYPT_OUTPUT_FILE = "outputFile"; - public static final String ENCRYPT_PROVIDER_URI = "providerUri"; - - // decrypt/verify - public static final String DECRYPT_SIGNED_ONLY = "signedOnly"; - public static final String DECRYPT_RETURN_BYTES = "returnBinary"; - public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertextBytes"; - public static final String DECRYPT_ASSUME_SYMMETRIC = "assumeSymmetric"; - public static final String DECRYPT_LOOKUP_UNKNOWN_KEY = "lookupUnknownKey"; - - // save keyring - public static final String SAVE_KEYRING_NEW_PASSPHRASE = "newPassphrase"; - public static final String SAVE_KEYRING_CURRENT_PASSPHRASE = "currentPassphrase"; - public static final String SAVE_KEYRING_USER_IDS = "userIds"; - public static final String SAVE_KEYRING_KEYS = "keys"; - public static final String SAVE_KEYRING_KEYS_USAGES = "keysUsages"; - public static final String SAVE_KEYRING_MASTER_KEY_ID = "masterKeyId"; - - // generate key - public static final String GENERATE_KEY_ALGORITHM = "algorithm"; - public static final String GENERATE_KEY_KEY_SIZE = "keySize"; - public static final String GENERATE_KEY_SYMMETRIC_PASSPHRASE = "passphrase"; - public static final String GENERATE_KEY_MASTER_KEY = "masterKey"; - - // delete file securely - public static final String DELETE_FILE = "deleteFile"; - - // import key - public static final String IMPORT_INPUT_STREAM = "importInputStream"; - public static final String IMPORT_FILENAME = "importFilename"; - public static final String IMPORT_BYTES = "importBytes"; - // public static final String IMPORT_KEY_TYPE = "importKeyType"; - - // export key - public static final String EXPORT_OUTPUT_STREAM = "exportOutputStream"; - public static final String EXPORT_FILENAME = "exportFilename"; - public static final String EXPORT_KEY_TYPE = "exportKeyType"; - public static final String EXPORT_ALL = "exportAll"; - public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "exportKeyRingId"; - - // upload key - public static final String UPLOAD_KEY_SERVER = "uploadKeyServer"; - public static final String UPLOAD_KEY_KEYRING_ROW_ID = "uploadKeyRingId"; - - // query key - public static final String QUERY_KEY_SERVER = "queryKeyServer"; - public static final String QUERY_KEY_TYPE = "queryKeyType"; - public static final String QUERY_KEY_STRING = "queryKeyString"; - public static final String QUERY_KEY_ID = "queryKeyId"; - - // sign key - public static final String SIGN_KEY_MASTER_KEY_ID = "signKeyMasterKeyId"; - public static final String SIGN_KEY_PUB_KEY_ID = "signKeyPubKeyId"; - - /* - * possible data keys as result send over messenger - */ - // keys - public static final String RESULT_NEW_KEY = "newKey"; - public static final String RESULT_NEW_KEY2 = "newKey2"; - - // encrypt - public static final String RESULT_SIGNATURE_BYTES = "signatureData"; - public static final String RESULT_SIGNATURE_STRING = "signatureText"; - public static final String RESULT_ENCRYPTED_STRING = "encryptedMessage"; - public static final String RESULT_ENCRYPTED_BYTES = "encryptedData"; - public static final String RESULT_URI = "resultUri"; - - // decrypt/verify - public static final String RESULT_DECRYPTED_STRING = "decryptedMessage"; - public static final String RESULT_DECRYPTED_BYTES = "decryptedData"; - public static final String RESULT_SIGNATURE = "signature"; - public static final String RESULT_SIGNATURE_KEY_ID = "signatureKeyId"; - public static final String RESULT_SIGNATURE_USER_ID = "signatureUserId"; - - public static final String RESULT_SIGNATURE_SUCCESS = "signatureSuccess"; - public static final String RESULT_SIGNATURE_UNKNOWN = "signatureUnknown"; - public static final String RESULT_SIGNATURE_LOOKUP_KEY = "lookupKey"; - - // import - public static final String RESULT_IMPORT_ADDED = "added"; - public static final String RESULT_IMPORT_UPDATED = "updated"; - public static final String RESULT_IMPORT_BAD = "bad"; - - // export - public static final String RESULT_EXPORT = "exported"; - - // query - public static final String RESULT_QUERY_KEY_KEY_DATA = "queryKeyKeyData"; - public static final String RESULT_QUERY_KEY_SEARCH_RESULT = "queryKeySearchResult"; - - Messenger mMessenger; - - public ApgIntentService() { - super("ApgService"); - } - - /** - * The IntentService calls this method from the default worker thread with the intent that - * started the service. When this method returns, IntentService stops the service, as - * appropriate. - */ - @Override - protected void onHandleIntent(Intent intent) { - Bundle extras = intent.getExtras(); - if (extras == null) { - Log.e(Constants.TAG, "Extras bundle is null!"); - return; - } - - if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || extras - .containsKey(EXTRA_ACTION))) { - Log.e(Constants.TAG, - "Extra bundle must contain a messenger, a data bundle, and an action!"); - return; - } - - mMessenger = (Messenger) extras.get(EXTRA_MESSENGER); - Bundle data = extras.getBundle(EXTRA_DATA); - - OtherHelper.logDebugBundle(data, "EXTRA_DATA"); - - int action = extras.getInt(EXTRA_ACTION); - - // execute action from extra bundle - switch (action) { - case ACTION_ENCRYPT_SIGN: - - try { - /* Input */ - int target = data.getInt(TARGET); - - long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID); - String encryptionPassphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); - - boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_AMOR); - long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS); - int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID); - boolean generateSignature = data.getBoolean(ENCRYPT_GENERATE_SIGNATURE); - boolean signOnly = data.getBoolean(ENCRYPT_SIGN_ONLY); - - InputStream inStream = null; - long inLength = -1; - InputData inputData = null; - OutputStream outStream = null; - String streamFilename = null; - switch (target) { - case TARGET_BYTES: /* encrypting bytes directly */ - byte[] bytes = data.getByteArray(ENCRYPT_MESSAGE_BYTES); - - inStream = new ByteArrayInputStream(bytes); - inLength = bytes.length; - - inputData = new InputData(inStream, inLength); - outStream = new ByteArrayOutputStream(); - - break; - case TARGET_FILE: /* encrypting file */ - String inputFile = data.getString(ENCRYPT_INPUT_FILE); - String outputFile = data.getString(ENCRYPT_OUTPUT_FILE); - - // check if storage is ready - if (!FileHelper.isStorageMounted(inputFile) - || !FileHelper.isStorageMounted(outputFile)) { - throw new ApgGeneralException( - getString(R.string.error_externalStorageNotReady)); - } - - inStream = new FileInputStream(inputFile); - File file = new File(inputFile); - inLength = file.length(); - inputData = new InputData(inStream, inLength); - - outStream = new FileOutputStream(outputFile); - - break; - - case TARGET_STREAM: /* Encrypting stream from content uri */ - Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI); - - // InputStream - InputStream in = getContentResolver().openInputStream(providerUri); - inLength = PGPMain.getLengthOfStream(in); - inputData = new InputData(in, inLength); - - // OutputStream - try { - while (true) { - streamFilename = PGPMain.generateRandomFilename(32); - if (streamFilename == null) { - throw new PGPMain.ApgGeneralException( - "couldn't generate random file name"); - } - openFileInput(streamFilename).close(); - } - } catch (FileNotFoundException e) { - // found a name that isn't used yet - } - outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); - - break; - - default: - throw new PGPMain.ApgGeneralException("No target choosen!"); - - } - - /* Operation */ - - if (generateSignature) { - Log.d(Constants.TAG, "generating signature..."); - PGPMain.generateSignature(this, this, inputData, outStream, useAsciiArmor, - false, secretKeyId, PassphraseCacheService.getCachedPassphrase(this, - secretKeyId), Preferences.getPreferences(this) - .getDefaultHashAlgorithm(), Preferences.getPreferences(this) - .getForceV3Signatures()); - } else if (signOnly) { - Log.d(Constants.TAG, "sign only..."); - PGPMain.signText(this, this, inputData, outStream, secretKeyId, - PassphraseCacheService.getCachedPassphrase(this, secretKeyId), - Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences - .getPreferences(this).getForceV3Signatures()); - } else { - Log.d(Constants.TAG, "encrypt..."); - PGPMain.encryptAndSign(this, this, inputData, outStream, useAsciiArmor, - compressionId, encryptionKeyIds, encryptionPassphrase, Preferences - .getPreferences(this).getDefaultEncryptionAlgorithm(), - secretKeyId, - Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences - .getPreferences(this).getForceV3Signatures(), - PassphraseCacheService.getCachedPassphrase(this, secretKeyId)); - } - - outStream.close(); - - /* Output */ - - Bundle resultData = new Bundle(); - - switch (target) { - case TARGET_BYTES: - if (useAsciiArmor) { - String output = new String( - ((ByteArrayOutputStream) outStream).toByteArray()); - if (generateSignature) { - resultData.putString(RESULT_SIGNATURE_STRING, output); - } else { - resultData.putString(RESULT_ENCRYPTED_STRING, output); - } - } else { - byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); - if (generateSignature) { - resultData.putByteArray(RESULT_SIGNATURE_BYTES, output); - } else { - resultData.putByteArray(RESULT_ENCRYPTED_BYTES, output); - } - } - - break; - case TARGET_FILE: - // nothing, file was written, just send okay - - break; - case TARGET_STREAM: - String uri = DataStream.buildDataStreamUri(streamFilename).toString(); - resultData.putString(RESULT_URI, uri); - - break; - } - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_DECRYPT_VERIFY: - try { - /* Input */ - int target = data.getInt(TARGET); - - long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID); - byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES); - boolean signedOnly = data.getBoolean(DECRYPT_SIGNED_ONLY); - boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES); - boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC); - - boolean lookupUnknownKey = data.getBoolean(DECRYPT_LOOKUP_UNKNOWN_KEY); - - InputStream inStream = null; - long inLength = -1; - InputData inputData = null; - OutputStream outStream = null; - String streamFilename = null; - switch (target) { - case TARGET_BYTES: /* decrypting bytes directly */ - inStream = new ByteArrayInputStream(bytes); - inLength = bytes.length; - - inputData = new InputData(inStream, inLength); - outStream = new ByteArrayOutputStream(); - - break; - - case TARGET_FILE: /* decrypting file */ - String inputFile = data.getString(ENCRYPT_INPUT_FILE); - String outputFile = data.getString(ENCRYPT_OUTPUT_FILE); - - // check if storage is ready - if (!FileHelper.isStorageMounted(inputFile) - || !FileHelper.isStorageMounted(outputFile)) { - throw new ApgGeneralException( - getString(R.string.error_externalStorageNotReady)); - } - - // InputStream - inLength = -1; - inStream = new FileInputStream(inputFile); - File file = new File(inputFile); - inLength = file.length(); - inputData = new InputData(inStream, inLength); - - // OutputStream - outStream = new FileOutputStream(outputFile); - - break; - - case TARGET_STREAM: /* decrypting stream from content uri */ - Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI); - - // InputStream - InputStream in = getContentResolver().openInputStream(providerUri); - inLength = PGPMain.getLengthOfStream(in); - inputData = new InputData(in, inLength); - - // OutputStream - try { - while (true) { - streamFilename = PGPMain.generateRandomFilename(32); - if (streamFilename == null) { - throw new PGPMain.ApgGeneralException( - "couldn't generate random file name"); - } - openFileInput(streamFilename).close(); - } - } catch (FileNotFoundException e) { - // found a name that isn't used yet - } - outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); - - break; - - default: - throw new PGPMain.ApgGeneralException("No target choosen!"); - - } - - /* Operation */ - - Bundle resultData = new Bundle(); - - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - if (signedOnly) { - resultData = PGPMain.verifyText(this, this, inputData, outStream, - lookupUnknownKey); - } else { - resultData = PGPMain.decryptAndVerify(this, this, inputData, outStream, - PassphraseCacheService.getCachedPassphrase(this, secretKeyId), - assumeSymmetricEncryption); - } - - outStream.close(); - - /* Output */ - - switch (target) { - case TARGET_BYTES: - if (returnBytes) { - byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); - resultData.putByteArray(RESULT_DECRYPTED_BYTES, output); - } else { - String output = new String( - ((ByteArrayOutputStream) outStream).toByteArray()); - resultData.putString(RESULT_DECRYPTED_STRING, output); - } - - break; - case TARGET_FILE: - // nothing, file was written, just send okay and verification bundle - - break; - case TARGET_STREAM: - String uri = DataStream.buildDataStreamUri(streamFilename).toString(); - resultData.putString(RESULT_URI, uri); - - break; - } - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_SAVE_KEYRING: - - try { - /* Input */ - String oldPassPhrase = data.getString(SAVE_KEYRING_CURRENT_PASSPHRASE); - String newPassPhrase = data.getString(SAVE_KEYRING_NEW_PASSPHRASE); - if (newPassPhrase == null) { - newPassPhrase = oldPassPhrase; - } - ArrayList userIds = data.getStringArrayList(SAVE_KEYRING_USER_IDS); - ArrayList keys = PGPConversionHelper.BytesToPGPSecretKeyList(data - .getByteArray(SAVE_KEYRING_KEYS)); - ArrayList keysUsages = data.getIntegerArrayList(SAVE_KEYRING_KEYS_USAGES); - long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); - - /* Operation */ - PGPMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase, - newPassPhrase, this); - PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); - - /* Output */ - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_GENERATE_KEY: - - try { - /* Input */ - int algorithm = data.getInt(GENERATE_KEY_ALGORITHM); - String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); - int keysize = data.getInt(GENERATE_KEY_KEY_SIZE); - PGPSecretKey masterKey = null; - if (data.containsKey(GENERATE_KEY_MASTER_KEY)) { - masterKey = PGPConversionHelper.BytesToPGPSecretKey(data - .getByteArray(GENERATE_KEY_MASTER_KEY)); - } - - /* Operation */ - PGPSecretKeyRing newKeyRing = PGPMain.createKey(this, algorithm, keysize, - passphrase, masterKey); - - /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, - PGPConversionHelper.PGPSecretKeyRingToBytes(newKeyRing)); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_GENERATE_DEFAULT_RSA_KEYS: - // generate one RSA 2048 key for signing and one subkey for encrypting! - try { - /* Input */ - String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); - - /* Operation */ - PGPSecretKeyRing masterKeyRing = PGPMain.createKey(this, Id.choice.algorithm.rsa, - 2048, passphrase, null); - - PGPSecretKeyRing subKeyRing = PGPMain.createKey(this, Id.choice.algorithm.rsa, - 2048, passphrase, masterKeyRing.getSecretKey()); - - /* Output */ - Bundle resultData = new Bundle(); - resultData.putByteArray(RESULT_NEW_KEY, - PGPConversionHelper.PGPSecretKeyRingToBytes(masterKeyRing)); - resultData.putByteArray(RESULT_NEW_KEY2, - PGPConversionHelper.PGPSecretKeyRingToBytes(subKeyRing)); - - OtherHelper.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_DELETE_FILE_SECURELY: - try { - /* Input */ - String deleteFile = data.getString(DELETE_FILE); - - /* Operation */ - try { - PGPMain.deleteFileSecurely(this, this, new File(deleteFile)); - } catch (FileNotFoundException e) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_fileNotFound, - deleteFile)); - } catch (IOException e) { - throw new PGPMain.ApgGeneralException(getString( - R.string.error_fileDeleteFailed, deleteFile)); - } - - /* Output */ - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_IMPORT_KEYRING: - try { - - /* Input */ - int target = data.getInt(TARGET); - - // int keyType = Id.type.public_key; - // if (data.containsKey(IMPORT_KEY_TYPE)) { - // keyType = data.getInt(IMPORT_KEY_TYPE); - // } - - /* Operation */ - InputStream inStream = null; - long inLength = -1; - InputData inputData = null; - switch (target) { - case TARGET_BYTES: /* import key from bytes directly */ - byte[] bytes = data.getByteArray(IMPORT_BYTES); - - inStream = new ByteArrayInputStream(bytes); - inLength = bytes.length; - - inputData = new InputData(inStream, inLength); - - break; - case TARGET_FILE: /* import key from file */ - String inputFile = data.getString(IMPORT_FILENAME); - - inStream = new FileInputStream(inputFile); - File file = new File(inputFile); - inLength = file.length(); - inputData = new InputData(inStream, inLength); - - break; - - case TARGET_STREAM: - // TODO: not implemented - break; - } - - Bundle resultData = new Bundle(); - resultData = PGPMain.importKeyRings(this, inputData, this); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_EXPORT_KEYRING: - try { - - /* Input */ - int keyType = Id.type.public_key; - if (data.containsKey(EXPORT_KEY_TYPE)) { - keyType = data.getInt(EXPORT_KEY_TYPE); - } - - String outputFile = data.getString(EXPORT_FILENAME); - - boolean exportAll = data.getBoolean(EXPORT_ALL); - long keyRingMasterKeyId = -1; - if (!exportAll) { - keyRingMasterKeyId = data.getLong(EXPORT_KEY_RING_MASTER_KEY_ID); - } - - /* Operation */ - - // check if storage is ready - if (!FileHelper.isStorageMounted(outputFile)) { - throw new ApgGeneralException(getString(R.string.error_externalStorageNotReady)); - } - - // OutputStream - FileOutputStream outStream = new FileOutputStream(outputFile); - - ArrayList keyRingMasterKeyIds = new ArrayList(); - if (exportAll) { - // get all key ring row ids based on export type - - if (keyType == Id.type.public_key) { - keyRingMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this); - } else { - keyRingMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this); - } - } else { - keyRingMasterKeyIds.add(keyRingMasterKeyId); - } - - Bundle resultData = new Bundle(); - resultData = PGPMain.exportKeyRings(this, keyRingMasterKeyIds, keyType, outStream, - this); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_UPLOAD_KEYRING: - try { - - /* Input */ - int keyRingRowId = data.getInt(UPLOAD_KEY_KEYRING_ROW_ID); - String keyServer = data.getString(UPLOAD_KEY_SERVER); - - /* Operation */ - HkpKeyServer server = new HkpKeyServer(keyServer); - - PGPPublicKeyRing keyring = ProviderHelper.getPGPPublicKeyRingByRowId(this, - keyRingRowId); - if (keyring != null) { - boolean uploaded = PGPMain.uploadKeyRingToServer(server, - (PGPPublicKeyRing) keyring); - if (!uploaded) { - throw new ApgGeneralException("Unable to export key to selected server"); - } - } - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_QUERY_KEYRING: - try { - - /* Input */ - int queryType = data.getInt(QUERY_KEY_TYPE); - String keyServer = data.getString(QUERY_KEY_SERVER); - - String queryString = data.getString(QUERY_KEY_STRING); - long keyId = data.getLong(QUERY_KEY_ID); - - /* Operation */ - Bundle resultData = new Bundle(); - - HkpKeyServer server = new HkpKeyServer(keyServer); - if (queryType == Id.keyserver.search) { - ArrayList searchResult = server.search(queryString); - - resultData.putParcelableArrayList(RESULT_QUERY_KEY_SEARCH_RESULT, searchResult); - } else if (queryType == Id.keyserver.get) { - String keyData = server.get(keyId); - - resultData.putString(RESULT_QUERY_KEY_KEY_DATA, keyData); - } - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - case ACTION_SIGN_KEYRING: - try { - - /* Input */ - long masterKeyId = data.getLong(SIGN_KEY_MASTER_KEY_ID); - long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID); - - /* Operation */ - String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this, - masterKeyId); - - PGPPublicKeyRing signedPubKeyRing = PGPMain.signKey(this, masterKeyId, pubKeyId, - signaturePassPhrase); - - // store the signed key in our local cache - int retval = PGPMain.storeKeyRingInCache(this, signedPubKeyRing); - if (retval != Id.return_value.ok && retval != Id.return_value.updated) { - throw new ApgGeneralException("Failed to store signed key in local cache"); - } - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY); - } catch (Exception e) { - sendErrorToHandler(e); - } - - break; - - default: - break; - } - - } - - private void sendErrorToHandler(Exception e) { - Log.e(Constants.TAG, "ApgService Exception: ", e); - e.printStackTrace(); - - Bundle data = new Bundle(); - data.putString(ApgIntentServiceHandler.DATA_ERROR, e.getMessage()); - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_EXCEPTION, null, data); - } - - private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) { - Message msg = Message.obtain(); - msg.arg1 = arg1; - if (arg2 != null) { - msg.arg2 = arg2; - } - if (data != null) { - msg.setData(data); - } - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } - - private void sendMessageToHandler(Integer arg1, Bundle data) { - sendMessageToHandler(arg1, null, data); - } - - private void sendMessageToHandler(Integer arg1) { - sendMessageToHandler(arg1, null, null); - } - - /** - * Set progress of ProgressDialog by sending message to handler on UI thread - */ - public void setProgress(String message, int progress, int max) { - Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max=" - + max); - - Bundle data = new Bundle(); - if (message != null) { - data.putString(ApgIntentServiceHandler.DATA_MESSAGE, message); - } - data.putInt(ApgIntentServiceHandler.DATA_PROGRESS, progress); - data.putInt(ApgIntentServiceHandler.DATA_PROGRESS_MAX, max); - - sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_UPDATE_PROGRESS, null, data); - } - - public void setProgress(int resourceId, int progress, int max) { - setProgress(getString(resourceId), progress, max); - } - - public void setProgress(int progress, int max) { - setProgress(null, progress, max); - } -} diff --git a/APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java b/APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java deleted file mode 100644 index 8494cf035..000000000 --- a/APG/src/org/thialfihar/android/apg/service/ApgIntentServiceHandler.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service; - -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment; - -import android.app.Activity; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.support.v4.app.FragmentActivity; -import android.widget.Toast; - -public class ApgIntentServiceHandler extends Handler { - - // possible messages send from this service to handler on ui - public static final int MESSAGE_OKAY = 1; - public static final int MESSAGE_EXCEPTION = 2; - public static final int MESSAGE_UPDATE_PROGRESS = 3; - - // possible data keys for messages - public static final String DATA_ERROR = "error"; - public static final String DATA_PROGRESS = "progress"; - public static final String DATA_PROGRESS_MAX = "max"; - public static final String DATA_MESSAGE = "message"; - public static final String DATA_MESSAGE_ID = "message_id"; - - Activity mActivity; - ProgressDialogFragment mProgressDialogFragment; - - public ApgIntentServiceHandler(Activity activity) { - this.mActivity = activity; - } - - public ApgIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) { - this.mActivity = activity; - this.mProgressDialogFragment = progressDialogFragment; - } - - public ApgIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) { - this.mActivity = activity; - this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId, - progressDialogStyle); - } - - public void showProgressDialog(FragmentActivity activity) { - mProgressDialogFragment.show(activity.getSupportFragmentManager(), "progressDialog"); - } - - @Override - public void handleMessage(Message message) { - Bundle data = message.getData(); - - switch (message.arg1) { - case MESSAGE_OKAY: - mProgressDialogFragment.dismiss(); - - break; - - case MESSAGE_EXCEPTION: - mProgressDialogFragment.dismiss(); - - // show error from service - if (data.containsKey(DATA_ERROR)) { - Toast.makeText(mActivity, - mActivity.getString(R.string.errorMessage, data.getString(DATA_ERROR)), - Toast.LENGTH_SHORT).show(); - } - - break; - - case MESSAGE_UPDATE_PROGRESS: - if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) { - - // update progress from service - if (data.containsKey(DATA_MESSAGE)) { - mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE), - data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); - } else if (data.containsKey(DATA_MESSAGE_ID)) { - mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID), - data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); - } else { - mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS), - data.getInt(DATA_PROGRESS_MAX)); - } - } - - break; - - default: - break; - } - } -} diff --git a/APG/src/org/thialfihar/android/apg/service/ApgKeyService.java b/APG/src/org/thialfihar/android/apg/service/ApgKeyService.java deleted file mode 100644 index 4a7d517c9..000000000 --- a/APG/src/org/thialfihar/android/apg/service/ApgKeyService.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service; - -import java.util.ArrayList; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.handler.IApgGetKeyringsHandler; -import org.thialfihar.android.apg.util.Log; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.IBinder; -import android.os.RemoteException; - -public class ApgKeyService extends Service { - Context mContext; - - @Override - public void onCreate() { - super.onCreate(); - mContext = this; - Log.d(Constants.TAG, "ApgKeyService, onCreate()"); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.d(Constants.TAG, "ApgKeyService, onDestroy()"); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - /** - * Synchronized implementation of getPublicKeyRings - */ - private synchronized void getPublicKeyRingsSafe(long[] masterKeyIds, - boolean asAsciiArmoredStringArray, IApgGetKeyringsHandler handler) - throws RemoteException { - if (asAsciiArmoredStringArray) { - ArrayList output = ProviderHelper.getPublicKeyRingsAsArmoredString(mContext, - masterKeyIds); - - handler.onSuccess(null, output); - } else { - byte[] outputBytes = ProviderHelper - .getPublicKeyRingsAsByteArray(mContext, masterKeyIds); - handler.onSuccess(outputBytes, null); - } - } - - /** - * Synchronized implementation of getSecretKeyRings - */ - private synchronized void getSecretKeyRingsSafe(long[] masterKeyIds, - boolean asAsciiArmoredStringArray, IApgGetKeyringsHandler handler) - throws RemoteException { - if (asAsciiArmoredStringArray) { - ArrayList output = ProviderHelper.getSecretKeyRingsAsArmoredString(mContext, - masterKeyIds); - - handler.onSuccess(null, output); - } else { - byte[] outputBytes = ProviderHelper - .getSecretKeyRingsAsByteArray(mContext, masterKeyIds); - handler.onSuccess(outputBytes, null); - } - - } - - /** - * This is the implementation of the interface IApgKeyService. All methods are oneway, meaning - * asynchronous and return to the client using handlers. - * - * The real PGP code is located in PGPMain. - */ - private final IApgKeyService.Stub mBinder = new IApgKeyService.Stub() { - - @Override - public void getPublicKeyRings(long[] masterKeyIds, boolean asAsciiArmoredStringArray, - IApgGetKeyringsHandler handler) throws RemoteException { - getPublicKeyRingsSafe(masterKeyIds, asAsciiArmoredStringArray, handler); - } - - @Override - public void getSecretKeyRings(long[] masterKeyIds, boolean asAsciiArmoredStringArray, - IApgGetKeyringsHandler handler) throws RemoteException { - getSecretKeyRingsSafe(masterKeyIds, asAsciiArmoredStringArray, handler); - } - - }; - - /** - * As we can not throw an exception through Android RPC, we assign identifiers to the exception - * types. - * - * @param e - * @return - */ - // private int getExceptionId(Exception e) { - // if (e instanceof NoSuchProviderException) { - // return 0; - // } else if (e instanceof NoSuchAlgorithmException) { - // return 1; - // } else if (e instanceof SignatureException) { - // return 2; - // } else if (e instanceof IOException) { - // return 3; - // } else if (e instanceof ApgGeneralException) { - // return 4; - // } else if (e instanceof PGPException) { - // return 5; - // } else { - // return -1; - // } - // } - -} diff --git a/APG/src/org/thialfihar/android/apg/service/IApgApiService.aidl b/APG/src/org/thialfihar/android/apg/service/IApgApiService.aidl deleted file mode 100644 index 277ce6722..000000000 --- a/APG/src/org/thialfihar/android/apg/service/IApgApiService.aidl +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service; - -import org.thialfihar.android.apg.service.handler.IApgEncryptHandler; -import org.thialfihar.android.apg.service.handler.IApgDecryptHandler; -import org.thialfihar.android.apg.service.handler.IApgGetDecryptionKeyIdHandler; - -/** - * All methods are oneway, which means they are asynchronous and non-blocking. - * Results are returned into given Handler, which has to be implemented on client side. - */ -interface IApgApiService { - - /** - * Encrypt - * - * Either inputBytes or inputUri is given, the other should be null. - * - * @param inputBytes - * Byte array you want to encrypt - * @param inputUri - * Blob in ContentProvider you want to encrypt - * @param useAsciiArmor - * Convert bytes to ascii armored text to guard against encoding problems - * @param compression - * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 - * @param encryptionKeyIds - * Ids of public keys used for encryption - * @param symmetricEncryptionAlgorithm - * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, - * 6: DES, 2: Triple DES, 1: IDEA - * @param handler - * Results are returned to this IApgEncryptDecryptHandler Handler - * to onSuccessEncrypt(in byte[] output), after successful encryption - */ - oneway void encryptAsymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, - in int compression, in long[] encryptionKeyIds, in int symmetricEncryptionAlgorithm, - in IApgEncryptHandler handler); - - /** - * Same as encryptAsymmetric but using a passphrase for symmetric encryption - * - * @param encryptionPassphrase - * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm - */ - oneway void encryptSymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, - in int compression, in String encryptionPassphrase, in int symmetricEncryptionAlgorithm, - in IApgEncryptHandler handler); - - /** - * Encrypt and sign - * - * Either inputBytes or inputUri is given, the other should be null. - * - * @param inputBytes - * Byte array you want to encrypt - * @param inputUri - * Blob in ContentProvider you want to encrypt - * @param useAsciiArmor - * Convert bytes to ascii armored text to guard against encoding problems - * @param compression - * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 - * @param encryptionKeyIds - * Ids of public keys used for encryption - * @param symmetricEncryptionAlgorithm - * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, - * 6: DES, 2: Triple DES, 1: IDEA - * @param signatureKeyId - * Key id of key to sign with - * @param signatureHashAlgorithm - * 1: MD5, 3: RIPEMD-160, 2: SHA-1, 11: SHA-224, 8: SHA-256, 9: SHA-384, - * 10: SHA-512 - * @param signatureForceV3 - * Force V3 signatures - * @param signaturePassphrase - * Passphrase to unlock signature key - * @param handler - * Results are returned to this IApgEncryptDecryptHandler Handler - * to onSuccessEncrypt(in byte[] output), after successful encryption and signing - */ - oneway void encryptAndSignAsymmetric(in byte[] inputBytes, in String inputUri, - in boolean useAsciiArmor, in int compression, in long[] encryptionKeyIds, - in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, - in boolean signatureForceV3, in String signaturePassphrase, - in IApgEncryptHandler handler); - - /** - * Same as encryptAndSignAsymmetric but using a passphrase for symmetric encryption - * - * @param encryptionPassphrase - * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm - */ - oneway void encryptAndSignSymmetric(in byte[] inputBytes, in String inputUri, - in boolean useAsciiArmor, in int compression, in String encryptionPassphrase, - in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, - in boolean signatureForceV3, in String signaturePassphrase, - in IApgEncryptHandler handler); - - /** - * Decrypts and verifies given input bytes. If no signature is present this method - * will only decrypt. - * - * @param inputBytes - * Byte array you want to decrypt and verify - * @param inputUri - * Blob in ContentProvider you want to decrypt and verify - * @param keyPassphrase - * Passphrase to unlock secret key for decryption. - * @param handler - * Handler where to return results to after successful encryption - */ - oneway void decryptAndVerifyAsymmetric(in byte[] inputBytes, in String inputUri, - in String keyPassphrase, in IApgDecryptHandler handler); - - /** - * Same as decryptAndVerifyAsymmetric but for symmetric decryption. - * - * @param encryptionPassphrase - * Passphrase to decrypt - */ - oneway void decryptAndVerifySymmetric(in byte[] inputBytes, in String inputUri, - in String encryptionPassphrase, in IApgDecryptHandler handler); - - /** - * - */ - oneway void getDecryptionKeyId(in byte[] inputBytes, in String inputUri, - in IApgGetDecryptionKeyIdHandler handler); - - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/IApgKeyService.aidl b/APG/src/org/thialfihar/android/apg/service/IApgKeyService.aidl deleted file mode 100644 index 12ecddc17..000000000 --- a/APG/src/org/thialfihar/android/apg/service/IApgKeyService.aidl +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service; - -import org.thialfihar.android.apg.service.handler.IApgGetKeyringsHandler; - -/** - * All methods are oneway, which means they are asynchronous and non-blocking. - * Results are returned into given Handler, which has to be implemented on client side. - */ -interface IApgKeyService { - - oneway void getPublicKeyRings(in long[] masterKeyIds, in boolean asAsciiArmoredStringArray, - in IApgGetKeyringsHandler handler); - - oneway void getSecretKeyRings(in long[] masterKeyIds, in boolean asAsciiArmoredStringArray, - in IApgGetKeyringsHandler handler); -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java b/APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java deleted file mode 100644 index c2bc48aa7..000000000 --- a/APG/src/org/thialfihar/android/apg/service/PassphraseCacheService.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service; - -import java.util.Date; -import java.util.HashMap; - -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.provider.ProviderHelper; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Binder; -import android.os.IBinder; -import android.util.Log; - -public class PassphraseCacheService extends Service { - public static final String TAG = Constants.TAG + ": PassphraseCacheService"; - - public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX - + "PASSPHRASE_CACHE_SERVICE"; - - public static final String EXTRA_TTL = "ttl"; - public static final String EXTRA_KEY_ID = "keyId"; - public static final String EXTRA_PASSPHRASE = "passphrase"; - - private static final int REQUEST_ID = 0; - private static final long DEFAULT_TTL = 15; - - private BroadcastReceiver mIntentReceiver; - - // This is static to be easily retrieved by getCachedPassphrase() without the need of callback - // functions - private static HashMap mPassphraseCache = new HashMap(); - - /** - * This caches a new passphrase by sending a new command to the service. An android service is - * only run once. Thus, when the service is already started, new commands just add new events to - * the alarm manager for new passphrases to let them timeout in the future. - * - * @param context - * @param keyId - * @param passphrase - */ - public static void addCachedPassphrase(Context context, long keyId, String passphrase) { - Log.d(TAG, "cacheNewPassphrase() for " + keyId); - - Intent intent = new Intent(context, PassphraseCacheService.class); - intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl()); - intent.putExtra(EXTRA_PASSPHRASE, passphrase); - intent.putExtra(EXTRA_KEY_ID, keyId); - - context.startService(intent); - } - - /** - * Gets a cached passphrase from memory - * - * @param context - * @param keyId - * @return - */ - public static String getCachedPassphrase(Context context, long keyId) { - // try to get master key id which is used as an identifier for cached passphrases - long masterKeyId = keyId; - if (masterKeyId != Id.key.symmetric) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - PGPSecretKey masterKey = PGPHelper.getMasterKey(keyRing); - if (masterKey == null) { - return null; - } - masterKeyId = masterKey.getKeyID(); - } - - // get cached passphrase - String cachedPassphrase = mPassphraseCache.get(masterKeyId); - if (cachedPassphrase == null) { - return null; - } - // set it again to reset the cache life cycle - Log.d(TAG, "Cache passphrase again when getting it!"); - addCachedPassphrase(context, masterKeyId, cachedPassphrase); - - return cachedPassphrase; - } - - /** - * Register BroadcastReceiver that is unregistered when service is destroyed. This - * BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout - * specific passphrases in memory. - */ - private void registerReceiver() { - if (mIntentReceiver == null) { - mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - - Log.d(TAG, "Received broadcast..."); - - if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) { - long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); - timeout(context, keyId); - } - } - }; - - IntentFilter filter = new IntentFilter(); - filter.addAction(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); - registerReceiver(mIntentReceiver, filter); - } - } - - /** - * Build pending intent that is executed by alarm manager to time out a specific passphrase - * - * @param context - * @param keyId - * @return - */ - private static PendingIntent buildIntent(Context context, long keyId) { - Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); - intent.putExtra(EXTRA_KEY_ID, keyId); - PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent, - PendingIntent.FLAG_CANCEL_CURRENT); - - return sender; - } - - @Override - public void onCreate() { - Log.d(TAG, "onCreate()"); - } - - /** - * Executed when service is started by intent - */ - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Log.d(TAG, "onStartCommand()"); - - // register broadcastreceiver - registerReceiver(); - - if (intent != null) { - long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL); - long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); - String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); - - Log.d(TAG, "Received intent in onStartCommand() with keyId: " + keyId + ", ttl: " + ttl); - - // add keyId and passphrase to memory - mPassphraseCache.put(keyId, passphrase); - - // register new alarm with keyId for this passphrase - long triggerTime = new Date().getTime() + (ttl * 1000); - AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); - am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId)); - } - - return START_STICKY; - } - - /** - * Called when one specific passphrase for keyId timed out - * - * @param context - * @param keyId - */ - private void timeout(Context context, long keyId) { - // remove passphrase corresponding to keyId from memory - mPassphraseCache.remove(keyId); - - Log.d(TAG, "Timeout of " + keyId + ", removed from memory!"); - - // stop whole service if no cached passphrases remaining - if (mPassphraseCache.isEmpty()) { - Log.d(TAG, "No passphrases remaining in memory, stopping service!"); - stopSelf(); - } - } - - @Override - public void onDestroy() { - Log.d(TAG, "onDestroy()"); - - unregisterReceiver(mIntentReceiver); - } - - public class PassphraseCacheBinder extends Binder { - public PassphraseCacheService getService() { - return PassphraseCacheService.this; - } - } - - private final IBinder mBinder = new PassphraseCacheBinder(); - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/handler/IApgDecryptHandler.aidl b/APG/src/org/thialfihar/android/apg/service/handler/IApgDecryptHandler.aidl deleted file mode 100644 index feb56bce6..000000000 --- a/APG/src/org/thialfihar/android/apg/service/handler/IApgDecryptHandler.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service.handler; - -interface IApgDecryptHandler { - - oneway void onSuccess(in byte[] outputBytes, in String outputUri, in boolean signature, - in long signatureKeyId, in String signatureUserId, in boolean signatureSuccess, - in boolean signatureUnknown); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/handler/IApgEncryptHandler.aidl b/APG/src/org/thialfihar/android/apg/service/handler/IApgEncryptHandler.aidl deleted file mode 100644 index be0317715..000000000 --- a/APG/src/org/thialfihar/android/apg/service/handler/IApgEncryptHandler.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service.handler; - -interface IApgEncryptHandler { - /** - * Either output or streamUri is given. One of them is null - * - */ - oneway void onSuccess(in byte[] outputBytes, in String outputUri); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/handler/IApgGetDecryptionKeyIdHandler.aidl b/APG/src/org/thialfihar/android/apg/service/handler/IApgGetDecryptionKeyIdHandler.aidl deleted file mode 100644 index 020a80b99..000000000 --- a/APG/src/org/thialfihar/android/apg/service/handler/IApgGetDecryptionKeyIdHandler.aidl +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service.handler; - -interface IApgGetDecryptionKeyIdHandler { - - oneway void onSuccess(in long secretKeyId, in boolean symmetric); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/handler/IApgGetKeyringsHandler.aidl b/APG/src/org/thialfihar/android/apg/service/handler/IApgGetKeyringsHandler.aidl deleted file mode 100644 index ffa9d0d2d..000000000 --- a/APG/src/org/thialfihar/android/apg/service/handler/IApgGetKeyringsHandler.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service.handler; - -interface IApgGetKeyringsHandler { - /** - * Either outputBytes or outputString is given. One of them is null - * - */ - oneway void onSuccess(in byte[] outputBytes, in List outputString); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/handler/IApgSignHandler.aidl b/APG/src/org/thialfihar/android/apg/service/handler/IApgSignHandler.aidl deleted file mode 100644 index cf91e8bc4..000000000 --- a/APG/src/org/thialfihar/android/apg/service/handler/IApgSignHandler.aidl +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service.handler; - -interface IApgSignHandler { - /** - * Either output or streamUri is given. One of them is null - * - */ - oneway void onSuccess(in byte[] outputBytes, in String outputUri); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/service/handler/IApgVerifyHandler.aidl b/APG/src/org/thialfihar/android/apg/service/handler/IApgVerifyHandler.aidl deleted file mode 100644 index 6baaec758..000000000 --- a/APG/src/org/thialfihar/android/apg/service/handler/IApgVerifyHandler.aidl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.service.handler; - -interface IApgVerifyHandler { - - oneway void onSuccess(in boolean signature, in long signatureKeyId, - in String signatureUserId, in boolean signatureSuccess, in boolean signatureUnknown); - - - oneway void onException(in int exceptionNumber, in String message); -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java b/APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java deleted file mode 100644 index 671b2b48e..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/DecryptActivity.java +++ /dev/null @@ -1,946 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.compatibility.ClipboardReflection; -import org.thialfihar.android.apg.helper.FileHelper; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.LookupUnknownKeyDialogFragment; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import org.thialfihar.android.apg.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.animation.AnimationUtils; -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.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.regex.Matcher; - -public class DecryptActivity extends SherlockFragmentActivity { - - /* Intents */ - // without permission - public static final String ACTION_DECRYPT = Constants.INTENT_PREFIX + "DECRYPT"; - public static final String ACTION_DECRYPT_FILE = Constants.INTENT_PREFIX + "DECRYPT_FILE"; - - // with permission - public static final String ACTION_DECRYPT_AND_RETURN = Constants.INTENT_PREFIX - + "DECRYPT_AND_RETURN"; - public static final String ACTION_DECRYPT_STREAM_AND_RETURN = Constants.INTENT_PREFIX - + "DECRYPT_STREAM_AND_RETURN"; - - /* EXTRA keys for input */ - public static final String EXTRA_TEXT = "text"; - public static final String EXTRA_DATA = "data"; - public static final String EXTRA_REPLY_TO = "replyTo"; - public static final String EXTRA_SUBJECT = "subject"; - public static final String EXTRA_BINARY = "binary"; - - private long mSignatureKeyId = 0; - - 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 boolean mDecryptEnabled = true; - private String mDecryptString = ""; - private boolean mReplyEnabled = true; - private String mReplyString = ""; - - 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[] mDataBytes = null; - private boolean mReturnBinary = false; - - private long mUnknownSignatureKeyId = 0; - - private long mSecretKeyId = Id.key.none; - - private FileDialogFragment mFileDialog; - - private boolean mLookupUnknownKey = true; - - private boolean mDecryptImmediately = false; - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - if (mDecryptEnabled) { - menu.add(1, Id.menu.option.decrypt, 0, mDecryptString).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - if (mReplyEnabled) { - menu.add(1, Id.menu.option.reply, 1, mReplyString).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.decrypt: { - decryptClicked(); - - return true; - } - case Id.menu.option.reply: { - replyClicked(); - - return true; - } - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - private void initView() { - 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); - 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) { - FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*", - Id.request.filename); - } - }); - - mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterDecryption); - - // default: message source - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceMessage) { - mSource.showNext(); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check permissions for intent actions without user interaction - String[] restrictedActions = new String[] { ACTION_DECRYPT_AND_RETURN }; - OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), - Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); - - setContentView(R.layout.decrypt); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - initView(); - - // Handle intent actions - handleActions(getIntent()); - - if (mSource.getCurrentView().getId() == R.id.sourceMessage - && mMessage.getText().length() == 0) { - - CharSequence clipboardText = ClipboardReflection.getClipboardText(this); - - String data = ""; - if (clipboardText != null) { - Matcher matcher = PGPMain.PGP_MESSAGE.matcher(clipboardText); - if (!matcher.matches()) { - matcher = PGPMain.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 = ProviderHelper.getPGPPublicKeyRingByKeyId( - DecryptActivity.this, mSignatureKeyId); - if (key != null) { - Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class); - intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); - intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, mSignatureKeyId); - startActivity(intent); - } - } - }); - - mReplyEnabled = false; - - // build new actionbar - invalidateOptionsMenu(); - - 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 (mDecryptImmediately - || (mSource.getCurrentView().getId() == R.id.sourceMessage && (mMessage.getText() - .length() > 0 || mDataBytes != null || mContentUri != null))) { - decryptClicked(); - } - } - - /** - * Handles all actions with this intent - * - * @param intent - */ - private void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - String type = intent.getType(); - Uri uri = intent.getData(); - - if (extras == null) { - extras = new Bundle(); - } - - /* - * Android's Action - */ - if (Intent.ACTION_SEND.equals(action) && type != null) { - // When sending to APG Encrypt via share menu - if ("text/plain".equals(type)) { - // Plain text - String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); - if (sharedText != null) { - // handle like normal text decryption, override action and extras to later - // execute ACTION_DECRYPT in main actions - extras.putString(EXTRA_TEXT, sharedText); - action = ACTION_DECRYPT; - } - } else { - // Binary via content provider (could also be files) - // override uri to get stream from send - uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); - action = ACTION_DECRYPT_FILE; - } - } else if (Intent.ACTION_VIEW.equals(action)) { - // Android's Action when opening file associated to APG (see AndroidManifest.xml) - - // override action - action = ACTION_DECRYPT_FILE; - - // EVERYTHING ELSE IS OLD CODE - // This gets the Uri, where an inputStream can be opened from - // mContentUri = intent.getData(); - - // TODO: old implementation of ACTION_VIEW. Is this used in K9? - // 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 - // } - - // same as ACTION_DECRYPT_FILE but decrypt it immediately - // handleActionDecryptFile(intent); - // mDecryptImmediately = true; - } - - /** - * Main Actions - */ - if (ACTION_DECRYPT.equals(action)) { - mDataBytes = extras.getByteArray(EXTRA_DATA); - String textData = null; - if (mDataBytes == null) { - Log.d(Constants.TAG, "EXTRA_DATA was null"); - textData = extras.getString(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 = PGPMain.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 = PGPMain.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); - - mDecryptString = getString(R.string.btn_verify); - // build new action bar - invalidateOptionsMenu(); - } else { - Log.d(Constants.TAG, "Nothing matched!"); - } - } - } - mReplyTo = extras.getString(EXTRA_REPLY_TO); - mSubject = extras.getString(EXTRA_SUBJECT); - } else if (ACTION_DECRYPT_FILE.equals(action)) { - // get file path from uri - String path = FileHelper.getPath(this, uri); - - if (path != null) { - mInputFilename = path; - mFilename.setText(mInputFilename); - guessOutputFilename(); - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceFile) { - mSource.showNext(); - } - } else { - Log.e(Constants.TAG, - "Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_DECRYPT_STREAM_AND_RETURN."); - Toast.makeText(this, R.string.error_onlyFilesAreSupported, Toast.LENGTH_LONG) - .show(); - // end activity - finish(); - } - } else if (ACTION_DECRYPT_AND_RETURN.equals(action)) { - mReturnBinary = extras.getBoolean(EXTRA_BINARY, false); - - if (mContentUri == null) { - mDataBytes = extras.getByteArray(EXTRA_DATA); - String data = extras.getString(EXTRA_TEXT); - if (data != null) { - Matcher matcher = PGPMain.PGP_MESSAGE.matcher(data); - if (matcher.matches()) { - data = matcher.group(1); - // replace non breakable spaces - data = data.replaceAll("\\xa0", " "); - mMessage.setText(data); - } else { - matcher = PGPMain.PGP_SIGNED_MESSAGE.matcher(data); - if (matcher.matches()) { - data = matcher.group(1); - // replace non breakable spaces - data = data.replaceAll("\\xa0", " "); - mMessage.setText(data); - mDecryptString = getString(R.string.btn_verify); - - // build new action bar - invalidateOptionsMenu(); - } - } - } - } - mReturnResult = true; - } else if (ACTION_DECRYPT_STREAM_AND_RETURN.equals(action)) { - // TODO: Implement decrypt stream - } - } - - 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); - mDecryptString = getString(R.string.btn_decrypt); - - // build new action bar - invalidateOptionsMenu(); - break; - } - - case R.id.sourceMessage: { - mSourceLabel.setText(R.string.label_message); - mDecryptString = getString(R.string.btn_decrypt); - - // build new action bar - invalidateOptionsMenu(); - 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 = PGPMain.PGP_SIGNED_MESSAGE.matcher(messageData); - if (matcher.matches()) { - mSignedOnly = true; - decryptStart(); - return; - } - } - - // else treat it as an decrypted message/file - mSignedOnly = false; - - getDecryptionKeyFromInputStream(); - - // if we need a symmetric passphrase or a passphrase to use a secret key ask for it - if (mSecretKeyId == Id.key.symmetric - || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { - showPassphraseDialog(); - } else { - if (mDecryptTarget == Id.target.file) { - askForOutputFilename(); - } else { - decryptStart(); - } - } - } - - /** - * Shows passphrase dialog to cache a new passphrase the user enters for using it later for - * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks - * for a symmetric passphrase - */ - private void showPassphraseDialog() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - if (mDecryptTarget == Id.target.file) { - askForOutputFilename(); - } else { - decryptStart(); - } - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, - messenger, mSecretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - /** - * TODO: Rework function, remove global variables - */ - private void getDecryptionKeyFromInputStream() { - InputStream inStream = null; - if (mContentUri != null) { - try { - inStream = getContentResolver().openInputStream(mContentUri); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "File not found!", e); - Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } else if (mDecryptTarget == Id.target.file) { - // check if storage is ready - if (!FileHelper.isStorageMounted(mInputFilename)) { - Toast.makeText(this, getString(R.string.error_externalStorageNotReady), - Toast.LENGTH_SHORT).show(); - return; - } - - try { - inStream = new FileInputStream(mInputFilename); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "File not found!", e); - Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } else { - if (mDataBytes != null) { - inStream = new ByteArrayInputStream(mDataBytes); - } else { - inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); - } - } - - // get decryption key for this inStream - try { - try { - mSecretKeyId = PGPMain.getDecryptionKeyId(this, inStream); - if (mSecretKeyId == Id.key.none) { - throw new PGPMain.ApgGeneralException( - getString(R.string.error_noSecretKeyFound)); - } - mAssumeSymmetricEncryption = false; - } catch (PGPMain.NoAsymmetricEncryptionException e) { - mSecretKeyId = Id.key.symmetric; - if (!PGPMain.hasSymmetricEncryption(this, inStream)) { - throw new PGPMain.ApgGeneralException( - getString(R.string.error_noKnownEncryptionFound)); - } - mAssumeSymmetricEncryption = true; - } - } catch (Exception e) { - Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } - - private void replyClicked() { - Intent intent = new Intent(this, EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - String data = mMessage.getText().toString(); - data = data.replaceAll("(?m)^", "> "); - data = "\n\n" + data; - intent.putExtra(EncryptActivity.EXTRA_TEXT, data); - intent.putExtra(EncryptActivity.EXTRA_SUBJECT, "Re: " + mSubject); - intent.putExtra(EncryptActivity.EXTRA_SEND_TO, mReplyTo); - intent.putExtra(EncryptActivity.EXTRA_SIGNATURE_KEY_ID, mSecretKeyId); - intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId }); - startActivity(intent); - } - - private void askForOutputFilename() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - decryptStart(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - mFileDialog = FileDialogFragment.newInstance(messenger, - getString(R.string.title_decryptToFile), - getString(R.string.specifyFileToDecryptTo), mOutputFilename, null, - Id.request.output_filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - - private void lookupUnknownKey(long unknownKeyId) { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_OKAY) { - // the result is handled by onActivityResult() as LookupUnknownKeyDialogFragment - // starts a new Intent which then returns data - } else if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_CANCEL) { - // decrypt again, but don't lookup unknown keys! - mLookupUnknownKey = false; - decryptStart(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - LookupUnknownKeyDialogFragment lookupKeyDialog = LookupUnknownKeyDialogFragment - .newInstance(messenger, unknownKeyId); - - lookupKeyDialog.show(getSupportFragmentManager(), "unknownKeyDialog"); - } - - private void decryptStart() { - Log.d(Constants.TAG, "decryptStart"); - - // Send all information needed to service to decrypt in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_DECRYPT_VERIFY); - - // choose action based on input: decrypt stream, file or bytes - if (mContentUri != null) { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_STREAM); - - data.putParcelable(ApgIntentService.ENCRYPT_PROVIDER_URI, mContentUri); - } else if (mDecryptTarget == Id.target.file) { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); - - Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" - + mOutputFilename); - - data.putString(ApgIntentService.ENCRYPT_INPUT_FILE, mInputFilename); - data.putString(ApgIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename); - } else { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); - - if (mDataBytes != null) { - data.putByteArray(ApgIntentService.DECRYPT_CIPHERTEXT_BYTES, mDataBytes); - } else { - String message = mMessage.getText().toString(); - data.putByteArray(ApgIntentService.DECRYPT_CIPHERTEXT_BYTES, message.getBytes()); - } - } - - data.putLong(ApgIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId); - - data.putBoolean(ApgIntentService.DECRYPT_SIGNED_ONLY, mSignedOnly); - data.putBoolean(ApgIntentService.DECRYPT_LOOKUP_UNKNOWN_KEY, mLookupUnknownKey); - data.putBoolean(ApgIntentService.DECRYPT_RETURN_BYTES, mReturnBinary); - data.putBoolean(ApgIntentService.DECRYPT_ASSUME_SYMMETRIC, mAssumeSymmetricEncryption); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after encrypting is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - // if key is unknown show lookup dialog - if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_LOOKUP_KEY) - && mLookupUnknownKey) { - mUnknownSignatureKeyId = returnData - .getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); - lookupUnknownKey(mUnknownSignatureKeyId); - return; - } - - mSignatureKeyId = 0; - mSignatureLayout.setVisibility(View.GONE); - mReplyEnabled = false; - - // build new action bar - invalidateOptionsMenu(); - - Toast.makeText(DecryptActivity.this, R.string.decryptionSuccessful, - Toast.LENGTH_SHORT).show(); - if (mReturnResult) { - Intent intent = new Intent(); - intent.putExtras(returnData); - setResult(RESULT_OK, intent); - finish(); - return; - } - - switch (mDecryptTarget) { - case Id.target.message: - String decryptedMessage = returnData - .getString(ApgIntentService.RESULT_DECRYPTED_STRING); - mMessage.setText(decryptedMessage); - mMessage.setHorizontallyScrolling(false); - mReplyEnabled = false; - - // build new action bar - invalidateOptionsMenu(); - break; - - case Id.target.file: - if (mDeleteAfter.isChecked()) { - // Create and show dialog to delete original file - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment - .newInstance(mInputFilename); - deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); - } - break; - - default: - // shouldn't happen - break; - - } - - if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE)) { - String userId = returnData - .getString(ApgIntentService.RESULT_SIGNATURE_USER_ID); - mSignatureKeyId = returnData - .getLong(ApgIntentService.RESULT_SIGNATURE_KEY_ID); - mUserIdRest - .setText("id: " + PGPHelper.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 (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_SUCCESS)) { - mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); - } else if (returnData.getBoolean(ApgIntentService.RESULT_SIGNATURE_UNKNOWN)) { - mSignatureStatusImage.setImageResource(R.drawable.overlay_error); - Toast.makeText(DecryptActivity.this, - R.string.unknownSignatureKeyTouchToLookUp, Toast.LENGTH_LONG) - .show(); - } else { - mSignatureStatusImage.setImageResource(R.drawable.overlay_error); - } - mSignatureLayout.setVisibility(View.VISIBLE); - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = FileHelper.getPath(this, data.getData()); - Log.d(Constants.TAG, "path=" + path); - - mFilename.setText(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - case Id.request.output_filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - mFileDialog.setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - // this request is returned after LookupUnknownKeyDialogFragment started - // KeyServerQueryActivity and user looked uo key - case Id.request.look_up_key_id: { - Log.d(Constants.TAG, "Returning from Lookup Key..."); - // decrypt again without lookup - mLookupUnknownKey = false; - decryptStart(); - return; - } - - default: { - break; - } - } - - super.onActivityResult(requestCode, resultCode, data); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java deleted file mode 100644 index 39074bcea..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/EditKeyActivity.java +++ /dev/null @@ -1,593 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.ui.dialog.SetPassphraseDialogFragment; -import org.thialfihar.android.apg.ui.widget.KeyEditor; -import org.thialfihar.android.apg.ui.widget.SectionView; -import org.thialfihar.android.apg.ui.widget.UserIdEditor; -import org.thialfihar.android.apg.util.IterableIterator; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import org.thialfihar.android.apg.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.LinearLayout; -import android.widget.Toast; -import android.widget.CompoundButton.OnCheckedChangeListener; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Vector; - -public class EditKeyActivity extends SherlockFragmentActivity { - - // possible intent actions for this activity - public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY"; - public static final String ACTION_EDIT_KEY = Constants.INTENT_PREFIX + "EDIT_KEY"; - - // possible extra keys - public static final String EXTRA_USER_IDS = "userIds"; - public static final String EXTRA_NO_PASSPHRASE = "noPassphrase"; - public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generateDefaultKeys"; - public static final String EXTRA_KEY_ID = "keyId"; - - // results when saving key - public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; - public static final String RESULT_EXTRA_USER_ID = "userId"; - - private ActionBar mActionBar; - - private PGPSecretKeyRing mKeyRing = null; - - private SectionView mUserIdsView; - private SectionView mKeysView; - - private String mCurrentPassPhrase = null; - private String mNewPassPhrase = null; - - private Button mChangePassPhrase; - - private CheckBox mNoPassphrase; - - Vector mUserIds; - Vector mKeys; - Vector mKeysUsages; - - // will be set to false to build layout later in handler - private boolean mBuildLayout = true; - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.save, 1, R.string.btn_save).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, KeyListSecretActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.save: - saveClicked(); - return true; - - case Id.menu.option.cancel: - cancelClicked(); - return true; - - default: - break; - - } - return false; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check permissions for intent actions without user interaction - String[] restrictedActions = new String[] { ACTION_CREATE_KEY }; - OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), - Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); - - setContentView(R.layout.edit_key); - - mActionBar = getSupportActionBar(); - mActionBar.setDisplayShowTitleEnabled(true); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - // find views - mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase); - mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase); - - mUserIds = new Vector(); - mKeys = new Vector(); - mKeysUsages = new Vector(); - - // Catch Intents opened from other apps - Intent intent = getIntent(); - String action = intent.getAction(); - if (ACTION_CREATE_KEY.equals(action)) { - handleActionCreateKey(intent); - } else if (ACTION_EDIT_KEY.equals(action)) { - handleActionEditKey(intent); - } - - mChangePassPhrase.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - showSetPassphraseDialog(); - } - }); - - // disable passphrase when no passphrase checkobox is checked! - mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() { - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - // remove passphrase - mNewPassPhrase = null; - - mChangePassPhrase.setVisibility(View.GONE); - } else { - mChangePassPhrase.setVisibility(View.VISIBLE); - } - } - }); - - if (mBuildLayout) { - buildLayout(); - } - } - - /** - * Handle intent action to create new key - * - * @param intent - */ - private void handleActionCreateKey(Intent intent) { - Bundle extras = intent.getExtras(); - - mActionBar.setTitle(R.string.title_createKey); - - mCurrentPassPhrase = ""; - - if (extras != null) { - // if userId is given, prefill the fields - if (extras.containsKey(EXTRA_USER_IDS)) { - Log.d(Constants.TAG, "UserIds are given!"); - mUserIds.add(extras.getString(EXTRA_USER_IDS)); - } - - // if no passphrase is given - if (extras.containsKey(EXTRA_NO_PASSPHRASE)) { - boolean noPassphrase = extras.getBoolean(EXTRA_NO_PASSPHRASE); - if (noPassphrase) { - // check "no passphrase" checkbox and remove button - mNoPassphrase.setChecked(true); - mChangePassPhrase.setVisibility(View.GONE); - } - } - - // generate key - if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) { - boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS); - if (generateDefaultKeys) { - - // build layout in handler after generating keys not directly in onCreate - mBuildLayout = false; - - // Send all information needed to service generate keys in other thread - Intent serviceIntent = new Intent(this, ApgIntentService.class); - serviceIntent.putExtra(ApgIntentService.EXTRA_ACTION, - ApgIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(ApgIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, - mCurrentPassPhrase); - - serviceIntent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after generating is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_generating, ProgressDialog.STYLE_SPINNER) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get new key from data bundle returned from service - Bundle data = message.getData(); - PGPSecretKeyRing masterKeyRing = (PGPSecretKeyRing) PGPConversionHelper - .BytesToPGPKeyRing(data - .getByteArray(ApgIntentService.RESULT_NEW_KEY)); - PGPSecretKeyRing subKeyRing = (PGPSecretKeyRing) PGPConversionHelper - .BytesToPGPKeyRing(data - .getByteArray(ApgIntentService.RESULT_NEW_KEY2)); - - // add master key - @SuppressWarnings("unchecked") - Iterator masterIt = masterKeyRing.getSecretKeys(); - mKeys.add(masterIt.next()); - mKeysUsages.add(Id.choice.usage.sign_only); - - // add sub key - @SuppressWarnings("unchecked") - Iterator subIt = subKeyRing.getSecretKeys(); - subIt.next(); // masterkey - mKeys.add(subIt.next()); - mKeysUsages.add(Id.choice.usage.encrypt_only); - - buildLayout(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - serviceIntent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - saveHandler.showProgressDialog(this); - - // start service with intent - startService(serviceIntent); - } - } - } - } - - /** - * Handle intent action to edit existing key - * - * @param intent - */ - @SuppressWarnings("unchecked") - private void handleActionEditKey(Intent intent) { - Bundle extras = intent.getExtras(); - - mActionBar.setTitle(R.string.title_editKey); - - mCurrentPassPhrase = PGPMain.getEditPassPhrase(); - if (mCurrentPassPhrase == null) { - mCurrentPassPhrase = ""; - } - - if (mCurrentPassPhrase.equals("")) { - // check "no passphrase" checkbox and remove button - mNoPassphrase.setChecked(true); - mChangePassPhrase.setVisibility(View.GONE); - } - - if (extras != null) { - if (extras.containsKey(EXTRA_KEY_ID)) { - long keyId = extras.getLong(EXTRA_KEY_ID); - - if (keyId != 0) { - PGPSecretKey masterKey = null; - mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, keyId); - if (mKeyRing != null) { - masterKey = PGPHelper.getMasterKey(mKeyRing); - for (PGPSecretKey key : new IterableIterator( - mKeyRing.getSecretKeys())) { - mKeys.add(key); - mKeysUsages.add(-1); // get usage when view is created - } - } - if (masterKey != null) { - for (String userId : new IterableIterator(masterKey.getUserIDs())) { - Log.d(Constants.TAG, "Added userId " + userId); - mUserIds.add(userId); - } - } - } - } - } - } - - /** - * Shows the dialog to set a new passphrase - */ - private void showSetPassphraseDialog() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - - // set new returned passphrase! - mNewPassPhrase = data - .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); - - updatePassPhraseButtonText(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - // set title based on isPassphraseSet() - int title = -1; - if (isPassphraseSet()) { - title = R.string.title_changePassPhrase; - } else { - title = R.string.title_setPassPhrase; - } - - SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance( - messenger, title); - - setPassphraseDialog.show(getSupportFragmentManager(), "setPassphraseDialog"); - } - - /** - * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user - * id and key. - */ - private void buildLayout() { - // Build layout based on given userIds and keys - LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); - mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); - mUserIdsView.setType(Id.type.user_id); - mUserIdsView.setUserIds(mUserIds); - container.addView(mUserIdsView); - mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); - mKeysView.setType(Id.type.key); - mKeysView.setKeys(mKeys, mKeysUsages); - container.addView(mKeysView); - - updatePassPhraseButtonText(); - } - - private long getMasterKeyId() { - if (mKeysView.getEditors().getChildCount() == 0) { - return 0; - } - return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID(); - } - - public boolean isPassphraseSet() { - if (mNoPassphrase.isChecked()) { - return true; - } else if ((!mCurrentPassPhrase.equals("")) - || (mNewPassPhrase != null && !mNewPassPhrase.equals(""))) { - return true; - } else { - return false; - } - } - - private void saveClicked() { - try { - if (!isPassphraseSet()) { - throw new PGPMain.ApgGeneralException(this.getString(R.string.setAPassPhrase)); - } - - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_SAVE_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(ApgIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE, mCurrentPassPhrase); - data.putString(ApgIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase); - data.putStringArrayList(ApgIntentService.SAVE_KEYRING_USER_IDS, - getUserIds(mUserIdsView)); - ArrayList keys = getKeys(mKeysView); - data.putByteArray(ApgIntentService.SAVE_KEYRING_KEYS, - PGPConversionHelper.PGPSecretKeyArrayListToBytes(keys)); - data.putIntegerArrayList(ApgIntentService.SAVE_KEYRING_KEYS_USAGES, - getKeysUsages(mKeysView)); - data.putLong(ApgIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId()); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after saving is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, getMasterKeyId()); - ArrayList userIds = null; - try { - userIds = getUserIds(mUserIdsView); - } catch (ApgGeneralException e) { - Log.e(Constants.TAG, "exception while getting user ids", e); - } - data.putExtra(RESULT_EXTRA_USER_ID, userIds.get(0)); - setResult(RESULT_OK, data); - finish(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } catch (PGPMain.ApgGeneralException e) { - Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), - Toast.LENGTH_SHORT).show(); - } - } - - private void cancelClicked() { - setResult(RESULT_CANCELED); - finish(); - } - - /** - * Returns user ids from the SectionView - * - * @param userIdsView - * @return - */ - private ArrayList getUserIds(SectionView userIdsView) - throws PGPMain.ApgGeneralException { - ArrayList userIds = new ArrayList(); - - ViewGroup userIdEditors = userIdsView.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 PGPMain.ApgGeneralException( - this.getString(R.string.error_userIdNeedsAName)); - } catch (UserIdEditor.NoEmailException e) { - throw new PGPMain.ApgGeneralException( - this.getString(R.string.error_userIdNeedsAnEmailAddress)); - } catch (UserIdEditor.InvalidEmailException e) { - throw new PGPMain.ApgGeneralException(e.getMessage()); - } - - if (userId.equals("")) { - continue; - } - - if (editor.isMainUserId()) { - userIds.add(0, userId); - gotMainUserId = true; - } else { - userIds.add(userId); - } - } - - if (userIds.size() == 0) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsAUserId)); - } - - if (!gotMainUserId) { - throw new PGPMain.ApgGeneralException( - getString(R.string.error_mainUserIdMustNotBeEmpty)); - } - - return userIds; - } - - /** - * Returns keys from the SectionView - * - * @param keysView - * @return - */ - private ArrayList getKeys(SectionView keysView) - throws PGPMain.ApgGeneralException { - ArrayList keys = new ArrayList(); - - ViewGroup keyEditors = keysView.getEditors(); - - if (keyEditors.getChildCount() == 0) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsMasterKey)); - } - - for (int i = 0; i < keyEditors.getChildCount(); ++i) { - KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); - keys.add(editor.getValue()); - } - - return keys; - } - - /** - * Returns usage selections of keys from the SectionView - * - * @param keysView - * @return - */ - private ArrayList getKeysUsages(SectionView keysView) - throws PGPMain.ApgGeneralException { - ArrayList getKeysUsages = new ArrayList(); - - ViewGroup keyEditors = keysView.getEditors(); - - if (keyEditors.getChildCount() == 0) { - throw new PGPMain.ApgGeneralException(getString(R.string.error_keyNeedsMasterKey)); - } - - for (int i = 0; i < keyEditors.getChildCount(); ++i) { - KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); - getKeysUsages.add(editor.getUsage()); - } - - return getKeysUsages; - } - - private void updatePassPhraseButtonText() { - mChangePassPhrase.setText(isPassphraseSet() ? R.string.btn_changePassPhrase - : R.string.btn_setPassPhrase); - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java b/APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java deleted file mode 100644 index b0c6aa957..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/EncryptActivity.java +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.compatibility.ClipboardReflection; -import org.thialfihar.android.apg.helper.FileHelper; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; -import org.thialfihar.android.apg.util.Choice; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import org.thialfihar.android.apg.util.Log; -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.File; -import java.util.Vector; - -public class EncryptActivity extends SherlockFragmentActivity { - - /* Intents */ - // without permission - public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT"; - public static final String ACTION_ENCRYPT_FILE = Constants.INTENT_PREFIX + "ENCRYPT_FILE"; - - // with permission - public static final String ACTION_ENCRYPT_AND_RETURN = Constants.INTENT_PREFIX - + "ENCRYPT_AND_RETURN"; - public static final String ACTION_GENERATE_SIGNATURE_AND_RETURN = Constants.INTENT_PREFIX - + "GENERATE_SIGNATURE_AND_RETURN"; - public static final String ACTION_ENCRYPT_STREAM_AND_RETURN = Constants.INTENT_PREFIX - + "ENCRYPT_STREAM_AND_RETURN"; - - /* EXTRA keys for input */ - public static final String EXTRA_TEXT = "text"; - public static final String EXTRA_DATA = "data"; - public static final String EXTRA_ASCII_ARMOUR = "asciiArmour"; - public static final String EXTRA_SEND_TO = "sendTo"; - public static final String EXTRA_SUBJECT = "subject"; - public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId"; - public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds"; - - private String mSubject = null; - private String mSendTo = null; - - private long mEncryptionKeyIds[] = null; - - private boolean mEncryptImmediately = false; - private EditText mMessage = null; - private Button mSelectKeysButton = null; - - private boolean mEncryptEnabled = false; - private String mEncryptString = ""; - private boolean mEncryptToClipboardEnabled = false; - private String mEncryptToClipboardString = ""; - - 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 mAsciiArmorDemand = false; - private boolean mOverrideAsciiArmour = false; - private Uri mStreamAndReturnUri = null; - private byte[] mData = null; - - private boolean mGenerateSignature = false; - - private long mSecretKeyId = Id.key.none; - - private FileDialogFragment mFileDialog; - - /** - * ActionBar menu is created based on class variables to change it at runtime - * - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (mEncryptToClipboardEnabled) { - menu.add(1, Id.menu.option.encrypt_to_clipboard, 0, mEncryptToClipboardString) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - if (mEncryptEnabled) { - menu.add(1, Id.menu.option.encrypt, 1, mEncryptString).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.encrypt_to_clipboard: - encryptToClipboardClicked(); - - return true; - - case Id.menu.option.encrypt: - encryptClicked(); - - return true; - - default: - return super.onOptionsItemSelected(item); - - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check permissions for intent actions without user interaction - String[] restrictedActions = new String[] { ACTION_ENCRYPT_AND_RETURN, - ACTION_GENERATE_SIGNATURE_AND_RETURN, ACTION_ENCRYPT_STREAM_AND_RETURN }; - OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), - Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); - - setContentView(R.layout.encrypt); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - initView(); - - // Handle intent actions - handleActions(getIntent()); - - updateView(); - updateSource(); - updateMode(); - - if (mEncryptImmediately) { - 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); - } - - updateActionBarButtons(); - - if (mEncryptImmediately - && (mMessage.getText().length() > 0 || mData != null) - && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || mSecretKeyId != 0)) { - encryptClicked(); - } - } - - /** - * Handles all actions with this intent - * - * @param intent - */ - private void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - String type = intent.getType(); - Uri uri = intent.getData(); - - if (extras == null) { - extras = new Bundle(); - } - - /* - * Android's Action - */ - if (Intent.ACTION_SEND.equals(action) && type != null) { - // When sending to APG Encrypt via share menu - if ("text/plain".equals(type)) { - // Plain text - String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); - if (sharedText != null) { - // handle like normal text encryption, override action and extras to later - // execute ACTION_ENCRYPT in main actions - extras.putString(EXTRA_TEXT, sharedText); - extras.putBoolean(EXTRA_ASCII_ARMOUR, true); - action = ACTION_ENCRYPT; - } - } else { - // Files via content provider, override uri and action - uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); - action = ACTION_ENCRYPT_FILE; - } - } - - if (ACTION_ENCRYPT_AND_RETURN.equals(action) - || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { - mEncryptImmediately = true; - } - - if (ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { - mGenerateSignature = true; - mOverrideAsciiArmour = true; - mAsciiArmorDemand = false; - } - - if (extras.containsKey(EXTRA_ASCII_ARMOUR)) { - mAsciiArmorDemand = extras.getBoolean(EXTRA_ASCII_ARMOUR, true); - mOverrideAsciiArmour = true; - mAsciiArmour.setChecked(mAsciiArmorDemand); - } - - mData = extras.getByteArray(EXTRA_DATA); - String textData = null; - if (mData == null) { - textData = extras.getString(EXTRA_TEXT); - } - mSendTo = extras.getString(EXTRA_SEND_TO); - mSubject = extras.getString(EXTRA_SUBJECT); - long signatureKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); - long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); - - // preselect keys given by intent - preselectKeys(signatureKeyId, encryptionKeyIds); - - /** - * Main Actions - */ - if (ACTION_ENCRYPT.equals(action) || ACTION_ENCRYPT_AND_RETURN.equals(action) - || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { - if (textData != null) { - mMessage.setText(textData); - } - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceMessage) { - mSource.showNext(); - } - } else if (ACTION_ENCRYPT_FILE.equals(action)) { - // get file path from uri - String path = FileHelper.getPath(this, uri); - - if (path != null) { - mInputFilename = path; - mFilename.setText(mInputFilename); - - mSource.setInAnimation(null); - mSource.setOutAnimation(null); - while (mSource.getCurrentView().getId() != R.id.sourceFile) { - mSource.showNext(); - } - } else { - Log.e(Constants.TAG, - "Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN."); - Toast.makeText(this, R.string.error_onlyFilesAreSupported, Toast.LENGTH_LONG) - .show(); - // end activity - finish(); - } - } else if (ACTION_ENCRYPT_STREAM_AND_RETURN.equals(action)) { - // TODO: Set mStreamAndReturnUri that is used later to encrypt a stream! - - mStreamAndReturnUri = uri; - } - } - - /** - * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those! - * - * @param preselectedSignatureKeyId - * @param preselectedEncryptionKeyIds - */ - private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) { - if (preselectedSignatureKeyId != 0) { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, - preselectedSignatureKeyId); - PGPSecretKey masterKey = null; - if (keyRing != null) { - masterKey = PGPHelper.getMasterKey(keyRing); - if (masterKey != null) { - Vector signKeys = PGPHelper.getUsableSigningKeys(keyRing); - if (signKeys.size() > 0) { - mSecretKeyId = masterKey.getKeyID(); - } - } - } - } - - if (preselectedEncryptionKeyIds != null) { - Vector goodIds = new Vector(); - for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, - preselectedEncryptionKeyIds[i]); - PGPPublicKey masterKey = null; - if (keyRing == null) { - continue; - } - masterKey = PGPHelper.getMasterKey(keyRing); - if (masterKey == null) { - continue; - } - Vector encryptKeys = PGPHelper.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); - } - } - } - } - - /** - * Guess output filename based on input path - * - * @param path - * @return Suggestion for output filename - */ - private String guessOutputFilename(String path) { - // output in the same directory but with additional ending - File file = new File(path); - String ending = (mAsciiArmour.isChecked() ? ".asc" : ".gpg"); - String outputFilename = file.getParent() + File.separator + file.getName() + ending; - - return outputFilename; - } - - 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; - } - } - updateActionBarButtons(); - } - - /** - * Set ActionBar buttons based on parameters - * - * @param encryptEnabled - * @param encryptStringRes - * @param encryptToClipboardEnabled - * @param encryptToClipboardStringRes - */ - private void setActionbarButtons(boolean encryptEnabled, int encryptStringRes, - boolean encryptToClipboardEnabled, int encryptToClipboardStringRes) { - mEncryptEnabled = encryptEnabled; - if (encryptEnabled) { - mEncryptString = getString(encryptStringRes); - } - mEncryptToClipboardEnabled = encryptToClipboardEnabled; - if (encryptToClipboardEnabled) { - mEncryptToClipboardString = getString(encryptToClipboardStringRes); - } - - // build new action bar based on these class variables - invalidateOptionsMenu(); - } - - /** - * Update ActionBar buttons based on current selection in view - */ - private void updateActionBarButtons() { - switch (mSource.getCurrentView().getId()) { - case R.id.sourceFile: { - setActionbarButtons(true, R.string.btn_encryptFile, false, 0); - - break; - } - - case R.id.sourceMessage: { - mSourceLabel.setText(R.string.label_message); - - if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { - if (mEncryptImmediately) { - setActionbarButtons(true, R.string.btn_encrypt, false, 0); - } else { - setActionbarButtons(true, R.string.btn_encryptAndEmail, true, - R.string.btn_encryptToClipboard); - } - } else { - if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) { - if (mSecretKeyId == 0) { - setActionbarButtons(false, 0, false, 0); - } else { - if (mEncryptImmediately) { - setActionbarButtons(true, R.string.btn_sign, false, 0); - } else { - setActionbarButtons(true, R.string.btn_signAndEmail, true, - R.string.btn_signToClipboard); - } - } - } else { - if (mEncryptImmediately) { - setActionbarButtons(true, R.string.btn_encrypt, false, 0); - } else { - setActionbarButtons(true, R.string.btn_encryptAndEmail, true, - R.string.btn_encryptToClipboard); - } - } - } - 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; - } - } - updateActionBarButtons(); - } - - private void encryptToClipboardClicked() { - mEncryptTarget = Id.target.clipboard; - initiateEncryption(); - } - - private void encryptClicked() { - Log.d(Constants.TAG, "encryptClicked invoked!"); - - 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)) { - mInputFilename = mFilename.getText().toString(); - } - - mOutputFilename = guessOutputFilename(mInputFilename); - - 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 && mSecretKeyId == 0) { - Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.LENGTH_SHORT) - .show(); - return; - } - - if (mSecretKeyId != 0 - && PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { - showPassphraseDialog(); - - return; - } - } - - if (mEncryptTarget == Id.target.file) { - showOutputFileDialog(); - } else { - encryptStart(); - } - } - - /** - * Shows passphrase dialog to cache a new passphrase the user enters for using it later for - * encryption - */ - private void showPassphraseDialog() { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - if (mEncryptTarget == Id.target.file) { - showOutputFileDialog(); - } else { - encryptStart(); - } - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( - EncryptActivity.this, messenger, mSecretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - private void showOutputFileDialog() { - // Message is received after file is selected - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - encryptStart(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - mFileDialog = FileDialogFragment.newInstance(messenger, - getString(R.string.title_encryptToFile), - getString(R.string.specifyFileToEncryptTo), mOutputFilename, null, - Id.request.output_filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - - private void encryptStart() { - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - boolean useAsciiArmor = true; - long encryptionKeyIds[] = null; - int compressionId = 0; - boolean signOnly = false; - - if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { - Log.d(Constants.TAG, "Symmetric encryption enabled!"); - String passPhrase = mPassPhrase.getText().toString(); - if (passPhrase.length() == 0) { - passPhrase = null; - } - - data.putString(ApgIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase); - } else { - encryptionKeyIds = mEncryptionKeyIds; - signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); - } - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_ENCRYPT_SIGN); - - // choose default settings, target and data bundle by target - if (mStreamAndReturnUri != null) { - // mIntentDataUri is only defined when ACTION_ENCRYPT_STREAM_AND_RETURN is used - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_STREAM); - data.putParcelable(ApgIntentService.ENCRYPT_PROVIDER_URI, mStreamAndReturnUri); - - } else if (mEncryptTarget == Id.target.file) { - useAsciiArmor = mAsciiArmour.isChecked(); - compressionId = ((Choice) mFileCompression.getSelectedItem()).getId(); - - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); - - Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" - + mOutputFilename); - - data.putString(ApgIntentService.ENCRYPT_INPUT_FILE, mInputFilename); - data.putString(ApgIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename); - - } else { - useAsciiArmor = true; - compressionId = Preferences.getPreferences(this).getDefaultMessageCompression(); - - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); - - if (mData != null) { - data.putByteArray(ApgIntentService.ENCRYPT_MESSAGE_BYTES, mData); - } else { - String message = mMessage.getText().toString(); - if (signOnly && !mEncryptImmediately) { - fixBadCharactersForGmail(message); - } - data.putByteArray(ApgIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes()); - } - } - - if (mOverrideAsciiArmour) { - useAsciiArmor = mAsciiArmorDemand; - } - - data.putLong(ApgIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId); - data.putBoolean(ApgIntentService.ENCRYPT_USE_ASCII_AMOR, useAsciiArmor); - data.putLongArray(ApgIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, encryptionKeyIds); - data.putInt(ApgIntentService.ENCRYPT_COMPRESSION_ID, compressionId); - data.putBoolean(ApgIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature); - data.putBoolean(ApgIntentService.ENCRYPT_SIGN_ONLY, signOnly); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after encrypting is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle data = message.getData(); - - String output; - switch (mEncryptTarget) { - case Id.target.clipboard: - output = data.getString(ApgIntentService.RESULT_ENCRYPTED_STRING); - Log.d(Constants.TAG, "output: " + output); - ClipboardReflection.copyToClipboard(EncryptActivity.this, output); - Toast.makeText(EncryptActivity.this, - R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT) - .show(); - break; - - case Id.target.email: - if (mEncryptImmediately) { - Intent intent = new Intent(); - intent.putExtras(data); - setResult(RESULT_OK, intent); - finish(); - return; - } - - output = data.getString(ApgIntentService.RESULT_ENCRYPTED_STRING); - Log.d(Constants.TAG, "output: " + output); - - Intent emailIntent = new Intent(Intent.ACTION_SEND); - emailIntent.setType("text/plain; charset=utf-8"); - emailIntent.putExtra(Intent.EXTRA_TEXT, output); - if (mSubject != null) { - emailIntent.putExtra(Intent.EXTRA_SUBJECT, mSubject); - } - if (mSendTo != null) { - emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { mSendTo }); - } - startActivity(Intent.createChooser(emailIntent, - getString(R.string.title_sendEmail))); - break; - - case Id.target.file: - Toast.makeText(EncryptActivity.this, R.string.encryptionSuccessful, - Toast.LENGTH_SHORT).show(); - - if (mDeleteAfter.isChecked()) { - // Create and show dialog to delete original file - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment - .newInstance(mInputFilename); - deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); - } - break; - - default: - // shouldn't happen - break; - - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - /** - * Fixes bad message characters for gmail - * - * @param message - * @return - */ - private String fixBadCharactersForGmail(String message) { - // 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"); - - return message; - } - - private void initView() { - 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); - 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) { - FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*", - Id.request.filename); - } - }); - - 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 = Preferences.getPreferences(this).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(Preferences.getPreferences(this).getDefaultAsciiArmour()); - - 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 { - mSecretKeyId = Id.key.none; - updateView(); - } - } - }); - } - - 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 (mSecretKeyId == Id.key.none) { - mSign.setChecked(false); - mMainUserId.setText(""); - mMainUserIdRest.setText(""); - } else { - String uid = getResources().getString(R.string.unknownUserId); - String uidExtra = ""; - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, - mSecretKeyId); - if (keyRing != null) { - PGPSecretKey key = PGPHelper.getMasterKey(keyRing); - if (key != null) { - String userId = PGPHelper.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); - } - - updateActionBarButtons(); - } - - private void selectPublicKeys() { - Intent intent = new Intent(this, SelectPublicKeyActivity.class); - Vector keyIds = new Vector(); - if (mSecretKeyId != 0) { - keyIds.add(mSecretKeyId); - } - 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(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds); - startActivityForResult(intent, Id.request.public_keys); - } - - private void selectSecretKey() { - Intent intent = new Intent(this, SelectSecretKeyActivity.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) { - try { - String path = FileHelper.getPath(this, data.getData()); - Log.d(Constants.TAG, "path=" + path); - - mFilename.setText(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - case Id.request.output_filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - mFileDialog.setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!"); - } - } - return; - } - - case Id.request.public_keys: { - if (resultCode == RESULT_OK) { - Bundle bundle = data.getExtras(); - mEncryptionKeyIds = bundle - .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS); - } - updateView(); - break; - } - - case Id.request.secret_keys: { - if (resultCode == RESULT_OK) { - Bundle bundle = data.getExtras(); - mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); - } else { - mSecretKeyId = Id.key.none; - } - updateView(); - break; - } - - default: { - break; - } - } - - super.onActivityResult(requestCode, resultCode, data); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/HelpActivity.java b/APG/src/org/thialfihar/android/apg/ui/HelpActivity.java deleted file mode 100644 index f426b42bd..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/HelpActivity.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.R; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.FragmentTransaction; -import android.widget.TextView; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.ActionBar.Tab; -import com.actionbarsherlock.view.MenuItem; - -import java.util.ArrayList; - -import android.content.Context; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; - -import com.actionbarsherlock.app.SherlockFragmentActivity; - -public class HelpActivity extends SherlockFragmentActivity { - public static final String EXTRA_SELECTED_TAB = "selectedTab"; - - ViewPager mViewPager; - TabsAdapter mTabsAdapter; - TextView tabCenter; - TextView tabText; - - /** - * Menu Items - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mViewPager = new ViewPager(this); - mViewPager.setId(R.id.pager); - - setContentView(mViewPager); - ActionBar bar = getSupportActionBar(); - bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - bar.setDisplayShowTitleEnabled(true); - bar.setDisplayHomeAsUpEnabled(true); - - mTabsAdapter = new TabsAdapter(this, mViewPager); - - int selectedTab = 0; - Intent intent = getIntent(); - if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) { - selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); - } - - Bundle startBundle = new Bundle(); - startBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_start); - mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_start)), - HelpFragmentHtml.class, startBundle, (selectedTab == 0 ? true : false)); - - Bundle nfcBundle = new Bundle(); - nfcBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_nfc_beam); - mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_nfc_beam)), - HelpFragmentHtml.class, nfcBundle, (selectedTab == 1 ? true : false)); - - Bundle changelogBundle = new Bundle(); - changelogBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_changelog); - mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_changelog)), - HelpFragmentHtml.class, changelogBundle, (selectedTab == 2 ? true : false)); - - mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_about)), - HelpFragmentAbout.class, null, (selectedTab == 3 ? true : false)); - } - - public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, - ViewPager.OnPageChangeListener { - private final Context mContext; - private final ActionBar mActionBar; - private final ViewPager mViewPager; - private final ArrayList mTabs = new ArrayList(); - - static final class TabInfo { - private final Class clss; - private final Bundle args; - - TabInfo(Class _class, Bundle _args) { - clss = _class; - args = _args; - } - } - - public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) { - super(activity.getSupportFragmentManager()); - mContext = activity; - mActionBar = activity.getSupportActionBar(); - mViewPager = pager; - mViewPager.setAdapter(this); - mViewPager.setOnPageChangeListener(this); - } - - public void addTab(ActionBar.Tab tab, Class clss, Bundle args, boolean selected) { - TabInfo info = new TabInfo(clss, args); - tab.setTag(info); - tab.setTabListener(this); - mTabs.add(info); - mActionBar.addTab(tab, selected); - notifyDataSetChanged(); - } - - @Override - public int getCount() { - return mTabs.size(); - } - - @Override - public Fragment getItem(int position) { - TabInfo info = mTabs.get(position); - return Fragment.instantiate(mContext, info.clss.getName(), info.args); - } - - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - } - - public void onPageSelected(int position) { - mActionBar.setSelectedNavigationItem(position); - } - - public void onPageScrollStateChanged(int state) { - } - - public void onTabSelected(Tab tab, FragmentTransaction ft) { - Object tag = tab.getTag(); - for (int i = 0; i < mTabs.size(); i++) { - if (mTabs.get(i) == tag) { - mViewPager.setCurrentItem(i); - } - } - } - - public void onTabUnselected(Tab tab, FragmentTransaction ft) { - } - - public void onTabReselected(Tab tab, FragmentTransaction ft) { - } - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java b/APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java deleted file mode 100644 index f1cc8407c..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/HelpFragmentAbout.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.io.IOException; -import java.io.InputStream; - -import net.nightwhistler.htmlspanner.HtmlSpanner; -import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; - -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Bundle; -import android.text.method.LinkMovementMethod; -import org.thialfihar.android.apg.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.actionbarsherlock.app.SherlockFragment; - -public class HelpFragmentAbout extends SherlockFragment { - - /** - * Workaround for Android Bug. See - * http://stackoverflow.com/questions/8748064/starting-activity-from - * -fragment-causes-nullpointerexception - */ - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - setUserVisibleHint(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.help_fragment_about, container, false); - - // load html from html file from /res/raw - InputStream inputStreamText = getResources().openRawResource(R.raw.help_about); - - TextView versionText = (TextView) view.findViewById(R.id.help_about_version); - versionText.setText(getString(R.string.help_about_version) + " " + getVersion()); - - JellyBeanSpanFixTextView aboutTextView = (JellyBeanSpanFixTextView) view - .findViewById(R.id.help_about_text); - - // load html into textview - HtmlSpanner htmlSpanner = new HtmlSpanner(); - htmlSpanner.setStripExtraWhiteSpace(true); - try { - aboutTextView.setText(htmlSpanner.fromHtml(inputStreamText)); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while reading raw resources as stream", e); - } - - // make links work - aboutTextView.setMovementMethod(LinkMovementMethod.getInstance()); - - // no flickering when clicking textview for Android < 4 - aboutTextView.setTextColor(getResources().getColor(android.R.color.black)); - - return view; - } - - /** - * Get the current package version. - * - * @return The current version. - */ - private String getVersion() { - String result = ""; - try { - PackageManager manager = getActivity().getPackageManager(); - PackageInfo info = manager.getPackageInfo(getActivity().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; - } - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java b/APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java deleted file mode 100644 index eeea56a00..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/HelpFragmentHtml.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.io.IOException; -import java.io.InputStream; - -import net.nightwhistler.htmlspanner.HtmlSpanner; -import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.util.Log; - -import android.app.Activity; -import android.os.Bundle; -import android.text.method.LinkMovementMethod; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ScrollView; - -import com.actionbarsherlock.app.SherlockFragment; - -public class HelpFragmentHtml extends SherlockFragment { - private Activity mActivity; - - private int htmlFile; - - public static final String ARG_HTML_FILE = "htmlFile"; - - /** - * Create a new instance of HelpFragmentHtml, providing "htmlFile" as an argument. - */ - static HelpFragmentHtml newInstance(int htmlFile) { - HelpFragmentHtml f = new HelpFragmentHtml(); - - // Supply html raw file input as an argument. - Bundle args = new Bundle(); - args.putInt(ARG_HTML_FILE, htmlFile); - f.setArguments(args); - - return f; - } - - /** - * Workaround for Android Bug. See - * http://stackoverflow.com/questions/8748064/starting-activity-from - * -fragment-causes-nullpointerexception - */ - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - setUserVisibleHint(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - htmlFile = getArguments().getInt(ARG_HTML_FILE); - - // load html from html file from /res/raw - InputStream inputStreamText = getResources().openRawResource(htmlFile); - - mActivity = getActivity(); - - ScrollView scroller = new ScrollView(mActivity); - JellyBeanSpanFixTextView text = new JellyBeanSpanFixTextView(mActivity); - - // padding - int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, mActivity - .getResources().getDisplayMetrics()); - text.setPadding(padding, padding, padding, 0); - - scroller.addView(text); - - // load html into textview - HtmlSpanner htmlSpanner = new HtmlSpanner(); - htmlSpanner.setStripExtraWhiteSpace(true); - try { - text.setText(htmlSpanner.fromHtml(inputStreamText)); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while reading raw resources as stream", e); - } - - // make links work - text.setMovementMethod(LinkMovementMethod.getInstance()); - - // no flickering when clicking textview for Android < 4 - text.setTextColor(getResources().getColor(android.R.color.black)); - - return scroller; - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/ImportKeysActivity.java b/APG/src/org/thialfihar/android/apg/ui/ImportKeysActivity.java deleted file mode 100644 index 990f1d161..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/ImportKeysActivity.java +++ /dev/null @@ -1,473 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.compatibility.DialogFragmentWorkaround; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.R; - -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; - -import org.thialfihar.android.apg.ui.dialog.DeleteFileDialogFragment; -import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; -import org.thialfihar.android.apg.util.Log; - -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.view.View; -import android.widget.Toast; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; -import com.google.zxing.integration.android.IntentIntegrator; -import com.google.zxing.integration.android.IntentResult; - -public class ImportKeysActivity extends SherlockFragmentActivity { - public static final String ACTION_IMPORT = Constants.INTENT_PREFIX + "IMPORT"; - public static final String ACTION_IMPORT_FROM_FILE = Constants.INTENT_PREFIX - + "IMPORT_FROM_FILE"; - public static final String ACTION_IMPORT_FROM_QR_CODE = Constants.INTENT_PREFIX - + "IMPORT_FROM_QR_CODE"; - public static final String ACTION_IMPORT_FROM_NFC = Constants.INTENT_PREFIX + "IMPORT_FROM_NFC"; - - // only used by IMPORT - public static final String EXTRA_TEXT = "text"; - public static final String EXTRA_KEYRING_BYTES = "keyringBytes"; - - // public static final String EXTRA_KEY_ID = "keyId"; - - protected String mImportFilename; - protected byte[] mImportData; - - protected boolean mDeleteAfterImport = false; - - FileDialogFragment mFileDialog; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.import_keys); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - - handleActions(getIntent()); - } - - /** - * ActionBar menu is created based on class variables to change it at runtime - * - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(1, Id.menu.option.import_from_file, 0, R.string.menu_importFromFile) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.import_from_qr_code, 1, R.string.menu_importFromQrCode) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.import_from_nfc, 2, R.string.menu_importFromNfc) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.import_from_file: - showImportFromFileDialog(); - return true; - - case Id.menu.option.import_from_qr_code: - importFromQrCode(); - return true; - - case Id.menu.option.import_from_nfc: - importFromNfc(); - return true; - - default: - return super.onOptionsItemSelected(item); - - } - } - - protected void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - - if (extras == null) { - extras = new Bundle(); - } - - /** - * Android Standard Actions - */ - if (Intent.ACTION_VIEW.equals(action)) { - // Android's Action when opening file associated to APG (see AndroidManifest.xml) - // override action to delegate it to APGs ACTION_IMPORT - action = ACTION_IMPORT; - } - - /** - * APG's own Actions - */ - if (ACTION_IMPORT.equals(action)) { - if ("file".equals(intent.getScheme()) && intent.getDataString() != null) { - mImportFilename = intent.getData().getPath(); - mImportData = null; - } else if (extras.containsKey(EXTRA_TEXT)) { - mImportData = intent.getStringExtra(EXTRA_TEXT).getBytes(); - mImportFilename = null; - } else if (extras.containsKey(EXTRA_KEYRING_BYTES)) { - mImportData = intent.getByteArrayExtra(EXTRA_KEYRING_BYTES); - mImportFilename = null; - } - loadKeyListFragment(); - } else if (ACTION_IMPORT_FROM_FILE.equals(action)) { - if ("file".equals(intent.getScheme()) && intent.getDataString() != null) { - mImportFilename = intent.getData().getPath(); - mImportData = null; - } - showImportFromFileDialog(); - } else if (ACTION_IMPORT_FROM_QR_CODE.equals(action)) { - importFromQrCode(); - } else if (ACTION_IMPORT_FROM_NFC.equals(action)) { - importFromNfc(); - } - } - - public void loadKeyListFragment() { - if (mImportData != null || mImportFilename != null) { - // generate list of keyrings - FragmentManager fragmentManager = getSupportFragmentManager(); - FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - - ImportKeysListFragment listFragment = new ImportKeysListFragment(); - Bundle args = new Bundle(); - args.putByteArray(ImportKeysListFragment.ARG_KEYRING_BYTES, mImportData); - args.putString(ImportKeysListFragment.ARG_IMPORT_FILENAME, mImportFilename); - listFragment.setArguments(args); - // replace container in view with fragment - fragmentTransaction.replace(R.id.import_keys_list_container, listFragment); - fragmentTransaction.commit(); - } - } - - private void importFromQrCode() { - new IntentIntegrator(this).initiateScan(); - } - - private void importFromNfc() { - // show nfc help - Intent intent = new Intent(this, HelpActivity.class); - intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 1); - startActivityForResult(intent, 0); - } - - /** - * Show to dialog from where to import keys - */ - public void showImportFromFileDialog() { - // Message is received after file is selected - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mImportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - mDeleteAfterImport = data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED); - - Log.d(Constants.TAG, "mImportFilename: " + mImportFilename); - Log.d(Constants.TAG, "mDeleteAfterImport: " + mDeleteAfterImport); - - loadKeyListFragment(); - } - } - }; - - // Create a new Messenger for the communication back - final Messenger messenger = new Messenger(returnHandler); - - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - mFileDialog = FileDialogFragment.newInstance(messenger, - getString(R.string.title_importKeys), - getString(R.string.specifyFileToImportFrom), Constants.path.APP_DIR + "/", - null, Id.request.filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - }); - } - - // private void importAndSignOld(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 - // - // // TODO: there should be only 1 - // HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]); - // String encodedKey = server.get(keyId); - // - // PGPKeyRing keyring = PGPHelper.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 = PGPHelper.convertFingerprintToHex(publicKeyRing - // .getPublicKey().getFingerprint()); - // if (expectedFingerprint.equals(actualFingerprint)) { - // // store the signed key in our local cache - // int retval = PGPMain.storeKeyRingInCache(publicKeyRing); - // if (retval != Id.return_value.ok - // && retval != Id.return_value.updated) { - // status.putString(EXTRA_ERROR, - // "Failed to store signed key in local cache"); - // } else { - // Intent intent = new Intent(ImportFromQRCodeActivity.this, - // SignKeyActivity.class); - // intent.putExtra(EXTRA_KEY_ID, keyId); - // startActivityForResult(intent, Id.request.sign_key); - // } - // } else { - // status.putString( - // 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(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(EXTRA_ERROR, "Failed to query KeyServer"); - // status.putInt(Constants.extras.STATUS, Id.message.done); - // } - // } - // }; - // - // t.setName("KeyExchange Download Thread"); - // t.setDaemon(true); - // t.start(); - // } - // } - - public void scanAgainOnClick(View view) { - new IntentIntegrator(this).initiateScan(); - } - - public void finishOnClick(View view) { - finish(); - } - - public void importOnClick(View view) { - Log.d(Constants.TAG, "Import key button clicked!"); - - importKeys(); - } - - /** - * Import keys with mImportData - */ - public void importKeys() { - if (mImportData != null || mImportFilename != null) { - Log.d(Constants.TAG, "importKeys started"); - - // Send all information needed to service to import key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_IMPORT_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - - // TODO: check for key type? - // data.putInt(ApgIntentService.IMPORT_KEY_TYPE, Id.type.secret_key); - - if (mImportData != null) { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_BYTES); - data.putByteArray(ApgIntentService.IMPORT_BYTES, mImportData); - } else { - data.putInt(ApgIntentService.TARGET, ApgIntentService.TARGET_FILE); - data.putString(ApgIntentService.IMPORT_FILENAME, mImportFilename); - } - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after importing is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - int added = returnData.getInt(ApgIntentService.RESULT_IMPORT_ADDED); - int updated = returnData.getInt(ApgIntentService.RESULT_IMPORT_UPDATED); - int bad = returnData.getInt(ApgIntentService.RESULT_IMPORT_BAD); - String toastMessage; - if (added > 0 && updated > 0) { - toastMessage = getString(R.string.keysAddedAndUpdated, added, updated); - } else if (added > 0) { - toastMessage = getString(R.string.keysAdded, added); - } else if (updated > 0) { - toastMessage = getString(R.string.keysUpdated, updated); - } else { - toastMessage = getString(R.string.noKeysAddedOrUpdated); - } - Toast.makeText(ImportKeysActivity.this, toastMessage, Toast.LENGTH_SHORT) - .show(); - if (bad > 0) { - AlertDialog.Builder alert = new AlertDialog.Builder( - ImportKeysActivity.this); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.warning); - alert.setMessage(ImportKeysActivity.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 - DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment - .newInstance(mImportFilename); - deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } else { - Toast.makeText(this, R.string.error_nothingImport, Toast.LENGTH_LONG).show(); - } - } - - public void signAndUploadOnClick(View view) { - // first, import! - importOnClick(view); - - // TODO: implement sign and upload! - Toast.makeText(ImportKeysActivity.this, "Not implemented right now!", Toast.LENGTH_SHORT) - .show(); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - // set filename used in export/import dialogs - mFileDialog.setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); - } - } - return; - } - case IntentIntegrator.REQUEST_CODE: { - IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, - data); - if (scanResult != null && scanResult.getFormatName() != null) { - - // mScannedContent = scanResult.getContents(); - - mImportData = scanResult.getContents().getBytes(); - mImportFilename = null; - - // mContentView.setText(mScannedContent); - // String[] bits = 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; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - } - } - } - - @Override - protected void onResume() { - super.onResume(); - - loadKeyListFragment(); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/ImportKeysListFragment.java b/APG/src/org/thialfihar/android/apg/ui/ImportKeysListFragment.java deleted file mode 100644 index 157f34c28..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/ImportKeysListFragment.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.ui.widget.ImportKeysListLoader; -import org.thialfihar.android.apg.util.Log; - -import com.actionbarsherlock.app.SherlockListFragment; - -import android.app.Activity; -import android.os.Bundle; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.View; -import android.widget.ListView; -import android.widget.SimpleAdapter; - -public class ImportKeysListFragment extends SherlockListFragment implements - LoaderManager.LoaderCallbacks>> { - public static String ARG_KEYRING_BYTES = "bytes"; - public static String ARG_IMPORT_FILENAME = "filename"; - - byte[] mKeyringBytes; - String mImportFilename; - - private Activity mActivity; - private SimpleAdapter mAdapter; - - @Override - public void onListItemClick(ListView listView, View view, int position, long id) { - // Map item = (Map) listView.getItemAtPosition(position); - // String userId = item.get(ImportKeysListLoader.MAP_ATTR_USER_ID); - } - - /** - * Resume is called after rotating - */ - @Override - public void onResume() { - super.onResume(); - - // Start out with a progress indicator. - setListShown(false); - - // reload list - getLoaderManager().restartLoader(0, null, this); - } - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mActivity = this.getActivity(); - - mKeyringBytes = getArguments().getByteArray(ARG_KEYRING_BYTES); - mImportFilename = getArguments().getString(ARG_IMPORT_FILENAME); - - // register long press context menu - registerForContextMenu(getListView()); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(mActivity.getString(R.string.error_nothingImport)); - - // Create an empty adapter we will use to display the loaded data. - String[] from = new String[] {}; - int[] to = new int[] {}; - List> data = new ArrayList>(); - mAdapter = new SimpleAdapter(getActivity(), data, android.R.layout.two_line_list_item, - from, to); - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - @Override - public Loader>> onCreateLoader(int id, Bundle args) { - return new ImportKeysListLoader(mActivity, mKeyringBytes, mImportFilename); - } - - @Override - public void onLoadFinished(Loader>> loader, - List> data) { - // Set the new data in the adapter. - // for (String item : data) { - // mAdapter.add(item); - // } - Log.d(Constants.TAG, "data: " + data); - // TODO: real swapping the data to the already defined adapter doesn't work - // Workaround: recreate adapter! - // http://stackoverflow.com/questions/2356091/android-add-function-of-arrayadapter-not-working - // mAdapter = new ArrayAdapter(mActivity, android.R.layout.two_line_list_item, - // data); - - String[] from = new String[] { ImportKeysListLoader.MAP_ATTR_USER_ID, - ImportKeysListLoader.MAP_ATTR_FINGERPINT }; - int[] to = new int[] { android.R.id.text1, android.R.id.text2 }; - mAdapter = new SimpleAdapter(getActivity(), data, android.R.layout.two_line_list_item, - from, to); - - mAdapter.notifyDataSetChanged(); - setListAdapter(mAdapter); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - @Override - public void onLoaderReset(Loader>> loader) { - // Clear the data in the adapter. - // Not available in SimpleAdapter! - // mAdapter.clear(); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java deleted file mode 100644 index 4b4a5daac..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyListActivity.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.compatibility.DialogFragmentWorkaround; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.ui.dialog.DeleteKeyDialogFragment; -import org.thialfihar.android.apg.ui.dialog.FileDialogFragment; -import org.thialfihar.android.apg.util.Log; - -import android.app.ProgressDialog; -import android.app.SearchManager; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; -import android.widget.Toast; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -public class KeyListActivity extends SherlockFragmentActivity { - - protected String mExportFilename = Constants.path.APP_DIR + "/"; - - protected String mImportData; - protected boolean mDeleteAfterImport = false; - - protected int mKeyType; - - FileDialogFragment mFileDialog; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - - handleActions(getIntent()); - } - - // TODO: needed? - // @Override - // protected void onNewIntent(Intent intent) { - // super.onNewIntent(intent); - // handleActions(intent); - // } - - protected void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - - if (extras == null) { - extras = new Bundle(); - } - - /** - * Android Standard Actions - */ - String searchString = null; - if (Intent.ACTION_SEARCH.equals(action)) { - searchString = extras.getString(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); - - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.filename: { - if (resultCode == RESULT_OK && data != null) { - try { - String path = data.getData().getPath(); - Log.d(Constants.TAG, "path=" + path); - - // set filename used in export/import dialogs - mFileDialog.setFilename(path); - } catch (NullPointerException e) { - Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); - } - } - return; - } - - default: { - break; - } - } - super.onActivityResult(requestCode, resultCode, data); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - // TODO: reimplement! - // menu.add(3, Id.menu.option.search, 0, R.string.menu_search) - // .setIcon(R.drawable.ic_menu_search).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - menu.add(0, Id.menu.option.import_from_file, 5, R.string.menu_importFromFile) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(0, Id.menu.option.export_keys, 6, R.string.menu_exportKeys).setShowAsAction( - MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.import_from_file: { - Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); - intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_FILE); - startActivityForResult(intentImportFromFile, 0); - return true; - } - - case Id.menu.option.export_keys: { - showExportKeysDialog(-1); - return true; - } - - // case Id.menu.option.search: - // startSearch("", false, null, false); - // return true; - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - /** - * Show dialog where to export keys - * - * @param keyRingMasterKeyId - * if -1 export all keys - */ - public void showExportKeysDialog(final long keyRingMasterKeyId) { - // Message is received after file is selected - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == FileDialogFragment.MESSAGE_OKAY) { - Bundle data = message.getData(); - mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - - exportKeys(keyRingMasterKeyId); - } - } - }; - - // Create a new Messenger for the communication back - final Messenger messenger = new Messenger(returnHandler); - - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - String title = null; - if (keyRingMasterKeyId != -1) { - // single key export - title = getString(R.string.title_exportKey); - } else { - title = getString(R.string.title_exportKeys); - } - - String message = null; - if (mKeyType == Id.type.public_key) { - message = getString(R.string.specifyFileToExportTo); - } else { - message = getString(R.string.specifyFileToExportSecretKeysTo); - } - - mFileDialog = FileDialogFragment.newInstance(messenger, title, message, - mExportFilename, null, Id.request.filename); - - mFileDialog.show(getSupportFragmentManager(), "fileDialog"); - } - }); - } - - /** - * Show dialog to delete key - * - * @param keyRingId - */ - public void showDeleteKeyDialog(long keyRingId) { - // Message is received after key is deleted - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { - // no further actions needed - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - keyRingId, mKeyType); - - deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); - } - - /** - * Export keys - * - * @param keyRingMasterKeyId - * if -1 export all keys - */ - public void exportKeys(long keyRingMasterKeyId) { - Log.d(Constants.TAG, "exportKeys started"); - - // Send all information needed to service to export key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_EXPORT_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - - data.putString(ApgIntentService.EXPORT_FILENAME, mExportFilename); - data.putInt(ApgIntentService.EXPORT_KEY_TYPE, mKeyType); - - if (keyRingMasterKeyId == -1) { - data.putBoolean(ApgIntentService.EXPORT_ALL, true); - } else { - data.putLong(ApgIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId); - } - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after exporting is done in ApgService - ApgIntentServiceHandler exportHandler = new ApgIntentServiceHandler(this, - R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - int exported = returnData.getInt(ApgIntentService.RESULT_EXPORT); - String toastMessage; - if (exported == 1) { - toastMessage = getString(R.string.keyExported); - } else if (exported > 0) { - toastMessage = getString(R.string.keysExported, exported); - } else { - toastMessage = getString(R.string.noKeysExported); - } - Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show(); - - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(exportHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - exportHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java b/APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java deleted file mode 100644 index dc50c2dd6..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyListFragment.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.ui.widget.ExpandableListFragment; - -import android.os.Bundle; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.View; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; - -public class KeyListFragment extends ExpandableListFragment { - protected KeyListActivity mKeyListActivity; - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListActivity = (KeyListActivity) getActivity(); - - // register long press context menu - registerForContextMenu(getListView()); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.listEmpty)); - } - - /** - * Context Menu on Long Click - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - menu.add(0, Id.menu.export, 5, R.string.menu_exportKey); - menu.add(0, Id.menu.delete, 111, R.string.menu_deleteKey); - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - - // expInfo.id would also return row id of childs, but we always want to get the row id of - // the group item, thus we are using the following way - int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); - long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - - switch (item.getItemId()) { - case Id.menu.export: - long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); - if (masterKeyId == -1) { - masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId); - } - - mKeyListActivity.showExportKeysDialog(masterKeyId); - return true; - - case Id.menu.delete: - mKeyListActivity.showDeleteKeyDialog(keyRingRowId); - return true; - - default: - return super.onContextItemSelected(item); - - } - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java deleted file mode 100644 index 50fb64da2..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyListPublicActivity.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; - -public class KeyListPublicActivity extends KeyListActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mKeyType = Id.type.public_key; - - setContentView(R.layout.key_list_public_activity); - - mExportFilename = Constants.path.APP_DIR + "/pubexport.asc"; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(1, Id.menu.option.key_server, 1, R.string.menu_keyServer) - .setIcon(R.drawable.ic_menu_search_list) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS); - menu.add(1, Id.menu.option.import_from_qr_code, 2, R.string.menu_importFromQrCode) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.import_from_nfc, 3, R.string.menu_importFromNfc) - .setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case Id.menu.option.key_server: { - startActivityForResult(new Intent(this, KeyServerQueryActivity.class), 0); - - return true; - } - case Id.menu.option.import_from_file: { - Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); - intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_FILE); - startActivityForResult(intentImportFromFile, 0); - - return true; - } - - case Id.menu.option.import_from_qr_code: { - Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); - intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_QR_CODE); - startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code); - - return true; - } - - case Id.menu.option.import_from_nfc: { - Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); - intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_NFC); - startActivityForResult(intentImportFromFile, 0); - - 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(KeyServerQueryActivity.RESULT_EXTRA_TEXT) == null) { - // return; - // } - // - // Intent intent = new Intent(this, KeyListPublicActivity.class); - // intent.setAction(KeyListPublicActivity.ACTION_IMPORT); - // intent.putExtra(KeyListPublicActivity.EXTRA_TEXT, - // data.getStringExtra(KeyListActivity.EXTRA_TEXT)); - // handleActions(intent); - // break; - // } - // - // default: { - // super.onActivityResult(requestCode, resultCode, data); - // break; - // } - // } - // } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java b/APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java deleted file mode 100644 index 7c23e6217..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyListPublicFragment.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.ui.widget.KeyListAdapter; - -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.ContextMenu; -import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.ExpandableListView; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; - -public class KeyListPublicFragment extends KeyListFragment implements - LoaderManager.LoaderCallbacks { - - private KeyListPublicActivity mKeyListPublicActivity; - - private KeyListAdapter mAdapter; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListPublicActivity = (KeyListPublicActivity) getActivity(); - - mAdapter = new KeyListAdapter(mKeyListPublicActivity, null, Id.type.public_key); - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - // id is -1 as the child cursors are numbered 0,...,n - getLoaderManager().initLoader(-1, null, this); - } - - /** - * Context Menu on Long Click - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - menu.add(0, Id.menu.update, 1, R.string.menu_updateKey); - menu.add(0, Id.menu.signKey, 2, R.string.menu_signKey); - menu.add(0, Id.menu.exportToServer, 3, R.string.menu_exportKeyToServer); - menu.add(0, Id.menu.share, 6, R.string.menu_share); - menu.add(0, Id.menu.share_qr_code, 7, R.string.menu_shareQrCode); - menu.add(0, Id.menu.share_nfc, 8, R.string.menu_shareNfc); - - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - - // expInfo.id would also return row id of childs, but we always want to get the row id of - // the group item, thus we are using the following way - int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); - long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - - switch (item.getItemId()) { - case Id.menu.update: - long updateKeyId = 0; - PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId( - mKeyListActivity, keyRingRowId); - if (updateKeyRing != null) { - updateKeyId = PGPHelper.getMasterKey(updateKeyRing).getKeyID(); - } - if (updateKeyId == 0) { - // this shouldn't happen - return true; - } - - Intent queryIntent = new Intent(mKeyListActivity, KeyServerQueryActivity.class); - queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN); - queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId); - - // TODO: lookup?? - startActivityForResult(queryIntent, Id.request.look_up_key_id); - - return true; - - case Id.menu.exportToServer: - Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class); - uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER); - uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, keyRingRowId); - startActivityForResult(uploadIntent, Id.request.export_to_server); - - return true; - - case Id.menu.signKey: - long keyId = 0; - PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId( - mKeyListActivity, keyRingRowId); - if (signKeyRing != null) { - keyId = PGPHelper.getMasterKey(signKeyRing).getKeyID(); - } - if (keyId == 0) { - // this shouldn't happen - return true; - } - - Intent signIntent = new Intent(mKeyListActivity, SignKeyActivity.class); - signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId); - startActivity(signIntent); - - return true; - - case Id.menu.share_qr_code: - // get master key id using row id - long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); - - Intent qrCodeIntent = new Intent(mKeyListActivity, ShareActivity.class); - qrCodeIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING_WITH_QR_CODE); - qrCodeIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId); - startActivityForResult(qrCodeIntent, 0); - - return true; - - case Id.menu.share_nfc: - // get master key id using row id - long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); - - Intent nfcIntent = new Intent(mKeyListActivity, ShareNfcBeamActivity.class); - nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC); - nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2); - startActivityForResult(nfcIntent, 0); - - return true; - - case Id.menu.share: - // get master key id using row id - long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); - - Intent shareIntent = new Intent(mKeyListActivity, ShareActivity.class); - shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING); - shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3); - startActivityForResult(shareIntent, 0); - - return true; - - default: - return super.onContextItemSelected(item); - - } - } - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, - UserIds.USER_ID }; - - static final String SORT_ORDER = UserIds.USER_ID + " ASC"; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.setGroupCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.setGroupCursor(null); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java deleted file mode 100644 index 97abd2296..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyListSecretActivity.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; -import org.thialfihar.android.apg.util.Log; - -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; - -public class KeyListSecretActivity extends KeyListActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mKeyType = Id.type.secret_key; - - setContentView(R.layout.key_list_secret_activity); - - mExportFilename = Constants.path.APP_DIR + "/secexport.asc"; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - menu.add(1, Id.menu.option.create, 1, R.string.menu_createKey).setShowAsAction( - MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case Id.menu.option.create: { - createKey(); - return true; - } - - default: { - return super.onOptionsItemSelected(item); - } - } - } - - public void checkPassPhraseAndEdit(long keyId) { - String passPhrase = PassphraseCacheService.getCachedPassphrase(this, keyId); - if (passPhrase == null) { - showPassphraseDialog(keyId); - } else { - PGPMain.setEditPassPhrase(passPhrase); - editKey(keyId); - } - } - - private void showPassphraseDialog(final long secretKeyId) { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - String passPhrase = PassphraseCacheService.getCachedPassphrase( - KeyListSecretActivity.this, secretKeyId); - PGPMain.setEditPassPhrase(passPhrase); - editKey(secretKeyId); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( - KeyListSecretActivity.this, messenger, secretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - private void createKey() { - PGPMain.setEditPassPhrase(""); - Intent intent = new Intent(EditKeyActivity.ACTION_CREATE_KEY); - startActivityForResult(intent, 0); - } - - private void editKey(long keyId) { - Intent intent = new Intent(EditKeyActivity.ACTION_EDIT_KEY); - intent.putExtra(EditKeyActivity.EXTRA_KEY_ID, keyId); - startActivityForResult(intent, 0); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java b/APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java deleted file mode 100644 index 1e852fd54..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyListSecretFragment.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import java.util.ArrayList; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.ui.widget.KeyListAdapter; - -import com.google.zxing.integration.android.IntentIntegrator; - -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.ContextMenu; -import android.view.View; -import android.view.ContextMenu.ContextMenuInfo; -import android.widget.ExpandableListView; -import android.widget.Toast; -import android.widget.ExpandableListView.ExpandableListContextMenuInfo; - -public class KeyListSecretFragment extends KeyListFragment implements - LoaderManager.LoaderCallbacks { - - private KeyListSecretActivity mKeyListSecretActivity; - - private KeyListAdapter mAdapter; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mKeyListSecretActivity = (KeyListSecretActivity) getActivity(); - - mAdapter = new KeyListAdapter(mKeyListSecretActivity, null, Id.type.secret_key); - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - // id is -1 as the child cursors are numbered 0,...,n - getLoaderManager().initLoader(-1, null, this); - } - - /** - * Context Menu on Long Click - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - menu.add(0, Id.menu.edit, 0, R.string.menu_editKey); - } - - @Override - public boolean onContextItemSelected(android.view.MenuItem item) { - ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); - - // expInfo.id would also return row id of childs, but we always want to get the row id of - // the group item, thus we are using the following way - int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); - long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); - - // get master key id using row id - long masterKeyId = ProviderHelper - .getSecretMasterKeyId(mKeyListSecretActivity, keyRingRowId); - - switch (item.getItemId()) { - case Id.menu.edit: - mKeyListSecretActivity.checkPassPhraseAndEdit(masterKeyId); - - return true; - - default: - return super.onContextItemSelected(item); - - } - } - - - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, - UserIds.USER_ID }; - - static final String SORT_ORDER = UserIds.USER_ID + " ASC"; - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildSecretKeyRingsUri(); - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.setGroupCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.setGroupCursor(null); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java deleted file mode 100644 index 45375712f..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyServerQueryActivity.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import java.util.ArrayList; -import java.util.List; - -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.util.Log; -import org.thialfihar.android.apg.util.KeyServer.KeyInfo; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -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 SherlockFragmentActivity { - - // possible intent actions for this activity - public static final String ACTION_LOOK_UP_KEY_ID = Constants.INTENT_PREFIX + "LOOK_UP_KEY_ID"; - public static final String ACTION_LOOK_UP_KEY_ID_AND_RETURN = Constants.INTENT_PREFIX - + "LOOK_UP_KEY_ID_AND_RETURN"; - - public static final String EXTRA_KEY_ID = "keyId"; - - public static final String RESULT_EXTRA_TEXT = "text"; - - 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 - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, KeyListPublicActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - break; - - } - return false; - } - - @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, Preferences.getPreferences(this) - .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(); - String action = intent.getAction(); - if (ACTION_LOOK_UP_KEY_ID.equals(action) || ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(action)) { - long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); - if (keyId != 0) { - String query = "0x" + PGPHelper.keyToHex(keyId); - mQuery.setText(query); - search(query); - } - } - } - - private void search(String query) { - mQueryType = Id.keyserver.search; - mQueryString = query; - mAdapter.setKeys(new ArrayList()); - - start(); - } - - private void get(long keyId) { - mQueryType = Id.keyserver.get; - mQueryId = keyId; - - start(); - } - - private void start() { - Log.d(Constants.TAG, "start search with service"); - - // Send all information needed to service to query keys in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_QUERY_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - - String server = (String) mKeyServer.getSelectedItem(); - data.putString(ApgIntentService.QUERY_KEY_SERVER, server); - - data.putInt(ApgIntentService.QUERY_KEY_TYPE, mQueryType); - - if (mQueryType == Id.keyserver.search) { - data.putString(ApgIntentService.QUERY_KEY_STRING, mQueryString); - } else if (mQueryType == Id.keyserver.get) { - data.putLong(ApgIntentService.QUERY_KEY_ID, mQueryId); - } - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after querying is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, - R.string.progress_querying, ProgressDialog.STYLE_SPINNER) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get returned data bundle - Bundle returnData = message.getData(); - - if (mQueryType == Id.keyserver.search) { - mSearchResult = returnData - .getParcelableArrayList(ApgIntentService.RESULT_QUERY_KEY_SEARCH_RESULT); - } else if (mQueryType == Id.keyserver.get) { - mKeyData = returnData.getString(ApgIntentService.RESULT_QUERY_KEY_KEY_DATA); - } - - // TODO: IMPROVE CODE!!! some global variables can be avoided!!! - if (mQueryType == Id.keyserver.search) { - if (mSearchResult != null) { - Toast.makeText(KeyServerQueryActivity.this, - getString(R.string.keysFound, mSearchResult.size()), - Toast.LENGTH_SHORT).show(); - mAdapter.setKeys(mSearchResult); - } - } else if (mQueryType == Id.keyserver.get) { - Intent orgIntent = getIntent(); - if (ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(orgIntent.getAction())) { - if (mKeyData != null) { - Intent intent = new Intent(); - intent.putExtra(RESULT_EXTRA_TEXT, mKeyData); - setResult(RESULT_OK, intent); - } else { - setResult(RESULT_CANCELED); - } - finish(); - } else { - if (mKeyData != null) { - Intent intent = new Intent(KeyServerQueryActivity.this, - ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT); - intent.putExtra(ImportKeysActivity.EXTRA_TEXT, mKeyData); - startActivity(intent); - } - } - } - - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(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 ArrayList(); - } - - 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(PGPHelper.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.MATCH_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/APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java b/APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java deleted file mode 100644 index f1ac05735..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/KeyServerUploadActivity.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.MenuItem; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -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 KeyServerUploadActivity extends SherlockFragmentActivity { - - // Not used in sourcode, but listed in AndroidManifest! - public static final String ACTION_EXPORT_KEY_TO_SERVER = Constants.INTENT_PREFIX - + "EXPORT_KEY_TO_SERVER"; - - public static final String EXTRA_KEYRING_ROW_ID = "keyId"; - - private Button export; - private Spinner keyServer; - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, KeyListPublicActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - break; - - } - return false; - } - - @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, Preferences.getPreferences(this) - .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) { - uploadKey(); - } - }); - } - - private void uploadKey() { - // Send all information needed to service to upload key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_UPLOAD_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - - int keyRingId = getIntent().getIntExtra(EXTRA_KEYRING_ROW_ID, -1); - data.putInt(ApgIntentService.UPLOAD_KEY_KEYRING_ROW_ID, keyRingId); - - String server = (String) keyServer.getSelectedItem(); - data.putString(ApgIntentService.UPLOAD_KEY_SERVER, server); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after uploading is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - - Toast.makeText(KeyServerUploadActivity.this, R.string.keySendSuccess, - Toast.LENGTH_SHORT).show(); - finish(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/MainActivity.java b/APG/src/org/thialfihar/android/apg/ui/MainActivity.java deleted file mode 100644 index b647299d1..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/MainActivity.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; -import android.view.View; - -public class MainActivity extends SherlockActivity { - - public void manageKeysOnClick(View view) { - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(new Intent(this, KeyListPublicActivity.class), 0); - } - - public void myKeysOnClick(View view) { - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(new Intent(this, KeyListSecretActivity.class), 0); - } - - public void encryptOnClick(View view) { - Intent intent = new Intent(MainActivity.this, EncryptActivity.class); - intent.setAction(EncryptActivity.ACTION_ENCRYPT); - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - } - - public void decryptOnClick(View view) { - Intent intent = new Intent(MainActivity.this, DecryptActivity.class); - intent.setAction(DecryptActivity.ACTION_DECRYPT); - // used instead of startActivity set actionbar based on callingPackage - startActivityForResult(intent, 0); - } - - public void scanQrcodeOnClick(View view) { - Intent intent = new Intent(this, ImportKeysActivity.class); - startActivityForResult(intent, 0); - } - - public void helpOnClick(View view) { - startActivity(new Intent(this, HelpActivity.class)); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences) - .setIcon(R.drawable.ic_menu_settings) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case Id.menu.option.preferences: - startActivity(new Intent(this, PreferencesActivity.class)); - return true; - - default: - break; - - } - return false; - } - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java b/APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java deleted file mode 100644 index e28b385f2..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/PreferencesActivity.java +++ /dev/null @@ -1,240 +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.thialfihar.android.apg.ui; - -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.ui.widget.IntegerListPreference; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockPreferenceActivity; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.PreferenceScreen; - -public class PreferencesActivity extends SherlockPreferenceActivity { - 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); - super.onCreate(savedInstanceState); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - - addPreferencesFromResource(R.xml.apg_preferences); - - 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())); - 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, - PreferencesKeyServerActivity.class); - intent.putExtra(PreferencesKeyServerActivity.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(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); - mPreferences.setKeyServers(servers); - mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, - servers.length)); - break; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - break; - } - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - break; - - } - return false; - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java b/APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java deleted file mode 100644 index b54cbde71..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/PreferencesKeyServerActivity.java +++ /dev/null @@ -1,162 +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.thialfihar.android.apg.ui; - -import java.util.Vector; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.ui.widget.Editor; -import org.thialfihar.android.apg.ui.widget.KeyServerEditor; -import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.TextView; - -public class PreferencesKeyServerActivity extends SherlockActivity implements OnClickListener, - EditorListener { - - public static final String EXTRA_KEY_SERVERS = "keyServers"; - - private LayoutInflater mInflater; - private ViewGroup mEditors; - private View mAdd; - private TextView mTitle; - private TextView mSummary; - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, PreferencesActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - - return true; - - case Id.menu.option.okay: - okClicked(); - - return true; - - case Id.menu.option.cancel: - cancelClicked(); - - return true; - - default: - break; - - } - return false; - } - - /** - * ActionBar menu is created based on class variables to change it at runtime - * - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - menu.add(1, Id.menu.option.cancel, 0, android.R.string.cancel).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - menu.add(1, Id.menu.option.okay, 1, android.R.string.ok).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - - return true; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.key_server_preference); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - - 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(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); - } - } - } - - 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(EXTRA_KEY_SERVERS, servers.toArray(dummy)); - setResult(RESULT_OK, data); - finish(); - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java deleted file mode 100644 index caef712ee..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyActivity.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; - -public class SelectPublicKeyActivity extends SherlockFragmentActivity { - - // Not used in sourcode, but listed in AndroidManifest! - public static final String ACTION_SELECT_PUBLIC_KEYS = Constants.INTENT_PREFIX - + "SELECT_PUBLIC_KEYS"; - - public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "masterKeyIds"; - - public static final String RESULT_EXTRA_MASTER_KEY_IDS = "masterKeyIds"; - public static final String RESULT_EXTRA_USER_IDS = "userIds"; - - SelectPublicKeyFragment mSelectFragment; - - long selectedMasterKeyIds[]; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.select_public_key_activity); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - mSelectFragment = (SelectPublicKeyFragment) getSupportFragmentManager().findFragmentById( - R.id.select_public_key_fragment); - - // TODO: reimplement! - // 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) { - // TODO: reimplement! - - // 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)); - // } - - // preselected master keys - selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); - } - - /** - * returns preselected key ids, this is used in the fragment - * - * @return - */ - public long[] getSelectedMasterKeyIds() { - return selectedMasterKeyIds; - } - - private void cancelClicked() { - setResult(RESULT_CANCELED, null); - finish(); - } - - private void okClicked() { - Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds()); - data.putExtra(RESULT_EXTRA_USER_IDS, mSelectFragment.getSelectedUserIds()); - setResult(RESULT_OK, data); - finish(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // TODO: reimplement! - // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( - // android.R.drawable.ic_menu_search); - menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - menu.add(1, Id.menu.option.okay, 1, R.string.btn_okay).setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - return true; - } - - /** - * Menu Options - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case Id.menu.option.okay: - okClicked(); - return true; - - case Id.menu.option.cancel: - cancelClicked(); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java b/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java deleted file mode 100644 index a316fbdd3..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/SelectPublicKeyFragment.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import java.util.Date; -import java.util.Vector; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.compatibility.ListFragmentWorkaround; -import org.thialfihar.android.apg.provider.ApgDatabase; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgDatabase.Tables; -import org.thialfihar.android.apg.ui.widget.SelectKeyCursorAdapter; - -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.widget.ListView; - -public class SelectPublicKeyFragment extends ListFragmentWorkaround implements - LoaderManager.LoaderCallbacks { - - private SelectPublicKeyActivity mActivity; - private SelectKeyCursorAdapter mAdapter; - private ListView mListView; - - private long mSelectedMasterKeyIds[]; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mActivity = (SelectPublicKeyActivity) getSherlockActivity(); - mListView = getListView(); - - // get selected master key ids, which are given to activity by intent - mSelectedMasterKeyIds = mActivity.getSelectedMasterKeyIds(); - - mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.listEmpty)); - - mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.public_key); - - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - /** - * Selects items based on master key ids in list view - * - * @param masterKeyIds - */ - private void preselectMasterKeyIds(long[] masterKeyIds) { - if (masterKeyIds != null) { - for (int i = 0; i < mListView.getCount(); ++i) { - long keyId = mAdapter.getMasterKeyId(i); - for (int j = 0; j < masterKeyIds.length; ++j) { - if (keyId == masterKeyIds[j]) { - mListView.setItemChecked(i, true); - break; - } - } - } - } - } - - /** - * Returns all selected master key ids - * - * @return - */ - public long[] getSelectedMasterKeyIds() { - // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key - // ids! - Vector vector = new Vector(); - for (int i = 0; i < mListView.getCount(); ++i) { - if (mListView.isItemChecked(i)) { - vector.add(mAdapter.getMasterKeyId(i)); - } - } - - // convert to long array - long[] selectedMasterKeyIds = new long[vector.size()]; - for (int i = 0; i < vector.size(); ++i) { - selectedMasterKeyIds[i] = vector.get(i); - } - - return selectedMasterKeyIds; - } - - /** - * Returns all selected user ids - * - * @return - */ - public String[] getSelectedUserIds() { - Vector userIds = new Vector(); - for (int i = 0; i < mListView.getCount(); ++i) { - if (mListView.isItemChecked(i)) { - userIds.add((String) mAdapter.getUserId(i)); - } - } - - // make empty array to not return null - String userIdArray[] = new String[0]; - return userIds.toArray(userIdArray); - } - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - - // These are the rows that we will retrieve. - long now = new Date().getTime() / 1000; - String[] projection = new String[] { - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - UserIds.USER_ID, - "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID - + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." - + Keys.CAN_ENCRYPT + " = '1') AS " - + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, - "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." - + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_ENCRYPT - + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " - + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY - + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; - - String inMasterKeyList = null; - if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) { - inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN ("; - for (int i = 0; i < mSelectedMasterKeyIds.length; ++i) { - if (i != 0) { - inMasterKeyList += ", "; - } - inMasterKeyList += DatabaseUtils.sqlEscapeString("" + mSelectedMasterKeyIds[i]); - } - inMasterKeyList += ")"; - } - - // 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.USER_ID + " ASC"; - if (inMasterKeyList != null) { - // sort by selected master keys - orderBy = inMasterKeyList + " DESC, " + orderBy; - } - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - - // preselect given master keys - preselectMasterKeyIds(mSelectedMasterKeyIds); - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java deleted file mode 100644 index cc763d4a1..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyActivity.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; - -import android.content.Intent; -import android.os.Bundle; - -public class SelectSecretKeyActivity extends SherlockFragmentActivity { - - // Not used in sourcode, but listed in AndroidManifest! - public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX - + "SELECT_SECRET_KEY"; - - public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; - public static final String RESULT_EXTRA_USER_ID = "userId"; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.select_secret_key_activity); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - // TODO: reimplement! - // 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()); - } - - /** - * This is executed by SelectSecretKeyFragment after clicking on an item - * - * @param masterKeyId - * @param userId - */ - public void afterListSelection(long masterKeyId, String userId) { - Intent data = new Intent(); - data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); - data.putExtra(RESULT_EXTRA_USER_ID, (String) userId); - setResult(RESULT_OK, data); - finish(); - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - // TODO: reimplement! - - // 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)); - // } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // TODO: reimplement! - // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( - // android.R.drawable.ic_menu_search); - return true; - } - - /** - * Menu Options - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - // app icon in Action Bar clicked; go home - Intent intent = new Intent(this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java b/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java deleted file mode 100644 index 1f3fcb816..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/SelectSecretKeyFragment.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui; - -import java.util.Date; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ApgDatabase; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.provider.ApgDatabase.Tables; -import org.thialfihar.android.apg.ui.widget.SelectKeyCursorAdapter; - -import com.actionbarsherlock.app.SherlockListFragment; - -import android.database.Cursor; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ListView; - -public class SelectSecretKeyFragment extends SherlockListFragment implements - LoaderManager.LoaderCallbacks { - - private SelectSecretKeyActivity mActivity; - private SelectKeyCursorAdapter mAdapter; - private ListView mListView; - - /** - * Define Adapter and Loader on create of Activity - */ - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mActivity = (SelectSecretKeyActivity) getSherlockActivity(); - mListView = getListView(); - - mListView.setOnItemClickListener(new OnItemClickListener() { - @Override - public void onItemClick(AdapterView adapterView, View view, int position, long id) { - long masterKeyId = mAdapter.getMasterKeyId(position); - String userId = mAdapter.getUserId(position); - - // return data to activity, which results in finishing it - mActivity.afterListSelection(masterKeyId, userId); - } - }); - - // Give some text to display if there is no data. In a real - // application this would come from a resource. - setEmptyText(getString(R.string.listEmpty)); - - mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.secret_key); - - setListAdapter(mAdapter); - - // Start out with a progress indicator. - setListShown(false); - - // Prepare the loader. Either re-connect with an existing one, - // or start a new one. - getLoaderManager().initLoader(0, null, this); - } - - @Override - public Loader onCreateLoader(int id, Bundle args) { - // This is called when a new Loader needs to be created. This - // sample only has one Loader, so we don't care about the ID. - Uri baseUri = KeyRings.buildPublicKeyRingsUri(); - - // These are the rows that we will retrieve. - long now = new Date().getTime() / 1000; - String[] projection = new String[] { - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - UserIds.USER_ID, - "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID - + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." - + Keys.CAN_SIGN + " = '1') AS " - + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, - "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS - + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " - + ApgDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." - + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN - + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " - + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY - + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; - - // 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(")"); - // } - - String orderBy = UserIds.USER_ID + " ASC"; - - // Now create and return a CursorLoader that will take care of - // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - mAdapter.swapCursor(data); - - // The list should now be shown. - if (isResumed()) { - setListShown(true); - } else { - setListShownNoAnimation(true); - } - } - - @Override - public void onLoaderReset(Loader loader) { - // This is called when the last Cursor provided to onLoadFinished() - // above is about to be closed. We need to make sure we are no - // longer using it. - mAdapter.swapCursor(null); - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/ShareActivity.java b/APG/src/org/thialfihar/android/apg/ui/ShareActivity.java deleted file mode 100644 index 7fec42e47..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/ShareActivity.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.util.ArrayList; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.provider.ProviderHelper; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.google.zxing.integration.android.IntentIntegrator; - -import android.content.Intent; -import android.os.Bundle; - -public class ShareActivity extends SherlockFragmentActivity { - public static final String ACTION_SHARE_KEYRING = Constants.INTENT_PREFIX + "SHARE_KEYRING"; - public static final String ACTION_SHARE_KEYRING_WITH_QR_CODE = Constants.INTENT_PREFIX - + "SHARE_KEYRING_WITH_QR_CODE"; - - public static final String EXTRA_MASTER_KEY_ID = "masterKeyId"; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - handleActions(getIntent()); - } - - protected void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - - if (extras == null) { - extras = new Bundle(); - } - - long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID); - - // get public keyring as ascii armored string - ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this, - new long[] { masterKeyId }); - - // close this activity - finish(); - - if (ACTION_SHARE_KEYRING.equals(action)) { - // let user choose application - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, keyringArmored.get(0)); - sendIntent.setType("text/plain"); - startActivity(Intent.createChooser(sendIntent, - getResources().getText(R.string.shareKeyringWith))); - } else if (ACTION_SHARE_KEYRING_WITH_QR_CODE.equals(action)) { - // use barcode scanner integration library - new IntentIntegrator(this).shareText(keyringArmored.get(0)); - } - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/ShareNfcBeamActivity.java b/APG/src/org/thialfihar/android/apg/ui/ShareNfcBeamActivity.java deleted file mode 100644 index abb2d6b82..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/ShareNfcBeamActivity.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2013 Dominik Schürmann - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.io.IOException; -import java.io.InputStream; - -import net.nightwhistler.htmlspanner.HtmlSpanner; -import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.provider.ProviderHelper; - -import com.actionbarsherlock.app.SherlockFragmentActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuInflater; -import com.actionbarsherlock.view.MenuItem; - -import android.annotation.TargetApi; -import android.content.Intent; -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.NfcAdapter; -import android.nfc.NfcAdapter.CreateNdefMessageCallback; -import android.nfc.NfcEvent; -import android.os.Build; -import android.os.Bundle; -import android.os.Parcelable; -import android.util.Log; -import android.widget.Toast; -import android.nfc.NfcAdapter.OnNdefPushCompleteCallback; -import android.os.Handler; -import android.os.Message; -import android.provider.Settings; -import android.text.method.LinkMovementMethod; - -@TargetApi(Build.VERSION_CODES.JELLY_BEAN) -public class ShareNfcBeamActivity extends SherlockFragmentActivity implements - CreateNdefMessageCallback, OnNdefPushCompleteCallback { - public static final String ACTION_SHARE_KEYRING_WITH_NFC = Constants.INTENT_PREFIX - + "SHARE_KEYRING_WITH_NFC"; - - public static final String EXTRA_MASTER_KEY_ID = "masterKeyId"; - - NfcAdapter mNfcAdapter; - - byte[] mSharedKeyringBytes; - - private static final int MESSAGE_SENT = 1; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { - Toast.makeText(this, - getString(R.string.error) + ": " + getString(R.string.error_jellyBeanNeeded), - Toast.LENGTH_LONG).show(); - finish(); - } else { - // Check for available NFC Adapter - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - if (mNfcAdapter == null) { - Toast.makeText(this, - getString(R.string.error) + ": " + getString(R.string.error_nfcNeeded), - Toast.LENGTH_LONG).show(); - finish(); - } else { - // handle actions after verifying that nfc works... - handleActions(getIntent()); - } - } - } - - protected void handleActions(Intent intent) { - String action = intent.getAction(); - Bundle extras = intent.getExtras(); - - if (extras == null) { - extras = new Bundle(); - } - - if (ACTION_SHARE_KEYRING_WITH_NFC.equals(action)) { - long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID); - - // get public keyring as byte array - mSharedKeyringBytes = ProviderHelper.getPublicKeyRingsAsByteArray(this, - new long[] { masterKeyId }); - - // Register callback to set NDEF message - mNfcAdapter.setNdefPushMessageCallback(this, this); - // Register callback to listen for message-sent success - mNfcAdapter.setOnNdefPushCompleteCallback(this, this); - } - } - - /** - * Parses the NDEF Message from the intent and prints to the TextView - */ - void handleActionNdefDiscovered(Intent intent) { - Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); - // only one message sent during the beam - NdefMessage msg = (NdefMessage) rawMsgs[0]; - // record 0 contains the MIME type, record 1 is the AAR, if present - byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload(); - - Intent importIntent = new Intent(this, ImportKeysActivity.class); - importIntent.setAction(ImportKeysActivity.ACTION_IMPORT); - importIntent.putExtra(ImportKeysActivity.EXTRA_KEYRING_BYTES, receivedKeyringBytes); - - finish(); - - startActivity(importIntent); - } - - private void buildView() { - // load html from html file from /res/raw - InputStream inputStreamText = getResources().openRawResource(R.raw.nfc_beam_share); - - setContentView(R.layout.share_nfc_beam); - - JellyBeanSpanFixTextView text = (JellyBeanSpanFixTextView) findViewById(R.id.nfc_beam_text); - - // load html into textview - HtmlSpanner htmlSpanner = new HtmlSpanner(); - htmlSpanner.setStripExtraWhiteSpace(true); - try { - text.setText(htmlSpanner.fromHtml(inputStreamText)); - } catch (IOException e) { - Log.e(Constants.TAG, "Error while reading raw resources as stream", e); - } - - // make links work - text.setMovementMethod(LinkMovementMethod.getInstance()); - - // no flickering when clicking textview for Android < 4 - text.setTextColor(getResources().getColor(android.R.color.black)); - - // set actionbar without home button if called from another app - OtherHelper.setActionBarBackButton(this); - } - - /** - * Implementation for the CreateNdefMessageCallback interface - */ - @Override - public NdefMessage createNdefMessage(NfcEvent event) { - /** - * When a device receives a push with an AAR in it, the application specified in the AAR is - * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to - * guarantee that this activity starts when receiving a beamed message. For now, this code - * uses the tag dispatch system. - */ - NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, - mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); - return msg; - } - - /** - * Implementation for the OnNdefPushCompleteCallback interface - */ - @Override - public void onNdefPushComplete(NfcEvent arg0) { - // A handler is needed to send messages to the activity when this - // callback occurs, because it happens from a binder thread - mHandler.obtainMessage(MESSAGE_SENT).sendToTarget(); - } - - /** This handler receives a message from onNdefPushComplete */ - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_SENT: - Toast.makeText(getApplicationContext(), R.string.nfcSuccessfull, Toast.LENGTH_LONG) - .show(); - break; - } - } - }; - - @Override - public void onResume() { - super.onResume(); - // Check to see that the Activity started due to an Android Beam - if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { - handleActionNdefDiscovered(getIntent()); - } else { - // build view only when sending nfc, not when receiving, as it gets directly into Import - // activity on receiving - buildView(); - } - } - - @Override - public void onNewIntent(Intent intent) { - // onResume gets called after this to handle the intent - setIntent(intent); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getSupportMenuInflater(); - inflater.inflate(R.menu.nfc_beam, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - - case android.R.id.home: - // app icon in Action Bar clicked; go to KeyListPublicActivity - Intent intent = new Intent(this, KeyListPublicActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(intent); - return true; - - case R.id.menu_settings: - Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); - startActivity(intentSettings); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java b/APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java deleted file mode 100644 index f8d47688c..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/SignKeyActivity.java +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui; - -import java.util.Iterator; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSignature; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.Preferences; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.PassphraseDialogFragment; - -import com.actionbarsherlock.app.ActionBar; -import com.actionbarsherlock.app.SherlockFragmentActivity; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; - -import org.thialfihar.android.apg.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 SherlockFragmentActivity { - - public static final String EXTRA_KEY_ID = "keyId"; - - private long mPubKeyId = 0; - private long mMasterKeyId = 0; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // check we havent already signed it - setContentView(R.layout.sign_key_layout); - - final ActionBar actionBar = getSupportActionBar(); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayHomeAsUpEnabled(false); - actionBar.setHomeButtonEnabled(false); - - final Spinner keyServer = (Spinner) findViewById(R.id.keyServer); - ArrayAdapter adapter = new ArrayAdapter(this, - android.R.layout.simple_spinner_item, Preferences.getPreferences(this) - .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 (mPubKeyId != 0) { - initiateSigning(); - } - } - }); - - mPubKeyId = getIntent().getLongExtra(EXTRA_KEY_ID, 0); - if (mPubKeyId == 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, SelectSecretKeyActivity.class); - startActivityForResult(intent, Id.request.secret_keys); - } - } - - private void showPassphraseDialog(final long secretKeyId) { - // Message is received after passphrase is cached - Handler returnHandler = new Handler() { - @Override - public void handleMessage(Message message) { - if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - startSigning(); - } - } - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(returnHandler); - - try { - PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, - messenger, secretKeyId); - - passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); - } catch (PGPMain.ApgGeneralException e) { - Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); - // send message to handler to start encryption directly - returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); - } - } - - /** - * handles the UI bits of the signing process on the UI thread - */ - private void initiateSigning() { - PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId); - if (pubring != null) { - // if we have already signed this key, dont bother doing it again - boolean alreadySigned = false; - - @SuppressWarnings("unchecked") - Iterator itr = pubring.getPublicKey(mPubKeyId).getSignatures(); - while (itr.hasNext()) { - PGPSignature sig = itr.next(); - if (sig.getKeyID() == mMasterKeyId) { - alreadySigned = true; - break; - } - } - - if (!alreadySigned) { - /* - * get the user's passphrase for this key (if required) - */ - String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId); - if (passphrase == null) { - showPassphraseDialog(mMasterKeyId); - return; // bail out; need to wait until the user has entered the passphrase - // before trying again - } else { - startSigning(); - } - } else { - Toast.makeText(this, "Key has already been signed", Toast.LENGTH_SHORT).show(); - - setResult(RESULT_CANCELED); - finish(); - } - } - } - - /** - * kicks off the actual signing process on a background thread - */ - private void startSigning() { - // Send all information needed to service to sign key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_SIGN_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - - data.putLong(ApgIntentService.SIGN_KEY_MASTER_KEY_ID, mMasterKeyId); - data.putLong(ApgIntentService.SIGN_KEY_PUB_KEY_ID, mPubKeyId); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after signing is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_signing, - ProgressDialog.STYLE_SPINNER) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - - Toast.makeText(SignKeyActivity.this, R.string.keySignSuccess, - Toast.LENGTH_SHORT).show(); - - // check if we need to send the key to the server or not - CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey); - if (sendKey.isChecked()) { - /* - * upload the newly signed key to the key server - */ - uploadKey(); - } else { - finish(); - } - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - private void uploadKey() { - // Send all information needed to service to upload key in other thread - Intent intent = new Intent(this, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_UPLOAD_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - - data.putLong(ApgIntentService.UPLOAD_KEY_KEYRING_ROW_ID, mPubKeyId); - - Spinner keyServer = (Spinner) findViewById(R.id.keyServer); - String server = (String) keyServer.getSelectedItem(); - data.putString(ApgIntentService.UPLOAD_KEY_SERVER, server); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // Message is received after uploading is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(this, R.string.progress_exporting, - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - - Toast.makeText(SignKeyActivity.this, R.string.keySendSuccess, - Toast.LENGTH_SHORT).show(); - - finish(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(this); - - // start service with intent - startService(intent); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case Id.request.secret_keys: { - if (resultCode == RESULT_OK) { - mMasterKeyId = data.getLongExtra(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); - } - } - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java deleted file mode 100644 index 342cd41d0..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteFileDialogFragment.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; -import android.widget.Toast; - -public class DeleteFileDialogFragment extends DialogFragment { - private static final String ARG_DELETE_FILE = "delete_file"; - - /** - * Creates new instance of this delete file dialog fragment - */ - public static DeleteFileDialogFragment newInstance(String deleteFile) { - DeleteFileDialogFragment frag = new DeleteFileDialogFragment(); - Bundle args = new Bundle(); - - args.putString(ARG_DELETE_FILE, deleteFile); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final FragmentActivity activity = getActivity(); - - final String deleteFile = getArguments().getString(ARG_DELETE_FILE); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.warning); - alert.setMessage(this.getString(R.string.fileDeleteConfirmation, deleteFile)); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(activity, ApgIntentService.class); - - // fill values for this action - Bundle data = new Bundle(); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_DELETE_FILE_SECURELY); - data.putString(ApgIntentService.DELETE_FILE, deleteFile); - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance( - R.string.progress_deletingSecurely, ProgressDialog.STYLE_HORIZONTAL); - - // Message is received after deleting is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(activity, deletingDialog) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - Toast.makeText(activity, R.string.fileDeleteSuccessful, - Toast.LENGTH_SHORT).show(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - deletingDialog.show(activity.getSupportFragmentManager(), "deletingDialog"); - - // start service with intent - activity.startService(intent); - } - }); - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - alert.setCancelable(true); - - return alert.create(); - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java deleted file mode 100644 index 97477ee17..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/dialog/DeleteKeyDialogFragment.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.util.Log; - -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.FragmentActivity; - -public class DeleteKeyDialogFragment extends DialogFragment { - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_DELETE_KEY_RING_ROW_ID = "delete_file"; - private static final String ARG_KEY_TYPE = "key_type"; - - public static final int MESSAGE_OKAY = 1; - - private Messenger mMessenger; - - /** - * Creates new instance of this delete file dialog fragment - */ - public static DeleteKeyDialogFragment newInstance(Messenger messenger, long deleteKeyRingRowId, - int keyType) { - DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment(); - Bundle args = new Bundle(); - - args.putParcelable(ARG_MESSENGER, messenger); - args.putLong(ARG_DELETE_KEY_RING_ROW_ID, deleteKeyRingRowId); - args.putInt(ARG_KEY_TYPE, keyType); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final FragmentActivity activity = getActivity(); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - final long deleteKeyRingRowId = getArguments().getLong(ARG_DELETE_KEY_RING_ROW_ID); - final int keyType = getArguments().getInt(ARG_KEY_TYPE); - - // TODO: better way to do this? - String userId = activity.getString(R.string.unknownUserId); - - if (keyType == Id.type.public_key) { - PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity, - deleteKeyRingRowId); - userId = PGPHelper.getMainUserIdSafe(activity, PGPHelper.getMasterKey(keyRing)); - } else { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity, - deleteKeyRingRowId); - userId = PGPHelper.getMainUserIdSafe(activity, PGPHelper.getMasterKey(keyRing)); - } - - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(R.string.warning); - builder.setMessage(getString( - keyType == 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() { - - @Override - public void onClick(DialogInterface dialog, int id) { - if (keyType == Id.type.public_key) { - ProviderHelper.deletePublicKeyRing(activity, deleteKeyRingRowId); - } else { - ProviderHelper.deleteSecretKeyRing(activity, deleteKeyRingRowId); - } - - dismiss(); - - sendMessageToHandler(MESSAGE_OKAY); - } - }); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - return builder.create(); - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what) { - Message msg = Message.obtain(); - msg.what = what; - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java deleted file mode 100644 index 4a9596743..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/dialog/FileDialogFragment.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.FileHelper; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; -import org.thialfihar.android.apg.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageButton; - -public class FileDialogFragment extends DialogFragment { - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_TITLE = "title"; - private static final String ARG_MESSAGE = "message"; - private static final String ARG_DEFAULT_FILE = "default_file"; - private static final String ARG_CHECKBOX_TEXT = "checkbox_text"; - private static final String ARG_REQUEST_CODE = "request_code"; - - public static final int MESSAGE_OKAY = 1; - - public static final String MESSAGE_DATA_FILENAME = "filename"; - public static final String MESSAGE_DATA_CHECKED = "checked"; - - private Messenger mMessenger; - - /** - * Creates new instance of this file dialog fragment - */ - public static FileDialogFragment newInstance(Messenger messenger, String title, String message, - String defaultFile, String checkboxText, int requestCode) { - FileDialogFragment frag = new FileDialogFragment(); - Bundle args = new Bundle(); - args.putParcelable(ARG_MESSENGER, messenger); - - args.putString(ARG_TITLE, title); - args.putString(ARG_MESSAGE, message); - args.putString(ARG_DEFAULT_FILE, defaultFile); - args.putString(ARG_CHECKBOX_TEXT, checkboxText); - args.putInt(ARG_REQUEST_CODE, requestCode); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - String title = getArguments().getString(ARG_TITLE); - String message = getArguments().getString(ARG_MESSAGE); - String defaultFile = getArguments().getString(ARG_DEFAULT_FILE); - String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT); - final int requestCode = getArguments().getInt(ARG_REQUEST_CODE); - - final EditText mFilename; - final ImageButton mBrowse; - final CheckBox mCheckBox; - - 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); - - 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) { - // only .asc or .gpg files - FileHelper.openFile(activity, mFilename.getText().toString(), "text/plain", - 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); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - boolean checked = false; - if (mCheckBox.isEnabled()) { - checked = mCheckBox.isChecked(); - } - - // return resulting data back to activity - Bundle data = new Bundle(); - data.putString(MESSAGE_DATA_FILENAME, mFilename.getText().toString()); - data.putBoolean(MESSAGE_DATA_CHECKED, checked); - - sendMessageToHandler(MESSAGE_OKAY, data); - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - return alert.create(); - } - - /** - * Updates filename in dialog, normally called in onActivityResult in activity using the - * FileDialog - * - * @param messageId - * @param progress - * @param max - */ - public void setFilename(String filename) { - AlertDialog dialog = (AlertDialog) getDialog(); - EditText filenameEditText = (EditText) dialog.findViewById(R.id.input); - - if (filenameEditText != null) { - filenameEditText.setText(filename); - } - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what, Bundle data) { - Message msg = Message.obtain(); - msg.what = what; - if (data != null) { - msg.setData(data); - } - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java deleted file mode 100644 index 359c94ee9..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/dialog/LookupUnknownKeyDialogFragment.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; - -import org.thialfihar.android.apg.ui.KeyServerQueryActivity; -import org.thialfihar.android.apg.util.Log; - -public class LookupUnknownKeyDialogFragment extends DialogFragment { - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_UNKNOWN_KEY_ID = "unknown_key_id"; - - public static final int MESSAGE_OKAY = 1; - public static final int MESSAGE_CANCEL = 2; - - private Messenger mMessenger; - - /** - * Creates new instance of this dialog fragment - * - * @param messenger - * @param unknownKeyId - * @return - */ - public static LookupUnknownKeyDialogFragment newInstance(Messenger messenger, long unknownKeyId) { - LookupUnknownKeyDialogFragment frag = new LookupUnknownKeyDialogFragment(); - Bundle args = new Bundle(); - args.putLong(ARG_UNKNOWN_KEY_ID, unknownKeyId); - args.putParcelable(ARG_MESSENGER, messenger); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - final long unknownKeyId = getArguments().getLong(ARG_UNKNOWN_KEY_ID); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.title_unknownSignatureKey); - alert.setMessage(getString(R.string.lookupUnknownKey, - PGPHelper.getSmallFingerPrint(unknownKeyId))); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - sendMessageToHandler(MESSAGE_OKAY); - - Intent intent = new Intent(activity, KeyServerQueryActivity.class); - intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); - intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, unknownKeyId); - startActivityForResult(intent, Id.request.look_up_key_id); - } - }); - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - sendMessageToHandler(MESSAGE_CANCEL); - } - }); - alert.setCancelable(true); - alert.setOnCancelListener(new OnCancelListener() { - - @Override - public void onCancel(DialogInterface dialog) { - sendMessageToHandler(MESSAGE_CANCEL); - } - }); - - return alert.create(); - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what) { - Message msg = Message.obtain(); - msg.what = what; - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java deleted file mode 100644 index 236386efe..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/dialog/PassphraseDialogFragment.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPPrivateKey; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; -import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; -import org.thialfihar.android.apg.helper.PGPMain.ApgGeneralException; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.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.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; - -import org.thialfihar.android.apg.provider.ProviderHelper; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.util.Log; - -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager.LayoutParams; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; -import android.widget.Toast; - -public class PassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_SECRET_KEY_ID = "secret_key_id"; - - public static final int MESSAGE_OKAY = 1; - - private Messenger mMessenger; - private EditText mPassphraseEditText; - - /** - * Creates new instance of this dialog fragment - * - * @param secretKeyId - * secret key id you want to use - * @param messenger - * to communicate back after caching the passphrase - * @return - * @throws ApgGeneralException - */ - public static PassphraseDialogFragment newInstance(Context context, Messenger messenger, - long secretKeyId) throws ApgGeneralException { - // check if secret key has a passphrase - if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) { - if (!hasPassphrase(context, secretKeyId)) { - throw new PGPMain.ApgGeneralException("No passphrase! No passphrase dialog needed!"); - } - } - - PassphraseDialogFragment frag = new PassphraseDialogFragment(); - Bundle args = new Bundle(); - args.putLong(ARG_SECRET_KEY_ID, secretKeyId); - args.putParcelable(ARG_MESSENGER, messenger); - - frag.setArguments(args); - - return frag; - } - - /** - * Checks if key has a passphrase - * - * @param secretKeyId - * @return true if it has a passphrase - */ - private static boolean hasPassphrase(Context context, long secretKeyId) { - // check if the key has no passphrase - try { - PGPSecretKey secretKey = PGPHelper.getMasterKey(ProviderHelper - .getPGPSecretKeyRingByKeyId(context, secretKeyId)); - // PGPSecretKey secretKey = - // PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); - - Log.d(Constants.TAG, "Check if key has no passphrase..."); - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - "SC").build("".toCharArray()); - PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); - if (testKey != null) { - Log.d(Constants.TAG, "Key has no passphrase! Caches empty passphrase!"); - - // cache empty passphrase - PassphraseCacheService.addCachedPassphrase(context, secretKey.getKeyID(), ""); - - return false; - } - } catch (PGPException e) { - // silently catch - } - - return true; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setTitle(R.string.title_authentication); - - final PGPSecretKey secretKey; - - if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) { - secretKey = null; - alert.setMessage(R.string.passPhraseForSymmetricEncryption); - } else { - // TODO: by master key id??? - secretKey = PGPHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - activity, secretKeyId)); - // secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); - - if (secretKey == null) { - alert.setTitle(R.string.title_keyNotFound); - alert.setMessage(getString(R.string.keyNotFound, secretKeyId)); - alert.setPositiveButton(android.R.string.ok, new OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dismiss(); - } - }); - alert.setCancelable(false); - return alert.create(); - } - String userId = PGPHelper.getMainUserIdSafe(activity, secretKey); - - Log.d(Constants.TAG, "User id: '" + userId + "'"); - alert.setMessage(getString(R.string.passPhraseFor, userId)); - } - - LayoutInflater inflater = activity.getLayoutInflater(); - View view = inflater.inflate(R.layout.passphrase, null); - alert.setView(view); - - mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - String passPhrase = mPassphraseEditText.getText().toString(); - long keyId; - if (secretKey != null) { - try { - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() - .setProvider(PGPMain.BOUNCY_CASTLE_PROVIDER_NAME).build( - passPhrase.toCharArray()); - PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); - 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; - } - - // cache the new passphrase - Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); - PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase); - - sendMessageToHandler(MESSAGE_OKAY); - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - - return alert.create(); - } - - @Override - public void onActivityCreated(Bundle arg0) { - super.onActivityCreated(arg0); - - // request focus and open soft keyboard - mPassphraseEditText.requestFocus(); - getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - - mPassphraseEditText.setOnEditorActionListener(this); - } - - /** - * Associate the "done" button on the soft keyboard with the okay button in the view - */ - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (EditorInfo.IME_ACTION_DONE == actionId) { - AlertDialog dialog = ((AlertDialog) getDialog()); - Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); - - bt.performClick(); - return true; - } - return false; - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what) { - Message msg = Message.obtain(); - msg.what = what; - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java deleted file mode 100644 index 2c007e8e3..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/dialog/ProgressDialogFragment.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import android.app.Activity; -import android.app.Dialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnKeyListener; -import android.os.Bundle; -import android.support.v4.app.DialogFragment; -import android.view.KeyEvent; - -public class ProgressDialogFragment extends DialogFragment { - private static final String ARG_MESSAGE_ID = "message_id"; - private static final String ARG_STYLE = "style"; - - /** - * Creates new instance of this fragment - * - * @param id - * @return - */ - public static ProgressDialogFragment newInstance(int messageId, int style) { - ProgressDialogFragment frag = new ProgressDialogFragment(); - Bundle args = new Bundle(); - args.putInt(ARG_MESSAGE_ID, messageId); - args.putInt(ARG_STYLE, style); - - frag.setArguments(args); - return frag; - } - - /** - * Updates progress of dialog - * - * @param messageId - * @param progress - * @param max - */ - public void setProgress(int messageId, int progress, int max) { - setProgress(getString(messageId), progress, max); - } - - /** - * Updates progress of dialog - * - * @param messageId - * @param progress - * @param max - */ - public void setProgress(int progress, int max) { - ProgressDialog dialog = (ProgressDialog) getDialog(); - - dialog.setProgress(progress); - dialog.setMax(max); - } - - /** - * Updates progress of dialog - * - * @param messageId - * @param progress - * @param max - */ - public void setProgress(String message, int progress, int max) { - ProgressDialog dialog = (ProgressDialog) getDialog(); - - dialog.setMessage(message); - dialog.setProgress(progress); - dialog.setMax(max); - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - Activity activity = getActivity(); - - ProgressDialog dialog = new ProgressDialog(activity); - dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - dialog.setCancelable(false); - dialog.setCanceledOnTouchOutside(false); - - int messageId = getArguments().getInt(ARG_MESSAGE_ID); - int style = getArguments().getInt(ARG_STYLE); - - dialog.setMessage(getString(messageId)); - dialog.setProgressStyle(style); - - // Disable the back button - OnKeyListener keyListener = new OnKeyListener() { - - @Override - public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - - if (keyCode == KeyEvent.KEYCODE_BACK) { - return true; - } - return false; - } - - }; - dialog.setOnKeyListener(keyListener); - - return dialog; - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java b/APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java deleted file mode 100644 index c1bbd886a..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/dialog/SetPassphraseDialogFragment.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.dialog; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; -import org.thialfihar.android.apg.util.Log; - -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager.LayoutParams; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; -import android.widget.TextView.OnEditorActionListener; - -public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_TITLE = "title"; - - public static final int MESSAGE_OKAY = 1; - - public static final String MESSAGE_NEW_PASSPHRASE = "new_passphrase"; - - private Messenger mMessenger; - private EditText mPassphraseEditText; - private EditText mPassphraseAgainEditText; - - /** - * Creates new instance of this dialog fragment - * - * @param title - * title of dialog - * @param messenger - * to communicate back after setting the passphrase - * @return - */ - public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) { - SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment(); - Bundle args = new Bundle(); - args.putInt(ARG_TITLE, title); - args.putParcelable(ARG_MESSENGER, messenger); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - int title = getArguments().getInt(ARG_TITLE); - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - AlertDialog.Builder alert = new AlertDialog.Builder(activity); - - alert.setTitle(title); - alert.setMessage(R.string.enterPassPhraseTwice); - - LayoutInflater inflater = activity.getLayoutInflater(); - View view = inflater.inflate(R.layout.passphrase_repeat, null); - alert.setView(view); - - mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); - mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - String passPhrase1 = mPassphraseEditText.getText().toString(); - String passPhrase2 = mPassphraseAgainEditText.getText().toString(); - if (!passPhrase1.equals(passPhrase2)) { - Toast.makeText( - activity, - getString(R.string.errorMessage, - getString(R.string.passPhrasesDoNotMatch)), Toast.LENGTH_SHORT) - .show(); - return; - } - - if (passPhrase1.equals("")) { - Toast.makeText( - activity, - getString(R.string.errorMessage, - getString(R.string.passPhraseMustNotBeEmpty)), - Toast.LENGTH_SHORT).show(); - return; - } - - // return resulting data back to activity - Bundle data = new Bundle(); - data.putString(MESSAGE_NEW_PASSPHRASE, passPhrase1); - - sendMessageToHandler(MESSAGE_OKAY, data); - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - - return alert.create(); - } - - @Override - public void onActivityCreated(Bundle arg0) { - super.onActivityCreated(arg0); - - // request focus and open soft keyboard - mPassphraseEditText.requestFocus(); - getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - - mPassphraseAgainEditText.setOnEditorActionListener(this); - } - - /** - * Associate the "done" button on the soft keyboard with the okay button in the view - */ - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (EditorInfo.IME_ACTION_DONE == actionId) { - AlertDialog dialog = ((AlertDialog) getDialog()); - Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); - - bt.performClick(); - return true; - } - return false; - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what - * Message integer you want to send - */ - private void sendMessageToHandler(Integer what, Bundle data) { - Message msg = Message.obtain(); - msg.what = what; - if (data != null) { - msg.setData(data); - } - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java b/APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java deleted file mode 100644 index 8a516ad41..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/DashboardLayout.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright 2011 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.thialfihar.android.apg.ui.widget; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; - -/** - * Custom layout that arranges children in a grid-like manner, optimizing for even horizontal and - * vertical whitespace. - */ -public class DashboardLayout extends ViewGroup { - private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10; - - private int mMaxChildWidth = 0; - private int mMaxChildHeight = 0; - - public DashboardLayout(Context context) { - super(context, null); - } - - public DashboardLayout(Context context, AttributeSet attrs) { - super(context, attrs, 0); - } - - public DashboardLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - mMaxChildWidth = 0; - mMaxChildHeight = 0; - - // Measure once to find the maximum child size. - - int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST); - int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( - MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST); - - final int count = getChildCount(); - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - - mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth()); - mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight()); - } - - // Measure again for each child to be exactly the same size. - - childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth, MeasureSpec.EXACTLY); - childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight, MeasureSpec.EXACTLY); - - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - child.measure(childWidthMeasureSpec, childHeightMeasureSpec); - } - - setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec), - resolveSize(mMaxChildHeight, heightMeasureSpec)); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - int width = r - l; - int height = b - t; - - final int count = getChildCount(); - - // Calculate the number of visible children. - int visibleCount = 0; - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - ++visibleCount; - } - - if (visibleCount == 0) { - return; - } - - // Calculate what number of rows and columns will optimize for even horizontal and - // vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on. - int bestSpaceDifference = Integer.MAX_VALUE; - int spaceDifference; - - // Horizontal and vertical space between items - int hSpace = 0; - int vSpace = 0; - - int cols = 1; - int rows; - - while (true) { - rows = (visibleCount - 1) / cols + 1; - - hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); - vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); - - spaceDifference = Math.abs(vSpace - hSpace); - if (rows * cols != visibleCount) { - spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; - } else if (rows * mMaxChildHeight > height || cols * mMaxChildWidth > width) { - spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; - } - - if (spaceDifference < bestSpaceDifference) { - // Found a better whitespace squareness/ratio - bestSpaceDifference = spaceDifference; - - // If we found a better whitespace squareness and there's only 1 row, this is - // the best we can do. - if (rows == 1) { - break; - } - } else { - // This is a worse whitespace ratio, use the previous value of cols and exit. - --cols; - rows = (visibleCount - 1) / cols + 1; - hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); - vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); - break; - } - - ++cols; - } - - // Lay out children based on calculated best-fit number of rows and cols. - - // If we chose a layout that has negative horizontal or vertical space, force it to zero. - hSpace = Math.max(0, hSpace); - vSpace = Math.max(0, vSpace); - - // Re-use width/height variables to be child width/height. - width = (width - hSpace * (cols + 1)) / cols; - height = (height - vSpace * (rows + 1)) / rows; - - int left, top; - int col, row; - int visibleIndex = 0; - for (int i = 0; i < count; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - - row = visibleIndex / cols; - col = visibleIndex % cols; - - left = hSpace * (col + 1) + width * col; - top = vSpace * (row + 1) + height * row; - - child.layout(left, top, (hSpace == 0 && col == cols - 1) ? r : (left + width), - (vSpace == 0 && row == rows - 1) ? b : (top + height)); - ++visibleIndex; - } - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/Editor.java b/APG/src/org/thialfihar/android/apg/ui/widget/Editor.java deleted file mode 100644 index a7200fad0..000000000 --- a/APG/src/org/thialfihar/android/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.thialfihar.android.apg.ui.widget; - -public interface Editor { - public interface EditorListener { - public void onDeleted(Editor editor); - } - - public void setEditorListener(EditorListener listener); -} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java b/APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java deleted file mode 100644 index 01488909f..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/ExpandableListFragment.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.support.v4.app.Fragment; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnCreateContextMenuListener; -import android.view.ViewGroup; -import android.view.animation.AnimationUtils; -import android.widget.AdapterView; -import android.widget.ExpandableListAdapter; -import android.widget.ExpandableListView; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.ListAdapter; -import android.widget.ProgressBar; -import android.widget.TextView; - -/** - * @author Khoa Tran - * - * @see android.support.v4.app.ListFragment - * @see android.app.ExpandableListActivity - * - * ExpandableListFragment for Android < 3.0 - * - * from - * http://stackoverflow.com/questions/6051050/expandablelistfragment-with-loadermanager-for- - * compatibility-package - * - */ -public class ExpandableListFragment extends Fragment implements OnCreateContextMenuListener, - ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener, - ExpandableListView.OnGroupExpandListener { - - static final int INTERNAL_EMPTY_ID = 0x00ff0001; - static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; - static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; - - final private Handler mHandler = new Handler(); - - final private Runnable mRequestFocus = new Runnable() { - public void run() { - mExpandableList.focusableViewAvailable(mExpandableList); - } - }; - - final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { - public void onItemClick(AdapterView parent, View v, int position, long id) { - onListItemClick((ExpandableListView) parent, v, position, id); - } - }; - - ExpandableListAdapter mAdapter; - ExpandableListView mExpandableList; - boolean mFinishedStart = false; - View mEmptyView; - TextView mStandardEmptyView; - View mProgressContainer; - View mExpandableListContainer; - CharSequence mEmptyText; - boolean mExpandableListShown; - - public ExpandableListFragment() { - } - - /** - * Provide default implementation to return a simple list view. Subclasses can override to - * replace with their own layout. If doing so, the returned view hierarchy must have a - * ListView whose id is {@link android.R.id#list android.R.id.list} and can optionally have a - * sibling view id {@link android.R.id#empty android.R.id.empty} that is to be shown when the - * list is empty. - * - *

- * If you are overriding this method with your own custom content, consider including the - * standard layout {@link android.R.layout#list_content} in your layout file, so that you - * continue to retain all of the standard behavior of ListFragment. In particular, this is - * currently the only way to have the built-in indeterminant progress state be shown. - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final Context context = getActivity(); - - FrameLayout root = new FrameLayout(context); - - // ------------------------------------------------------------------ - - LinearLayout pframe = new LinearLayout(context); - pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); - pframe.setOrientation(LinearLayout.VERTICAL); - pframe.setVisibility(View.GONE); - pframe.setGravity(Gravity.CENTER); - - ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); - pframe.addView(progress, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - - root.addView(pframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - FrameLayout lframe = new FrameLayout(context); - lframe.setId(INTERNAL_LIST_CONTAINER_ID); - - TextView tv = new TextView(getActivity()); - tv.setId(INTERNAL_EMPTY_ID); - tv.setGravity(Gravity.CENTER); - lframe.addView(tv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - ExpandableListView lv = new ExpandableListView(getActivity()); - lv.setId(android.R.id.list); - lv.setDrawSelectorOnTop(false); - lframe.addView(lv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - root.addView(lframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - // ------------------------------------------------------------------ - - root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT)); - - return root; - } - - /** - * Attach to list view once the view hierarchy has been created. - */ - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - ensureList(); - } - - /** - * Detach from list view. - */ - @Override - public void onDestroyView() { - mHandler.removeCallbacks(mRequestFocus); - mExpandableList = null; - mExpandableListShown = false; - mEmptyView = mProgressContainer = mExpandableListContainer = null; - mStandardEmptyView = null; - super.onDestroyView(); - } - - /** - * This method will be called when an item in the list is selected. Subclasses should override. - * Subclasses can call getListView().getItemAtPosition(position) if they need to access the data - * associated with the selected item. - * - * @param l - * The ListView where the click happened - * @param v - * The view that was clicked within the ListView - * @param position - * The position of the view in the list - * @param id - * The row id of the item that was clicked - */ - public void onListItemClick(ExpandableListView l, View v, int position, long id) { - } - - /** - * Provide the cursor for the list view. - */ - public void setListAdapter(ExpandableListAdapter adapter) { - boolean hadAdapter = mAdapter != null; - mAdapter = adapter; - if (mExpandableList != null) { - mExpandableList.setAdapter(adapter); - if (!mExpandableListShown && !hadAdapter) { - // The list was hidden, and previously didn't have an - // adapter. It is now time to show it. - setListShown(true, getView().getWindowToken() != null); - } - } - } - - /** - * Set the currently selected list item to the specified position with the adapter's data - * - * @param position - */ - public void setSelection(int position) { - ensureList(); - mExpandableList.setSelection(position); - } - - /** - * Get the position of the currently selected list item. - */ - public int getSelectedItemPosition() { - ensureList(); - return mExpandableList.getSelectedItemPosition(); - } - - /** - * Get the cursor row ID of the currently selected list item. - */ - public long getSelectedItemId() { - ensureList(); - return mExpandableList.getSelectedItemId(); - } - - /** - * Get the activity's list view widget. - */ - public ExpandableListView getListView() { - ensureList(); - return mExpandableList; - } - - /** - * The default content for a ListFragment has a TextView that can be shown when the list is - * empty. If you would like to have it shown, call this method to supply the text it should use. - */ - public void setEmptyText(CharSequence text) { - ensureList(); - if (mStandardEmptyView == null) { - throw new IllegalStateException("Can't be used with a custom content view"); - } - mStandardEmptyView.setText(text); - if (mEmptyText == null) { - mExpandableList.setEmptyView(mStandardEmptyView); - } - mEmptyText = text; - } - - /** - * Control whether the list is being displayed. You can make it not displayed if you are waiting - * for the initial data to show in it. During this time an indeterminant progress indicator will - * be shown instead. - * - *

- * Applications do not normally need to use this themselves. The default behavior of - * ListFragment is to start with the list not being shown, only showing it once an adapter is - * given with {@link #setListAdapter(ListAdapter)}. If the list at that point had not been - * shown, when it does get shown it will be do without the user ever seeing the hidden state. - * - * @param shown - * If true, the list view is shown; if false, the progress indicator. The initial - * value is true. - */ - public void setListShown(boolean shown) { - setListShown(shown, true); - } - - /** - * Like {@link #setListShown(boolean)}, but no animation is used when transitioning from the - * previous state. - */ - public void setListShownNoAnimation(boolean shown) { - setListShown(shown, false); - } - - /** - * Control whether the list is being displayed. You can make it not displayed if you are waiting - * for the initial data to show in it. During this time an indeterminant progress indicator will - * be shown instead. - * - * @param shown - * If true, the list view is shown; if false, the progress indicator. The initial - * value is true. - * @param animate - * If true, an animation will be used to transition to the new state. - */ - private void setListShown(boolean shown, boolean animate) { - ensureList(); - if (mProgressContainer == null) { - throw new IllegalStateException("Can't be used with a custom content view"); - } - if (mExpandableListShown == shown) { - return; - } - mExpandableListShown = shown; - if (shown) { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_out)); - mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_in)); - } else { - mProgressContainer.clearAnimation(); - mExpandableListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.GONE); - mExpandableListContainer.setVisibility(View.VISIBLE); - } else { - if (animate) { - mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_in)); - mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), - android.R.anim.fade_out)); - } else { - mProgressContainer.clearAnimation(); - mExpandableListContainer.clearAnimation(); - } - mProgressContainer.setVisibility(View.VISIBLE); - mExpandableListContainer.setVisibility(View.GONE); - } - } - - /** - * Get the ListAdapter associated with this activity's ListView. - */ - public ExpandableListAdapter getListAdapter() { - return mAdapter; - } - - private void ensureList() { - if (mExpandableList != null) { - return; - } - View root = getView(); - if (root == null) { - throw new IllegalStateException("Content view not yet created"); - } - if (root instanceof ExpandableListView) { - mExpandableList = (ExpandableListView) root; - } else { - mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID); - if (mStandardEmptyView == null) { - mEmptyView = root.findViewById(android.R.id.empty); - } else { - mStandardEmptyView.setVisibility(View.GONE); - } - mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); - mExpandableListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); - View rawExpandableListView = root.findViewById(android.R.id.list); - if (!(rawExpandableListView instanceof ExpandableListView)) { - if (rawExpandableListView == null) { - throw new RuntimeException( - "Your content must have a ListView whose id attribute is " - + "'android.R.id.list'"); - } - throw new RuntimeException( - "Content has view with id attribute 'android.R.id.list' " - + "that is not a ListView class"); - } - mExpandableList = (ExpandableListView) rawExpandableListView; - if (mEmptyView != null) { - mExpandableList.setEmptyView(mEmptyView); - } else if (mEmptyText != null) { - mStandardEmptyView.setText(mEmptyText); - mExpandableList.setEmptyView(mStandardEmptyView); - } - } - mExpandableListShown = true; - mExpandableList.setOnItemClickListener(mOnClickListener); - if (mAdapter != null) { - ExpandableListAdapter adapter = mAdapter; - mAdapter = null; - setListAdapter(adapter); - } else { - // We are starting without an adapter, so assume we won't - // have our data right away and start with the progress indicator. - if (mProgressContainer != null) { - setListShown(false, false); - } - } - mHandler.post(mRequestFocus); - } - - /** - * Override this to populate the context menu when an item is long pressed. menuInfo will - * contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} whose - * packedPosition is a packed position that should be used with - * {@link ExpandableListView#getPackedPositionType(long)} and the other similar methods. - *

- * {@inheritDoc} - */ - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - } - - /** - * Override this for receiving callbacks when a child has been clicked. - *

- * {@inheritDoc} - */ - public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, - int childPosition, long id) { - return false; - } - - /** - * Override this for receiving callbacks when a group has been collapsed. - */ - public void onGroupCollapse(int groupPosition) { - } - - /** - * Override this for receiving callbacks when a group has been expanded. - */ - public void onGroupExpand(int groupPosition) { - } - - // /** - // * Ensures the expandable list view has been created before Activity restores all - // * of the view states. - // * - // *@see Activity#onRestoreInstanceState(Bundle) - // */ - // @Override - // protected void onRestoreInstanceState(Bundle state) { - // ensureList(); - // super.onRestoreInstanceState(state); - // } - - /** - * Updates the screen state (current list and other views) when the content changes. - * - * @see Activity#onContentChanged() - */ - - public void onContentChanged() { - // super.onContentChanged(); - View emptyView = getView().findViewById(android.R.id.empty); - mExpandableList = (ExpandableListView) getView().findViewById(android.R.id.list); - if (mExpandableList == null) { - throw new RuntimeException( - "Your content must have a ExpandableListView whose id attribute is " - + "'android.R.id.list'"); - } - if (emptyView != null) { - mExpandableList.setEmptyView(emptyView); - } - mExpandableList.setOnChildClickListener(this); - mExpandableList.setOnGroupExpandListener(this); - mExpandableList.setOnGroupCollapseListener(this); - - if (mFinishedStart) { - setListAdapter(mAdapter); - } - mFinishedStart = true; - } - - /** - * Get the activity's expandable list view widget. This can be used to get the selection, set - * the selection, and many other useful functions. - * - * @see ExpandableListView - */ - public ExpandableListView getExpandableListView() { - ensureList(); - return mExpandableList; - } - - /** - * Get the ExpandableListAdapter associated with this activity's ExpandableListView. - */ - public ExpandableListAdapter getExpandableListAdapter() { - return mAdapter; - } - - /** - * Gets the ID of the currently selected group or child. - * - * @return The ID of the currently selected group or child. - */ - public long getSelectedId() { - return mExpandableList.getSelectedId(); - } - - /** - * Gets the position (in packed position representation) of the currently selected group or - * child. Use {@link ExpandableListView#getPackedPositionType}, - * {@link ExpandableListView#getPackedPositionGroup}, and - * {@link ExpandableListView#getPackedPositionChild} to unpack the returned packed position. - * - * @return A packed position representation containing the currently selected group or child's - * position and type. - */ - public long getSelectedPosition() { - return mExpandableList.getSelectedPosition(); - } - - /** - * Sets the selection to the specified child. If the child is in a collapsed group, the group - * will only be expanded and child subsequently selected if shouldExpandGroup is set to true, - * otherwise the method will return false. - * - * @param groupPosition - * The position of the group that contains the child. - * @param childPosition - * The position of the child within the group. - * @param shouldExpandGroup - * Whether the child's group should be expanded if it is collapsed. - * @return Whether the selection was successfully set on the child. - */ - public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) { - return mExpandableList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup); - } - - /** - * Sets the selection to the specified group. - * - * @param groupPosition - * The position of the group that should be selected. - */ - public void setSelectedGroup(int groupPosition) { - mExpandableList.setSelectedGroup(groupPosition); - } -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/ImportKeysListLoader.java b/APG/src/org/thialfihar/android/apg/ui/widget/ImportKeysListLoader.java deleted file mode 100644 index 6a2947f94..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/ImportKeysListLoader.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2012-2013 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.spongycastle.openpgp.PGPKeyRing; -import org.spongycastle.openpgp.PGPObjectFactory; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPPublicKeyRingCollection; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.spongycastle.openpgp.PGPSecretKeyRingCollection; -import org.spongycastle.openpgp.PGPUtil; -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.util.InputData; -import org.thialfihar.android.apg.util.Log; -import org.thialfihar.android.apg.util.PositionAwareInputStream; - -import android.content.Context; -import android.support.v4.content.AsyncTaskLoader; - -/** - * A custom Loader to search for bad adware apps, based on - * https://github.com/brosmike/AirPush-Detector. Daniel Bjorge licensed it under Apachev2 after - * asking him by mail. - */ -public class ImportKeysListLoader extends AsyncTaskLoader>> { - public static final String MAP_ATTR_USER_ID = "user_id"; - public static final String MAP_ATTR_FINGERPINT = "fingerprint"; - - ArrayList> data = new ArrayList>(); - - Context mContext; - List mItems; - - byte[] mKeyringBytes; - String mImportFilename; - - public ImportKeysListLoader(Context context, byte[] keyringBytes, String importFilename) { - super(context); - this.mContext = context; - this.mKeyringBytes = keyringBytes; - this.mImportFilename = importFilename; - } - - @Override - public List> loadInBackground() { - InputData inputData = null; - if (mKeyringBytes != null) { - inputData = new InputData(new ByteArrayInputStream(mKeyringBytes), mKeyringBytes.length); - } else { - try { - inputData = new InputData(new FileInputStream(mImportFilename), - mImportFilename.length()); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "Failed to init FileInputStream!", e); - } - } - - generateListOfKeyrings(inputData); - - return data; - } - - @Override - protected void onReset() { - super.onReset(); - - // Ensure the loader is stopped - onStopLoading(); - } - - @Override - protected void onStartLoading() { - forceLoad(); - } - - @Override - protected void onStopLoading() { - cancelLoad(); - } - - @Override - public void deliverResult(List> data) { - super.deliverResult(data); - } - - /** - * Similar to PGPMain.importKeyRings - * - * @param keyringBytes - * @return - */ - private void generateListOfKeyrings(InputData inputData) { - PositionAwareInputStream progressIn = new PositionAwareInputStream( - inputData.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); - try { - - // read all available blocks... (asc files can contain many blocks with BEGIN END) - while (bufferedInput.available() > 0) { - InputStream in = PGPUtil.getDecoderStream(bufferedInput); - PGPObjectFactory objectFactory = new PGPObjectFactory(in); - - // go through all objects in this block - Object obj; - while ((obj = objectFactory.nextObject()) != null) { - Log.d(Constants.TAG, "Found class: " + obj.getClass()); - - if (obj instanceof PGPKeyRing) { - PGPKeyRing newKeyring = (PGPKeyRing) obj; - addToData(newKeyring); - } else { - Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); - } - } - } - } catch (Exception e) { - Log.e(Constants.TAG, "Exception on parsing key file!", e); - } - } - - private void addToData(PGPKeyRing keyring) { - String userId = PGPHelper.getMainUserId(keyring.getPublicKey()); - - if (keyring instanceof PGPSecretKeyRing) { - userId = mContext.getString(R.string.secretKeyring) + " " + userId; - } - - String fingerprint = PGPHelper.convertFingerprintToHex(keyring.getPublicKey() - .getFingerprint()); - - Map attrs = new HashMap(); - attrs.put(MAP_ATTR_USER_ID, userId); - attrs.put(MAP_ATTR_FINGERPINT, mContext.getString(R.string.fingerprint) + "\n" - + fingerprint); - data.add(attrs); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java b/APG/src/org/thialfihar/android/apg/ui/widget/IntegerListPreference.java deleted file mode 100644 index 1d5f9e3d7..000000000 --- a/APG/src/org/thialfihar/android/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.thialfihar.android.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/APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java deleted file mode 100644 index f8adc3b72..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java +++ /dev/null @@ -1,236 +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.thialfihar.android.apg.ui.widget; - -import org.spongycastle.openpgp.PGPPublicKey; -import org.spongycastle.openpgp.PGPSecretKey; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.util.Choice; -import org.thialfihar.android.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, int usage) { - mKey = key; - - mIsMasterKey = isMasterKey; - if (mIsMasterKey) { - mDeleteButton.setVisibility(View.INVISIBLE); - } - - mAlgorithm.setText(PGPHelper.getAlgorithmInfo(key)); - String keyId1Str = PGPHelper.getSmallFingerPrint(key.getKeyID()); - String keyId2Str = PGPHelper.getSmallFingerPrint(key.getKeyID() >> 32); - mKeyId.setText(keyId1Str + " " + keyId2Str); - - Vector choices = new Vector(); - boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); - boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA); - if (!isElGamalKey) { - choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString( - R.string.choice_signOnly))); - } - if (!mIsMasterKey && !isDSAKey) { - choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString( - R.string.choice_encryptOnly))); - } - if (!isElGamalKey && !isDSAKey) { - 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); - - // Set value in choice dropdown to key - int selectId = 0; - if (PGPHelper.isEncryptionKey(key)) { - if (PGPHelper.isSigningKey(key)) { - selectId = Id.choice.usage.sign_and_encrypt; - } else { - selectId = Id.choice.usage.encrypt_only; - } - } else { - // set usage if it is predefined - if (usage != -1) { - selectId = usage; - } 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(PGPHelper.getCreationDate(key)); - mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime())); - cal = new GregorianCalendar(); - Date date = PGPHelper.getExpiryDate(key); - if (date == null) { - setExpiryDate(null); - } else { - cal.setTime(PGPHelper.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/APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java b/APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java deleted file mode 100644 index 194142b28..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/KeyListAdapter.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2012-2013 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.ui.widget; - -import org.thialfihar.android.apg.Constants; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ApgContract.Keys; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; -import org.thialfihar.android.apg.util.Log; - -import android.content.Context; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.MergeCursor; -import android.net.Uri; -import android.provider.BaseColumns; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CursorTreeAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -public class KeyListAdapter extends CursorTreeAdapter { - private Context mContext; - private LayoutInflater mInflater; - - protected int mKeyType; - - private static final int CHILD_KEY = 0; - private static final int CHILD_USER_ID = 1; - private static final int CHILD_FINGERPRINT = 2; - - public KeyListAdapter(Context context, Cursor groupCursor, int keyType) { - super(groupCursor, context); - mContext = context; - mInflater = LayoutInflater.from(context); - mKeyType = keyType; - } - - /** - * Inflate new view for group items - */ - @Override - public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_group_item, null); - } - - /** - * Binds TextViews from group view to results from database group cursor. - */ - @Override - protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { - int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID); - - TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); - mainUserId.setText(R.string.unknownUserId); - TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); - mainUserIdRest.setText(""); - - String userId = cursor.getString(userIdIndex); - if (userId != null) { - String[] userIdSplit = OtherHelper.splitUserId(userId); - - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - } - mainUserId.setText(userIdSplit[0]); - } - - if (mainUserId.getText().length() == 0) { - mainUserId.setText(R.string.unknownUserId); - } - - if (mainUserIdRest.getText().length() == 0) { - mainUserIdRest.setVisibility(View.GONE); - } - } - - /** - * Inflate new view for child items - */ - @Override - public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) { - return mInflater.inflate(R.layout.key_list_child_item, null); - } - - /** - * Bind TextViews from view of childs based on query results - */ - @Override - protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { - LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout); - LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout); - - // first entry is fingerprint - if (cursor.getPosition() == 0) { - // show only userId layout - keyLayout.setVisibility(View.GONE); - userIdLayout.setVisibility(View.VISIBLE); - - String fingerprint = PGPHelper.getFingerPrint(context, - cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID))); - fingerprint = fingerprint.replace(" ", "\n"); - - TextView userId = (TextView) view.findViewById(R.id.userId); - if (userId == null) { - Log.d(Constants.TAG, "userId is null!"); - } - userId.setText(context.getString(R.string.fingerprint) + "\n" + fingerprint); - } else { - // differentiate between keys and userIds in MergeCursor - if (cursor.getColumnIndex(Keys.KEY_ID) != -1) { - keyLayout.setVisibility(View.VISIBLE); - userIdLayout.setVisibility(View.GONE); - - String keyIdStr = PGPHelper.getSmallFingerPrint(cursor.getLong(cursor - .getColumnIndex(Keys.KEY_ID))); - String algorithmStr = PGPHelper.getAlgorithmInfo( - cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)), - cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE))); - - TextView keyId = (TextView) view.findViewById(R.id.keyId); - keyId.setText(keyIdStr); - - TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); - keyDetails.setText("(" + algorithmStr + ")"); - - ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) { - masterKeyIcon.setVisibility(View.INVISIBLE); - } else { - masterKeyIcon.setVisibility(View.VISIBLE); - } - - ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) { - encryptIcon.setVisibility(View.GONE); - } else { - encryptIcon.setVisibility(View.VISIBLE); - } - - ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); - if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { - signIcon.setVisibility(View.GONE); - } else { - signIcon.setVisibility(View.VISIBLE); - } - } else { - keyLayout.setVisibility(View.GONE); - userIdLayout.setVisibility(View.VISIBLE); - - String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); - - TextView userId = (TextView) view.findViewById(R.id.userId); - userId.setText(userIdStr); - } - } - } - - /** - * Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are - * merged together and build the child cursor - */ - @Override - protected Cursor getChildrenCursor(Cursor groupCursor) { - final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID)); - - Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT); - Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY); - Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID); - - MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor, - userIdCursor }); - Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor)); - - return mergeCursor; - } - - /** - * This builds a cursor for a specific type of children - * - * @param keyRingRowId - * foreign row id of the keyRing - * @param type - * @return - */ - private Cursor getChildCursor(long keyRingRowId, int type) { - Uri uri = null; - String[] projection = null; - String sortOrder = null; - String selection = null; - - switch (type) { - case CHILD_FINGERPRINT: - projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, - Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; - sortOrder = Keys.RANK + " ASC"; - - // use only master key for fingerprint - selection = Keys.IS_MASTER_KEY + " = 1 "; - - if (mKeyType == Id.type.public_key) { - uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); - } else { - uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); - } - break; - - case CHILD_KEY: - projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, - Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; - sortOrder = Keys.RANK + " ASC"; - - if (mKeyType == Id.type.public_key) { - uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); - } else { - uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); - } - - break; - - case CHILD_USER_ID: - projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, }; - sortOrder = UserIds.RANK + " ASC"; - - // not the main user id - selection = UserIds.RANK + " > 0 "; - - if (mKeyType == Id.type.public_key) { - uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId)); - } else { - uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId)); - } - - break; - - default: - return null; - - } - - return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder); - } - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java b/APG/src/org/thialfihar/android/apg/ui/widget/KeyServerEditor.java deleted file mode 100644 index cbea7f031..000000000 --- a/APG/src/org/thialfihar/android/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.thialfihar.android.apg.ui.widget; - -import org.thialfihar.android.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/APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java b/APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java deleted file mode 100644 index c7e8087cb..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/SectionView.java +++ /dev/null @@ -1,327 +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.thialfihar.android.apg.ui.widget; - -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.helper.PGPConversionHelper; -import org.thialfihar.android.apg.service.ApgIntentServiceHandler; -import org.thialfihar.android.apg.service.ApgIntentService; -import org.thialfihar.android.apg.service.PassphraseCacheService; -import org.thialfihar.android.apg.ui.dialog.ProgressDialogFragment; -import org.thialfihar.android.apg.ui.widget.Editor.EditorListener; -import org.thialfihar.android.apg.util.Choice; -import org.thialfihar.android.apg.R; - -import com.actionbarsherlock.app.SherlockFragmentActivity; - -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -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 java.util.Iterator; -import java.util.Vector; - -public class SectionView extends LinearLayout implements OnClickListener, EditorListener { - private LayoutInflater mInflater; - private View mAdd; - private ViewGroup mEditors; - private TextView mTitle; - private int mType = 0; - - private Choice mNewKeyAlgorithmChoice; - private int mNewKeySize; - - private SherlockFragmentActivity mActivity; - - private ProgressDialogFragment mGeneratingDialog; - - public SectionView(Context context) { - super(context); - mActivity = (SherlockFragmentActivity) context; - } - - public SectionView(Context context, AttributeSet attrs) { - super(context, attrs); - mActivity = (SherlockFragmentActivity) context; - } - - 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); - - boolean wouldBeMasterKey = (mEditors.getChildCount() == 0); - - final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_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.create_key_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, Vector usages) { - if (mType != Id.type.key) { - return; - } - - mEditors.removeAllViews(); - - // go through all keys and set view based on them - for (int i = 0; i < list.size(); i++) { - KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, - false); - view.setEditorListener(this); - boolean isMasterKey = (mEditors.getChildCount() == 0); - view.setValue(list.get(i), isMasterKey, usages.get(i)); - mEditors.addView(view); - } - - this.updateEditorsVisible(); - } - - private void createKey() { - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(mActivity, ApgIntentService.class); - - intent.putExtra(ApgIntentService.EXTRA_ACTION, ApgIntentService.ACTION_GENERATE_KEY); - - // fill values for this action - Bundle data = new Bundle(); - - String passPhrase; - if (mEditors.getChildCount() > 0) { - PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue(); - passPhrase = PassphraseCacheService - .getCachedPassphrase(mActivity, masterKey.getKeyID()); - - data.putByteArray(ApgIntentService.GENERATE_KEY_MASTER_KEY, - PGPConversionHelper.PGPSecretKeyToBytes(masterKey)); - } else { - passPhrase = ""; - } - data.putString(ApgIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase); - data.putInt(ApgIntentService.GENERATE_KEY_ALGORITHM, mNewKeyAlgorithmChoice.getId()); - data.putInt(ApgIntentService.GENERATE_KEY_KEY_SIZE, mNewKeySize); - - intent.putExtra(ApgIntentService.EXTRA_DATA, data); - - // show progress dialog - mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating, - ProgressDialog.STYLE_SPINNER); - - // Message is received after generating is done in ApgService - ApgIntentServiceHandler saveHandler = new ApgIntentServiceHandler(mActivity, mGeneratingDialog) { - public void handleMessage(Message message) { - // handle messages by standard ApgHandler first - super.handleMessage(message); - - if (message.arg1 == ApgIntentServiceHandler.MESSAGE_OKAY) { - // get new key from data bundle returned from service - Bundle data = message.getData(); - PGPSecretKeyRing newKeyRing = (PGPSecretKeyRing) PGPConversionHelper - .BytesToPGPKeyRing(data.getByteArray(ApgIntentService.RESULT_NEW_KEY)); - - boolean isMasterKey = (mEditors.getChildCount() == 0); - - // take only the key from this ring - PGPSecretKey newKey = null; - @SuppressWarnings("unchecked") - Iterator it = newKeyRing.getSecretKeys(); - - if (isMasterKey) { - newKey = it.next(); - } else { - // first one is the master key - it.next(); - newKey = it.next(); - } - - // add view with new key - KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, - mEditors, false); - view.setEditorListener(SectionView.this); - view.setValue(newKey, isMasterKey, -1); - mEditors.addView(view); - SectionView.this.updateEditorsVisible(); - } - }; - }; - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(ApgIntentService.EXTRA_MESSENGER, messenger); - - mGeneratingDialog.show(mActivity.getSupportFragmentManager(), "dialog"); - - // start service with intent - mActivity.startService(intent); - } -} diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java b/APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java deleted file mode 100644 index 0f6bd5f11..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/SelectKeyCursorAdapter.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * 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.thialfihar.android.apg.ui.widget; - -import org.thialfihar.android.apg.Id; -import org.thialfihar.android.apg.R; -import org.thialfihar.android.apg.helper.OtherHelper; -import org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.provider.ApgContract.KeyRings; -import org.thialfihar.android.apg.provider.ApgContract.UserIds; - -import android.content.Context; -import android.database.Cursor; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ListView; -import android.widget.TextView; - -public class SelectKeyCursorAdapter extends CursorAdapter { - - protected int mKeyType; - - private LayoutInflater mInflater; - private ListView mListView; - - public final static String PROJECTION_ROW_AVAILABLE = "available"; - public final static String PROJECTION_ROW_VALID = "valid"; - - @SuppressWarnings("deprecation") - public SelectKeyCursorAdapter(Context context, ListView listView, Cursor c, int keyType) { - super(context, c); - - mInflater = LayoutInflater.from(context); - mListView = listView; - mKeyType = keyType; - } - - public String getUserId(int position) { - mCursor.moveToPosition(position); - return mCursor.getString(mCursor.getColumnIndex(UserIds.USER_ID)); - } - - public long getMasterKeyId(int position) { - mCursor.moveToPosition(position); - return mCursor.getLong(mCursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - boolean valid = cursor.getInt(cursor - .getColumnIndex(PROJECTION_ROW_VALID)) > 0; - - 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 = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); - if (userId != null) { - String[] userIdSplit = OtherHelper.splitUserId(userId); - - if (userIdSplit[1] != null) { - mainUserIdRest.setText(userIdSplit[1]); - } - mainUserId.setText(userIdSplit[0]); - } - - long masterKeyId = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); - keyId.setText(PGPHelper.getSmallFingerPrint(masterKeyId)); - - if (mainUserIdRest.getText().length() == 0) { - mainUserIdRest.setVisibility(View.GONE); - } - - if (valid) { - if (mKeyType == Id.type.public_key) { - status.setText(R.string.canEncrypt); - } else { - status.setText(R.string.canSign); - } - } else { - if (cursor.getInt(cursor - .getColumnIndex(PROJECTION_ROW_AVAILABLE)) > 0) { - // has some CAN_ENCRYPT keys, but col(ROW_VALID) = 0, so must be revoked or - // expired - status.setText(R.string.expired); - } else { - status.setText(R.string.noKey); - } - } - - CheckBox selected = (CheckBox) view.findViewById(R.id.selected); - if (mKeyType == Id.type.public_key) { - selected.setVisibility(View.VISIBLE); - - if (!valid) { - mListView.setItemChecked(cursor.getPosition(), false); - } - - selected.setChecked(mListView.isItemChecked(cursor.getPosition())); - selected.setEnabled(valid); - } else { - selected.setVisibility(View.GONE); - } - - status.setText(status.getText() + " "); - - view.setEnabled(valid); - mainUserId.setEnabled(valid); - mainUserIdRest.setEnabled(valid); - keyId.setEnabled(valid); - status.setEnabled(valid); - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.select_key_item, null); - } - -} \ No newline at end of file diff --git a/APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java b/APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java deleted file mode 100644 index 2495a4f9d..000000000 --- a/APG/src/org/thialfihar/android/apg/ui/widget/UserIdEditor.java +++ /dev/null @@ -1,196 +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.thialfihar.android.apg.ui.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.thialfihar.android.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; - - // see http://www.regular-expressions.info/email.html - // RFC 2822 if we omit the syntax using double quotes and square brackets - // android.util.Patterns.EMAIL_ADDRESS is only available as of Android 2.2+ - private static final Pattern EMAIL_PATTERN = Pattern - .compile( - "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", - 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/APG/src/org/thialfihar/android/apg/util/Choice.java b/APG/src/org/thialfihar/android/apg/util/Choice.java deleted file mode 100644 index 94cc58f55..000000000 --- a/APG/src/org/thialfihar/android/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.thialfihar.android.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/APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java b/APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java deleted file mode 100644 index 492e2e3d0..000000000 --- a/APG/src/org/thialfihar/android/apg/util/HkpKeyServer.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.util; - -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.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 org.thialfihar.android.apg.helper.PGPHelper; -import org.thialfihar.android.apg.helper.PGPMain; - -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); - } - - 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"); - } - - @Override - public ArrayList search(String query) throws QueryException, TooManyResponses, - InsufficientQuery { - ArrayList results = new ArrayList(); - - 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 = PGPHelper.keyFromHex(matcher.group(3)); - info.fingerPrint = PGPHelper.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 ArrayList(); - 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" + PGPHelper.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 = PGPMain.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 - public 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/APG/src/org/thialfihar/android/apg/util/InputData.java b/APG/src/org/thialfihar/android/apg/util/InputData.java deleted file mode 100644 index 6b357e6de..000000000 --- a/APG/src/org/thialfihar/android/apg/util/InputData.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.util; - -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/APG/src/org/thialfihar/android/apg/util/IterableIterator.java b/APG/src/org/thialfihar/android/apg/util/IterableIterator.java deleted file mode 100644 index 1071cc81c..000000000 --- a/APG/src/org/thialfihar/android/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.thialfihar.android.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/APG/src/org/thialfihar/android/apg/util/KeyServer.java b/APG/src/org/thialfihar/android/apg/util/KeyServer.java deleted file mode 100644 index 97c6c7636..000000000 --- a/APG/src/org/thialfihar/android/apg/util/KeyServer.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2011 Senecaso - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.util; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import android.os.Parcel; -import android.os.Parcelable; - -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, Parcelable { - private static final long serialVersionUID = -7797972113284992662L; - public ArrayList userIds; - public String revoked; - public Date date; - public String fingerPrint; - public long keyId; - public int size; - public String algorithm; - - public KeyInfo() { - userIds = new ArrayList(); - } - - public KeyInfo(Parcel in) { - this(); - - in.readStringList(this.userIds); - this.revoked = in.readString(); - this.date = (Date) in.readSerializable(); - this.fingerPrint = in.readString(); - this.keyId = in.readLong(); - this.size = in.readInt(); - this.algorithm = in.readString(); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeStringList(userIds); - dest.writeString(revoked); - dest.writeSerializable(date); - dest.writeString(fingerPrint); - dest.writeLong(keyId); - dest.writeInt(size); - dest.writeString(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/APG/src/org/thialfihar/android/apg/util/Log.java b/APG/src/org/thialfihar/android/apg/util/Log.java deleted file mode 100644 index c2e3cc7a5..000000000 --- a/APG/src/org/thialfihar/android/apg/util/Log.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2012 Dominik Schürmann - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.util; - -import org.thialfihar.android.apg.Constants; - -/** - * Wraps Android Logging to enable or disable debug output using Constants - * - */ -public final class Log { - - public static void v(String tag, String msg) { - if (Constants.DEBUG) { - android.util.Log.v(tag, msg); - } - } - - public static void v(String tag, String msg, Throwable tr) { - if (Constants.DEBUG) { - android.util.Log.v(tag, msg, tr); - } - } - - public static void d(String tag, String msg) { - if (Constants.DEBUG) { - android.util.Log.d(tag, msg); - } - } - - public static void d(String tag, String msg, Throwable tr) { - if (Constants.DEBUG) { - android.util.Log.d(tag, msg, tr); - } - } - - public static void i(String tag, String msg) { - if (Constants.DEBUG) { - android.util.Log.i(tag, msg); - } - } - - public static void i(String tag, String msg, Throwable tr) { - if (Constants.DEBUG) { - android.util.Log.i(tag, msg, tr); - } - } - - public static void w(String tag, String msg) { - android.util.Log.w(tag, msg); - } - - public static void w(String tag, String msg, Throwable tr) { - android.util.Log.w(tag, msg, tr); - } - - public static void w(String tag, Throwable tr) { - android.util.Log.w(tag, tr); - } - - public static void e(String tag, String msg) { - android.util.Log.e(tag, msg); - } - - public static void e(String tag, String msg, Throwable tr) { - android.util.Log.e(tag, msg, tr); - } - -} diff --git a/APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java b/APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java deleted file mode 100644 index 7850e2513..000000000 --- a/APG/src/org/thialfihar/android/apg/util/PositionAwareInputStream.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.thialfihar.android.apg.util; - -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/APG/src/org/thialfihar/android/apg/util/Primes.java b/APG/src/org/thialfihar/android/apg/util/Primes.java deleted file mode 100644 index bc52927e8..000000000 --- a/APG/src/org/thialfihar/android/apg/util/Primes.java +++ /dev/null @@ -1,188 +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.thialfihar.android.apg.util; - -import java.math.BigInteger; - -/** - * Primes for ElGamal - */ -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/APG/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java b/APG/src/org/thialfihar/android/apg/util/ProgressDialogUpdater.java deleted file mode 100644 index 1f76cb071..000000000 --- a/APG/src/org/thialfihar/android/apg/util/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.thialfihar.android.apg.util; - -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/OpenPGP-Keychain-API-Demo/.gitignore b/OpenPGP-Keychain-API-Demo/.gitignore new file mode 100644 index 000000000..2e423e1a3 --- /dev/null +++ b/OpenPGP-Keychain-API-Demo/.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/OpenPGP-Keychain-API-Demo/AndroidManifest.xml b/OpenPGP-Keychain-API-Demo/AndroidManifest.xml new file mode 100644 index 000000000..9d735860c --- /dev/null +++ b/OpenPGP-Keychain-API-Demo/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain-API-Demo/build.xml b/OpenPGP-Keychain-API-Demo/build.xml new file mode 100644 index 000000000..e72865c84 --- /dev/null +++ b/OpenPGP-Keychain-API-Demo/build.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenPGP-Keychain-API-Demo/proguard-project.txt b/OpenPGP-Keychain-API-Demo/proguard-project.txt new file mode 100644 index 000000000..f2fe1559a --- /dev/null +++ b/OpenPGP-Keychain-API-Demo/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/OpenPGP-Keychain-API-Demo/project.properties b/OpenPGP-Keychain-API-Demo/project.properties new file mode 100644 index 000000000..85ec9915d --- /dev/null +++ b/OpenPGP-Keychain-API-Demo/project.properties @@ -0,0 +1,12 @@ +# 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 +android.library.reference.1=../OpenPGP-Keychain-API-Lib diff --git a/OpenPGP-Keychain-API-Demo/res/drawable-hdpi/icon.png b/OpenPGP-Keychain-API-Demo/res/drawable-hdpi/icon.png new file mode 100644 index 000000000..6b8cc3d73 Binary files /dev/null and b/OpenPGP-Keychain-API-Demo/res/drawable-hdpi/icon.png differ diff --git a/OpenPGP-Keychain-API-Demo/res/drawable-ldpi/icon.png b/OpenPGP-Keychain-API-Demo/res/drawable-ldpi/icon.png new file mode 100644 index 000000000..a1adf6bcb Binary files /dev/null and b/OpenPGP-Keychain-API-Demo/res/drawable-ldpi/icon.png differ diff --git a/OpenPGP-Keychain-API-Demo/res/drawable-mdpi/icon.png b/OpenPGP-Keychain-API-Demo/res/drawable-mdpi/icon.png new file mode 100644 index 000000000..6b10a2ad3 Binary files /dev/null and b/OpenPGP-Keychain-API-Demo/res/drawable-mdpi/icon.png differ diff --git a/OpenPGP-Keychain-API-Demo/res/drawable-xhdpi/icon.png b/OpenPGP-Keychain-API-Demo/res/drawable-xhdpi/icon.png new file mode 100644 index 000000000..03ee31bbd Binary files /dev/null and b/OpenPGP-Keychain-API-Demo/res/drawable-xhdpi/icon.png differ diff --git a/OpenPGP-Keychain-API-Demo/res/layout/aidl_demo.xml b/OpenPGP-Keychain-API-Demo/res/layout/aidl_demo.xml new file mode 100644 index 000000000..59977869d --- /dev/null +++ b/OpenPGP-Keychain-API-Demo/res/layout/aidl_demo.xml @@ -0,0 +1,72 @@ + + + + + + + + diff --git a/OpenPGP-Keychain/res/menu/nfc_beam.xml b/OpenPGP-Keychain/res/menu/nfc_beam.xml new file mode 100644 index 000000000..9cb86892d --- /dev/null +++ b/OpenPGP-Keychain/res/menu/nfc_beam.xml @@ -0,0 +1,10 @@ + +

+ + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/raw/help_about.html b/OpenPGP-Keychain/res/raw/help_about.html new file mode 100644 index 000000000..fd3a5f0e1 --- /dev/null +++ b/OpenPGP-Keychain/res/raw/help_about.html @@ -0,0 +1,32 @@ + + + + + +

https://github.com/dschuermann/openpgp-keychain

+

OpenPGP Keychain is an OpenPGP implementation. +The development began as a fork of Android Privacy Guard (APG)

+

License: Apache License v2

+ +

Developer

+
    +
  • Dominik Schürmann (Developer OpenPGP Keychain)
  • +
  • Oliver Runge
  • +
  • Markus Doits
  • +
  • Senecaso (QRCode, sign key, upload key)
  • +
  • Thialfihar (Main developer APG v1.x)
  • +
+ +

Libraries

+ + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/raw/help_changelog.html b/OpenPGP-Keychain/res/raw/help_changelog.html new file mode 100644 index 000000000..fa1201cc1 --- /dev/null +++ b/OpenPGP-Keychain/res/raw/help_changelog.html @@ -0,0 +1,87 @@ + + + + + +

2.0

+
    +
  • Complete redesign
  • +
  • Share public keys via qr codes, nfc beam
  • +
  • Sign keys
  • +
  • Upload keys to server
  • +
  • Fixes import issues
  • +
  • New AIDL API
  • +
+ +

1.08

+
    +
  • basic key server support
  • +
  • app2sd (untested, let me know if there are problems)
  • +
  • more choices for pass phrase cache: 1, 2, 4, 8, hours
  • +
  • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
  • +
  • bugfixes
  • +
  • optimizations
  • +
+ +

1.0.7

+
    +
  • clear sign problem with lacking trailing newline fixed
  • +
  • more options for pass phrase cache time to live (20, 40, 60 mins)
  • +
+ +

1.0.6

+
    +
  • account adding crash on Froyo fixed
  • +
  • secure file deletion
  • +
  • option to delete key file after import
  • +
  • stream encryption/decryption (gallery, etc.)
  • +
  • new options (language, force v3 signatures)
  • +
  • interface changes
  • +
  • bugfixes
  • +
+ +

1.0.5

+
    +
  • German and Italian translation
  • +
  • much smaller package, due to reduced BC sources
  • +
  • new preferences GUI
  • +
  • layout adjustment for localization
  • +
  • signature bugfix
  • +
+ +

1.0.4

+
    +
  • fixed another crash caused by some SDK bug with query builder
  • +
+ +

1.0.3

+
    +
  • fixed crashes during encryption/signing and possibly key export
  • +
+ +

1.0.2

+
    +
  • filterable key lists
  • +
  • smarter preselection of encryption keys
  • +
  • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
  • +
  • fixes and additional features (key preselection) for k9, new beta build available
  • +
+ +

1.0.1

+
    +
  • GMail account listing was broken in 1.0.0, fixed again
  • +
+ +

1.0.0

+
    +
  • k9mail integration, APG supporting beta build of k9mail
  • +
  • support of more file managers (including ASTRO)
  • +
  • Slovenian translation
  • +
  • new database, much faster, less memory usage
  • +
  • defined Intents and content provider for other apps
  • +
  • bugfixes
  • +
+ + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/raw/help_nfc_beam.html b/OpenPGP-Keychain/res/raw/help_nfc_beam.html new file mode 100644 index 000000000..aa510008c --- /dev/null +++ b/OpenPGP-Keychain/res/raw/help_nfc_beam.html @@ -0,0 +1,16 @@ + + + + + +

How to receive keyrings

+
    +
  1. Go to your partners 'Manage Public Keyrings' and long press on the keyring you want to share.
  2. +
  3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
  4. +
  5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
  6. +
  7. Tap the card and the content will then load on the your device.
  8. +
+ + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/raw/help_start.html b/OpenPGP-Keychain/res/raw/help_start.html new file mode 100644 index 000000000..1d72eb135 --- /dev/null +++ b/OpenPGP-Keychain/res/raw/help_start.html @@ -0,0 +1,27 @@ + + + + + +

EXPERIMENTAL software

+

This is EXPERIMENTAL beta software. It contains many remaining bugs!

+ +

NOT working right now

+
    +
  • Importing existing keys will be stripped of certificates right now
  • +
  • Key cross-certification is NOT supported, so signing with those keys will get a warning when the signature is checked.
  • +
  • K9 Mail integration not implemented
  • +
+

If you want to contribute, fork it and do a pull request on Github: https://github.com/dschuermann/openpgp-keychain

+ +

Getting started

+

Install K-9 Mail for the best integration, it supports APG for PGP/INLINE and lets you directly encrypt/decrypt emails. +
It is recommended that you install OI File Manager to be able to use the browse button for file selection in APG. +
First you need some keys. Either import them via the option menus in "Manage Keys" and "My Keys" or create them in "My Keys".

+ +

I found a bug in OpenPGP Keychain!

+

Please report it in the issue tracker of OpenPGP Keychain.

+ + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/raw/nfc_beam_share.html b/OpenPGP-Keychain/res/raw/nfc_beam_share.html new file mode 100644 index 000000000..734e69fc5 --- /dev/null +++ b/OpenPGP-Keychain/res/raw/nfc_beam_share.html @@ -0,0 +1,15 @@ + + + + + +
    +
  1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
  2. +
  3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
  4. +
  5. After it vibrates you’ll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
  6. +
  7. Tap the card and the content will then load on the other person’s device.
  8. +
+ + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/values/arrays.xml b/OpenPGP-Keychain/res/values/arrays.xml new file mode 100644 index 000000000..05320c609 --- /dev/null +++ b/OpenPGP-Keychain/res/values/arrays.xml @@ -0,0 +1,47 @@ + + + + + + + @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 + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/values/colors.xml b/OpenPGP-Keychain/res/values/colors.xml new file mode 100644 index 000000000..d1dc6c1f4 --- /dev/null +++ b/OpenPGP-Keychain/res/values/colors.xml @@ -0,0 +1,7 @@ + + + + #31b6e7 + #cecbce + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/values/static_strings.xml b/OpenPGP-Keychain/res/values/static_strings.xml new file mode 100644 index 000000000..6fe68affc --- /dev/null +++ b/OpenPGP-Keychain/res/values/static_strings.xml @@ -0,0 +1,7 @@ + + + + OpenPGP Keychain + https://github.com/dschuermann/apg + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml new file mode 100644 index 000000000..5e3807a3a --- /dev/null +++ b/OpenPGP-Keychain/res/values/strings.xml @@ -0,0 +1,361 @@ + + + + + + + Mail Inbox + Manage Public Keyrings + Manage Secret Keyrings + Select Public Keyring + Select Secret Keyring + Encrypt + Decrypt + Passphrase + Create Key + Edit Keyring + Create Keyring + Preferences + Key Server Preference + Change Passphrase + Set Passphrase + "Send Mail…" + Encrypt To File + Decrypt To File + Import Keyrings + Export Keyring + Export Keyrings + Key Not Found + Getting Started + Query Key Server + Export to Key Server + Unknown Signature Key + Sign Keyring + About + Help + Share keyring with NFC + + + User IDs + Keys + General + Defaults + Advanced + + + Sign (Clipboard) + Encrypt (Clipboard) + Encrypt (Email) + Sign (Email) + Encrypt + Sign + Decrypt + Verify + Select Recipients + Reply + Encrypt Message + Decrypt Message + Encrypt File + Decrypt File + Save + Cancel + Delete + None + Okay + Clear Filter + Change Passphrase + Set Passphrase + Search + Export To Server + + + About + Delete Account + Manage Public Keyrings + Manage Secret Keyrings + Settings + Import from file + Import from QR Code + Import from NFC + Export All Keyrings + Export To File + Delete Keyring + Create Keyring + Edit Keyring + Search + Help + Query Key Server + Update from Server + Export To Server + Share with… + Share with QR Code + Share with NFC + Scan QR Code + Sign Key + Beam Settings + + + Sign + Message + File + No Passphrase + Passphrase + Again + Algorithm + ASCII Armor + Public Key(s) + Delete After Encryption + Delete After Decryption + Delete After Import + Encryption Algorithm + Hash Algorithm + Public Key + Passphrase + Passphrase Cache + Message Compression + File Compression + Force V3 Signatures + Key Servers + Key ID + Creation + Expiry + Usage + Key Size + Main User ID + Name + Comment + Email + Send Keyring to Server? + Select + 1 Selected + Selected + <unknown> + <none> + <no key> + - + <no expiry> + + can encrypt + can sign + expired + not valid + %s key server(s) + Fingerprint: + Secret Keyring: + + + 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 passphrase. + Using clipboard content. + Key saved. + Set a passphrase first. + No compatible file manager installed. + The passphrases didn\'t match. + Empty passphrases are not allowed. + Symmetric encryption. + Enter passphrase for \'%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 passphrase twice. + Select at least one encryption keyring. + Select at least one encryption keyring or a signature keyring. + 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. + Please specify which file to import keyrings 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 keyrings.\nWARNING! File will be overwritten if it exists. + Do you really want to delete the keyring \'%s\'?\nYou can\'t undo this! + Do you really want to delete the SECRET keyring \'%s\'?\nYou can\'t undo this! + Successfully added %1$s keyring(s) and updated %2$s keyring(s). + Successfully added %s keyring(s). + Successfully updated %s keyring(s). + No keyrings added or updated. + Successfully exported 1 keyring. + Successfully exported %s keyrings. + No keyrings 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. + %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 keyring %s, do you want to try finding it on a keyserver? + Successfully sent keyring to server + Successfully signed keyring + Successfully validated and imported keyring + Long press one entry in this list to show more options! + This list is empty! + Successfully sent keyring with NFC Beam! + + + deleting \'%s\' failed + file not found + no suitable secret key found + no known kind of encryption found + external storage not ready + 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 passphrase given + signature failed + no passphrase given + no signature key given + not valid encryption data + corrupt data + couldn\'t find a packet with symmetric encryption + wrong passphrase + error saving some key(s) + could not extract private key + Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN. + You need Android 4.1 alias Jelly Bean to use Androids NFC Beam feature! + NFC is not available on your device! + Nothing to import! + + + 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 keyring… + importing secret keyrings… + importing public keyrings… + exporting keyring… + exporting keyrings… + 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… + + + OpenPGP Keychain + Permissions to use OpenPGP + Encrypt/Sign/Decrypt/Create keys without user interaction, Read key details of public and secret keys (The keys themselves can NOT be read.) + Encrypt/Sign/Decrypt/Create keys (by using Intents or Remote Service) without user interaction. Read key details of public and secret keys stored in OpenPGP, such as key ID and user IDs. The keys themselves can NOT be read. + Import/Export actual public and secret keys + Import and export actual private and public keys directly without user interaction. + + + Encrypt + Decrypt + Import Public Keyrings + Import Secret Keyrings + Search Public Keyrings + Search Secret Keyrings + Filter: \"%s\" + Share keyring with… + + + fast + slow + very slow + + + + + + Manage Public Keyrings + My Secret Keyrings + Encrypt + Decrypt + Help + Import Keys + + + Start + NFC Beam + Changelog + About + Version: + + + Import keyring(s) (only locally) + Import, Sign, and upload keyring(s) + Finish + + + OpenPGP: Decrypt File + OpenPGP: Import Keyring + OpenPGP: Encrypt + OpenPGP: Decrypt + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/values/styles.xml b/OpenPGP-Keychain/res/values/styles.xml new file mode 100644 index 000000000..ef3ff63f6 --- /dev/null +++ b/OpenPGP-Keychain/res/values/styles.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/xml/preferences.xml b/OpenPGP-Keychain/res/xml/preferences.xml new file mode 100644 index 000000000..6b040f8a7 --- /dev/null +++ b/OpenPGP-Keychain/res/xml/preferences.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/xml/searchable_public_keys.xml b/OpenPGP-Keychain/res/xml/searchable_public_keys.xml new file mode 100644 index 000000000..e9602b121 --- /dev/null +++ b/OpenPGP-Keychain/res/xml/searchable_public_keys.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/res/xml/searchable_secret_keys.xml b/OpenPGP-Keychain/res/xml/searchable_secret_keys.xml new file mode 100644 index 000000000..a7e8873d6 --- /dev/null +++ b/OpenPGP-Keychain/res/xml/searchable_secret_keys.xml @@ -0,0 +1,22 @@ + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Constants.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Constants.java new file mode 100644 index 000000000..10e431d4b --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Constants.java @@ -0,0 +1,57 @@ +/* + * 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.sufficientlysecure.keychain; + +import android.os.Environment; + +public final class Constants { + + public static final boolean DEBUG = true; + + public static final String TAG = "APG"; + + public static final String PACKAGE_NAME = "org.sufficientlysecure.keychain"; + + public static final String NFC_MIME = "application/org.sufficientlysecure.keychain"; + + public static final String PERMISSION_ACCESS_KEY_DATABASE = PACKAGE_NAME + + ".permission.ACCESS_KEY_DATABASE"; + public static final String PERMISSION_ACCESS_API = PACKAGE_NAME + ".permission.ACCESS_API"; + + public static final String INTENT_PREFIX = PACKAGE_NAME + ".action."; + + public static final class path { + public static final String APP_DIR = Environment.getExternalStorageDirectory() + "/APG"; + } + + public static final class pref { + 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"; + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java new file mode 100644 index 000000000..382f144d7 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java @@ -0,0 +1,231 @@ +/* + * 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.sufficientlysecure.keychain; + +import org.spongycastle.bcpg.CompressionAlgorithmTags; + +/** + * + * TODO: + * + * - refactor ids, some are not needed and can be done with xml + * + */ +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 share_qr_code = 0x21070007; + public static final int share_nfc = 0x21070008; + public static final int signKey = 0x21070009; + + 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 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 int encrypt = 0x21070013; + public static final int encrypt_to_clipboard = 0x21070014; + public static final int decrypt = 0x21070015; + public static final int reply = 0x21070016; + public static final int cancel = 0x21070017; + public static final int save = 0x21070018; + public static final int okay = 0x21070019; + public static final int import_from_file = 0x21070020; + public static final int import_from_qr_code = 0x21070021; + public static final int import_from_nfc = 0x21070022; + } + } + + // use only lower 16 bits due to compatibility lib + public static final class message { + public static final int progress_update = 0x00006001; + public static final int done = 0x00006002; + public static final int import_keys = 0x00006003; + public static final int export_keys = 0x00006004; + public static final int import_done = 0x00006005; + public static final int export_done = 0x00006006; + public static final int create_key = 0x00006007; + public static final int edit_key = 0x00006008; + public static final int delete_done = 0x00006009; + public static final int query_done = 0x00006010; + public static final int unknown_signature_key = 0x00006011; + } + + // 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; + // } + + // use only lower 16 bits due to compatibility lib + public static final class request { + public static final int public_keys = 0x00007001; + public static final int secret_keys = 0x00007002; + public static final int filename = 0x00007003; + public static final int output_filename = 0x00007004; + public static final int key_server_preference = 0x00007005; + public static final int look_up_key_id = 0x00007006; + public static final int export_to_server = 0x00007007; + public static final int import_from_qr_code = 0x00007008; + public static final int sign_key = 0x00007009; + } + + // 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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/KeychainApplication.java new file mode 100644 index 000000000..dbe9f6394 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/KeychainApplication.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain; + +import java.io.File; +import java.security.Security; + +import org.spongycastle.jce.provider.BouncyCastleProvider; + +import android.app.Application; +import android.os.Environment; + +public class KeychainApplication extends Application { + + static { + // Define Java Security Provider to be Bouncy Castle + Security.addProvider(new BouncyCastleProvider()); + } + + @Override + public void onCreate() { + super.onCreate(); + + // Create APG directory on sdcard if not existing + 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 + } + } + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java new file mode 100644 index 000000000..5ea5bd09a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.compatibility; + +import java.lang.reflect.Method; + +import android.content.Context; + +import org.sufficientlysecure.keychain.util.Log; + +public class ClipboardReflection { + + private static final String clipboardLabel = "APG"; + + /** + * Wrapper around ClipboardManager based on Android version using Reflection API + * + * @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 methodSetText = clipboard.getClass() + .getMethod("setText", CharSequence.class); + methodSetText.invoke(clipboard, text); + } else if ("android.content.ClipboardManager".equals(clipboard.getClass().getName())) { + Class classClipData = Class.forName("android.content.ClipData"); + Method methodNewPlainText = classClipData.getMethod("newPlainText", + CharSequence.class, CharSequence.class); + Object clip = methodNewPlainText.invoke(null, clipboardLabel, text); + methodNewPlainText = clipboard.getClass() + .getMethod("setPrimaryClip", classClipData); + methodNewPlainText.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 methodGetText = clipboard.getClass().getMethod("getText"); + Object text = methodGetText.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", int.class); + 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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java new file mode 100644 index 000000000..8c0a26a28 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/DialogFragmentWorkaround.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.compatibility; + +import android.os.Build; +import android.os.Handler; + +/** + * Bug on Android >= 4.2 + * + * http://code.google.com/p/android/issues/detail?id=41901 + * + * DialogFragment disappears on pressing home and comming back. This also happens especially in + * FileDialogFragment after launching a file manager and coming back. + * + * Usage: + * DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { + * public void run() { + * // show dialog... + * } + * }); + * + */ +public class DialogFragmentWorkaround { + public static final SDKLevel17Interface INTERFACE = ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ? new SDKLevel17Impl() + : new SDKLevelPriorLevel17Impl()); + + private static final int RUNNABLE_DELAY = 300; + + public interface SDKLevel17Interface { + // Workaround for http://code.google.com/p/android/issues/detail?id=41901 + void runnableRunDelayed(Runnable runnable); + } + + private static class SDKLevelPriorLevel17Impl implements SDKLevel17Interface { + @Override + public void runnableRunDelayed(Runnable runnable) { + runnable.run(); + } + } + + private static class SDKLevel17Impl implements SDKLevel17Interface { + @Override + public void runnableRunDelayed(Runnable runnable) { + new Handler().postDelayed(runnable, RUNNABLE_DELAY); + } + } + + // Can't instantiate this class + private DialogFragmentWorkaround() { + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ListFragmentWorkaround.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ListFragmentWorkaround.java new file mode 100644 index 000000000..a1f27ca20 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/compatibility/ListFragmentWorkaround.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.compatibility; + +import android.view.View; +import android.widget.ListView; + +import com.actionbarsherlock.app.SherlockListFragment; + +/** + * Bug on Android >= 4.1 + * + * http://code.google.com/p/android/issues/detail?id=35885 + * + * Items are not checked in layout + */ +public class ListFragmentWorkaround extends SherlockListFragment { + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + l.setItemChecked(position, l.isItemChecked(position)); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/FileHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/FileHelper.java new file mode 100644 index 000000000..f16fd874b --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/FileHelper.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.helper; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Log; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Environment; +import android.widget.Toast; + +public class FileHelper { + + /** + * Checks if external storage is mounted if file is located on external storage + * + * @param file + * @return true if storage is mounted + */ + public static boolean isStorageMounted(String file) { + if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + return false; + } + } + + return true; + } + + /** + * Opens the preferred installed file manager on Android and shows a toast if no manager is + * installed. + * + * @param activity + * @param filename + * default selected file, not supported by all file managers + * @param type + * can be text/plain for example + * @param requestCode + * requestCode used to identify the result coming back from file manager to + * onActivityResult() in your activity + */ + public static void openFile(Activity activity, String filename, String type, int requestCode) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + + intent.setData(Uri.parse("file://" + filename)); + intent.setType(type); + + try { + activity.startActivityForResult(intent, requestCode); + } catch (ActivityNotFoundException e) { + // No compatible file manager was found. + Toast.makeText(activity, R.string.noFilemanagerInstalled, Toast.LENGTH_SHORT).show(); + } + } + + /** + * Get a file path from a Uri. + * + * from https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/ + * afilechooser/utils/FileUtils.java + * + * @param context + * @param uri + * @return + * + * @author paulburke + */ + public static String getPath(Context context, Uri uri) { + Log.d(Constants.TAG + " File -", + "Authority: " + uri.getAuthority() + ", Fragment: " + uri.getFragment() + + ", Port: " + uri.getPort() + ", Query: " + uri.getQuery() + ", Scheme: " + + uri.getScheme() + ", Host: " + uri.getHost() + ", Segments: " + + uri.getPathSegments().toString()); + + if ("content".equalsIgnoreCase(uri.getScheme())) { + String[] projection = { "_data" }; + Cursor cursor = null; + + try { + cursor = context.getContentResolver().query(uri, projection, null, null, null); + int column_index = cursor.getColumnIndexOrThrow("_data"); + if (cursor.moveToFirst()) { + return cursor.getString(column_index); + } + } catch (Exception e) { + // Eat it + } + } + + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java new file mode 100644 index 000000000..6a71ca0ba --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.helper; + +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Iterator; +import java.util.Set; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import android.app.Activity; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.widget.Toast; + +public class OtherHelper { + + /** + * Return the number if days between two dates + * + * @param first + * @param second + * @return number of days + */ + public 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; + } + + /** + * Logs bundle content to debug for inspecting the content + * + * @param bundle + * @param bundleName + */ + public static void logDebugBundle(Bundle bundle, String bundleName) { + if (Constants.DEBUG) { + if (bundle != null) { + Set ks = bundle.keySet(); + Iterator iterator = ks.iterator(); + + Log.d(Constants.TAG, "Bundle " + bundleName + ":"); + Log.d(Constants.TAG, "------------------------------"); + while (iterator.hasNext()) { + String key = iterator.next(); + Object value = bundle.get(key); + + if (value != null) { + Log.d(Constants.TAG, key + " : " + value.toString()); + } else { + Log.d(Constants.TAG, key + " : null"); + } + } + Log.d(Constants.TAG, "------------------------------"); + } else { + Log.d(Constants.TAG, "Bundle " + bundleName + ": null"); + } + } + } + + /** + * Set actionbar without home button if called from another app + * + * @param activity + */ + public static void setActionBarBackButton(SherlockFragmentActivity activity) { + // set actionbar without home button if called from another app + final ActionBar actionBar = activity.getSupportActionBar(); + Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)=" + + activity.getCallingPackage()); + if (activity.getCallingPackage() != null + && activity.getCallingPackage().equals(Constants.PACKAGE_NAME)) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + } else { + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + } + } + + /** + * Check if the calling package has the needed permission to invoke an intent with specific + * restricted actions. + * + * If pkgName is null, this will also deny the use of the given action + * + * @param activity + * @param pkgName + * @param permName + * @param action + * @param restrictedActions + */ + public static void checkPackagePermissionForActions(Activity activity, String pkgName, + String permName, String action, String[] restrictedActions) { + if (action != null) { + PackageManager pkgManager = activity.getPackageManager(); + + for (int i = 0; i < restrictedActions.length; i++) { + if (restrictedActions[i].equals(action)) { + if (pkgName != null + && (pkgManager.checkPermission(permName, pkgName) == PackageManager.PERMISSION_GRANTED || pkgName + .equals(Constants.PACKAGE_NAME))) { + Log.d(Constants.TAG, pkgName + " has permission " + permName + ". Action " + + action + " was granted!"); + } else { + String error = pkgName + " does NOT have permission " + permName + + ". Action " + action + " was NOT granted!"; + Log.e(Constants.TAG, error); + Toast.makeText(activity, activity.getString(R.string.errorMessage, error), + Toast.LENGTH_LONG).show(); + + // end activity + activity.setResult(Activity.RESULT_CANCELED, null); + activity.finish(); + } + } + } + } + } + + /** + * Splits userId string into naming part and email part + * + * @param userId + * @return array with naming (0) and email (1) + */ + public static String[] splitUserId(String userId) { + String[] output = new String[2]; + + String chunks[] = userId.split(" <", 2); + userId = chunks[0]; + if (chunks.length > 1) { + output[1] = "<" + chunks[1]; + } + output[0] = userId; + + return output; + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java new file mode 100644 index 000000000..3be4c595c --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpConversionHelper.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.helper; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPUtil; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + + +public class PgpConversionHelper { + + /** + * Convert from byte[] to PGPKeyRing + * + * @param keysBytes + * @return + */ + public static PGPKeyRing BytesToPGPKeyRing(byte[] keysBytes) { + PGPObjectFactory factory = new PGPObjectFactory(keysBytes); + PGPKeyRing keyRing = null; + try { + if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) { + Log.e(Constants.TAG, "No keys given!"); + } + } catch (IOException e) { + Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e); + } + + return keyRing; + } + + /** + * Convert from byte[] to ArrayList + * + * @param keysBytes + * @return + */ + public static ArrayList BytesToPGPSecretKeyList(byte[] keysBytes) { + PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes); + ArrayList keys = new ArrayList(); + + @SuppressWarnings("unchecked") + Iterator itr = keyRing.getSecretKeys(); + while (itr.hasNext()) { + keys.add(itr.next()); + } + + return keys; + } + + /** + * Convert from byte[] to PGPSecretKey + * + * Singles keys are encoded as keyRings with one single key in it by Bouncy Castle + * + * @param keysBytes + * @return + */ + public static PGPSecretKey BytesToPGPSecretKey(byte[] keyBytes) { + PGPSecretKey key = BytesToPGPSecretKeyList(keyBytes).get(0); + + return key; + } + + /** + * Convert from ArrayList to byte[] + * + * @param keys + * @return + */ + public static byte[] PGPSecretKeyArrayListToBytes(ArrayList keys) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + for (PGPSecretKey key : keys) { + try { + key.encode(os); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while converting ArrayList to byte[]!", e); + } + } + + return os.toByteArray(); + } + + /** + * Convert from PGPSecretKey to byte[] + * + * @param keysBytes + * @return + */ + public static byte[] PGPSecretKeyToBytes(PGPSecretKey key) { + try { + return key.getEncoded(); + } catch (IOException e) { + Log.e(Constants.TAG, "Encoding failed", e); + + return null; + } + } + + /** + * Convert from PGPSecretKeyRing to byte[] + * + * @param keysBytes + * @return + */ + public static byte[] PGPSecretKeyRingToBytes(PGPSecretKeyRing keyRing) { + try { + return keyRing.getEncoded(); + } catch (IOException e) { + Log.e(Constants.TAG, "Encoding failed", e); + + return null; + } + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java new file mode 100644 index 000000000..f59bd58de --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpHelper.java @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.helper; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.Vector; + +import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.content.Context; + +public class PgpHelper { + + 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(Context context, long masterKeyId) { + PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context, + masterKeyId); + if (keyRing == null) { + Log.e(Constants.TAG, "keyRing is null!"); + return null; + } + Vector encryptKeys = getUsableEncryptKeys(keyRing); + if (encryptKeys.size() == 0) { + Log.e(Constants.TAG, "encryptKeys is null!"); + return null; + } + return encryptKeys.get(0); + } + + public static PGPSecretKey getSigningKey(Context context, long masterKeyId) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, + 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.equals("")) { + userId = context.getString(R.string.unknownUserId); + } + return userId; + } + + public static String getMainUserIdSafe(Context context, PGPSecretKey key) { + String userId = getMainUserId(key); + if (userId == null || userId.equals("")) { + userId = context.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 convertFingerprintToHex(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(Locale.US); + while (chunk.length() < 2) { + chunk = "0" + chunk; + } + fingerPrint += chunk; + } + + return fingerPrint; + + } + + public static String getFingerPrint(Context context, long keyId) { + PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId); + // if it is no public key get it from your own keys... + if (key == null) { + PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId); + if (secretKey == null) { + Log.e(Constants.TAG, "Key could not be found!"); + return null; + } + key = secretKey.getPublicKey(); + } + + return convertFingerprintToHex(key.getFingerprint()); + } + + public static String getSmallFingerPrint(long keyId) { + String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase(Locale.US); + 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); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java new file mode 100644 index 000000000..26842e4dc --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/PgpMain.java @@ -0,0 +1,1857 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.helper; + +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.PGPPublicKeyRingCollection; +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.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.PGPDigestCalculator; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.util.HkpKeyServer; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.PositionAwareInputStream; +import org.sufficientlysecure.keychain.util.Primes; +import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; +import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException; +import org.sufficientlysecure.keychain.R; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.os.Environment; + +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.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.regex.Pattern; + +/** + * TODO: + * + * - Separate this file into different helpers + * + */ +public class PgpMain { + + static { + // register spongy castle provider + Security.addProvider(new BouncyCastleProvider()); + } + + // Not BC due to the use of Spongy Castle for Android + public static final String BOUNCY_CASTLE_PROVIDER_NAME = "SC"; + + 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 String mEditPassPhrase = null; + + public static class PgpGeneralException extends Exception { + static final long serialVersionUID = 0xf812773342L; + + public PgpGeneralException(String message) { + super(message); + } + } + + public static class NoAsymmetricEncryptionException extends Exception { + static final long serialVersionUID = 0xf812773343L; + + public NoAsymmetricEncryptionException() { + super(); + } + } + + public static void setEditPassPhrase(String passPhrase) { + mEditPassPhrase = passPhrase; + } + + public static String getEditPassPhrase() { + return mEditPassPhrase; + } + + public static void updateProgress(ProgressDialogUpdater progress, int message, int current, + int total) { + if (progress != null) { + progress.setProgress(message, current, total); + } + } + + public static void updateProgress(ProgressDialogUpdater progress, int current, int total) { + if (progress != null) { + progress.setProgress(current, total); + } + } + + /** + * Creates new secret key. The returned PGPSecretKeyRing contains only one newly generated key + * when this key is the new masterkey. If a masterkey is supplied in the parameters + * PGPSecretKeyRing contains the masterkey and the new key as a subkey (certified by the + * masterkey). + * + * @param context + * @param algorithmChoice + * @param keySize + * @param passPhrase + * @param masterSecretKey + * @return + * @throws NoSuchAlgorithmException + * @throws PGPException + * @throws NoSuchProviderException + * @throws PgpGeneralException + * @throws InvalidAlgorithmParameterException + */ + public static PGPSecretKeyRing createKey(Context context, int algorithmChoice, int keySize, + String passPhrase, PGPSecretKey masterSecretKey) throws NoSuchAlgorithmException, + PGPException, NoSuchProviderException, PgpGeneralException, + InvalidAlgorithmParameterException { + + if (keySize < 512) { + throw new PgpGeneralException(context.getString(R.string.error_keySizeMinimum512bit)); + } + + if (passPhrase == null) { + passPhrase = ""; + } + + int algorithm = 0; + KeyPairGenerator keyGen = null; + + switch (algorithmChoice) { + case Id.choice.algorithm.dsa: { + keyGen = KeyPairGenerator.getInstance("DSA", BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + algorithm = PGPPublicKey.DSA; + break; + } + + case Id.choice.algorithm.elgamal: { + if (masterSecretKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_masterKeyMustNotBeElGamal)); + } + keyGen = KeyPairGenerator.getInstance("ElGamal", BOUNCY_CASTLE_PROVIDER_NAME); + 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", BOUNCY_CASTLE_PROVIDER_NAME); + keyGen.initialize(keySize, new SecureRandom()); + + algorithm = PGPPublicKey.RSA_GENERAL; + break; + } + + default: { + throw new PgpGeneralException(context.getString(R.string.error_unknownAlgorithmChoice)); + } + } + + // build new key pair + PGPKeyPair keyPair = new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + + // Build key encrypter and decrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + passPhrase.toCharArray()); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray()); + + PGPKeyRingGenerator ringGen = null; + PGPContentSignerBuilder certificationSignerBuilder = null; + if (masterSecretKey == null) { + certificationSignerBuilder = new JcaPGPContentSignerBuilder(keyPair.getPublicKey() + .getAlgorithm(), HashAlgorithmTags.SHA1); + + // build keyRing with only this one master key in it! + ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, keyPair, "", + sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); + } else { + PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); + PGPPrivateKey masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); + + certificationSignerBuilder = new JcaPGPContentSignerBuilder(masterKeyPair + .getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + // build keyRing with master key and new key as subkey (certified by masterkey) + ringGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, + "", sha1Calc, null, null, certificationSignerBuilder, keyEncryptor); + + ringGen.addSubKey(keyPair); + } + + PGPSecretKeyRing secKeyRing = ringGen.generateSecretKeyRing(); + + return secKeyRing; + } + + public static void buildSecretKey(Context context, ArrayList userIds, + ArrayList keys, ArrayList keysUsages, long masterKeyId, + String oldPassPhrase, String newPassPhrase, ProgressDialogUpdater progress) + throws PgpGeneralException, NoSuchProviderException, PGPException, + NoSuchAlgorithmException, SignatureException, IOException { + + Log.d(Constants.TAG, "userIds: " + userIds.toString()); + + updateProgress(progress, R.string.progress_buildingKey, 0, 100); + + if (oldPassPhrase == null) { + oldPassPhrase = ""; + } + if (newPassPhrase == null) { + newPassPhrase = ""; + } + + updateProgress(progress, R.string.progress_preparingMasterKey, 10, 100); + + int usageId = keysUsages.get(0); + 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); + + // this removes all userIds and certifications previously attached to the masterPublicKey + PGPPublicKey tmpKey = masterKey.getPublicKey(); + PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(), + tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime()); + + // already done by code above: + // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); + // // Somehow, the PGPPublicKey already has an empty certification attached to it when the + // // keyRing is generated the first time, we remove that when it exists, before adding the + // new + // // ones + // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey, + // ""); + // if (masterPublicKeyRmCert != null) { + // masterPublicKey = masterPublicKeyRmCert; + // } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); + + updateProgress(progress, R.string.progress_certifyingMasterKey, 20, 100); + + for (String userId : userIds) { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + sGen.init(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 (APG 1) + + 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 (APG 1) + // if (keyEditor.getExpiryDate() != null) { + // GregorianCalendar creationDate = new GregorianCalendar(); + // creationDate.setTime(getCreationDate(masterKey)); + // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); + // long numDays = Utils.getNumDaysBetween(creationDate, expiryDate); + // if (numDays <= 0) { + // throw new GeneralException( + // context.getString(R.string.error_expiryMustComeAfterCreation)); + // } + // hashedPacketsGen.setKeyExpirationTime(true, numDays * 86400); + // } + + updateProgress(progress, R.string.progress_buildingMasterKeyRing, 30, 100); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( + masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + // Build key encrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + newPassPhrase.toCharArray()); + + PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), + unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); + + updateProgress(progress, R.string.progress_addingSubKeys, 40, 100); + + for (int i = 1; i < keys.size(); ++i) { + updateProgress(progress, 40 + 50 * (i - 1) / (keys.size() - 1), 100); + + PGPSecretKey subKey = keys.get(i); + PGPPublicKey subPublicKey = subKey.getPublicKey(); + + PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); + + // TODO: now used without algorithm and creation time?! (APG 1) + PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); + + hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + keyFlags = 0; + + usageId = keysUsages.get(i); + 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 (APG 1) + // if (keyEditor.getExpiryDate() != null) { + // GregorianCalendar creationDate = new GregorianCalendar(); + // creationDate.setTime(getCreationDate(masterKey)); + // GregorianCalendar expiryDate = keyEditor.getExpiryDate(); + // long numDays = Utils.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(); + + updateProgress(progress, R.string.progress_savingKeyRing, 90, 100); + + ProviderHelper.saveKeyRing(context, secretKeyRing); + ProviderHelper.saveKeyRing(context, publicKeyRing); + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + /** + * TODO: implement Id.return_value.updated as status when key already existed + * + * @param context + * @param keyring + * @return + */ + public static int storeKeyRingInCache(Context context, PGPKeyRing keyring) { + int status = Integer.MIN_VALUE; // out of bounds value (Id.return_value.*) + try { + if (keyring instanceof PGPSecretKeyRing) { + PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; + boolean save = true; + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(new char[] {}); + PGPPrivateKey testKey = secretKeyRing.getSecretKey().extractPrivateKey( + keyDecryptor); + 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) { + ProviderHelper.saveKeyRing(context, secretKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } else if (keyring instanceof PGPPublicKeyRing) { + PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring; + ProviderHelper.saveKeyRing(context, publicKeyRing); + // TODO: remove status returns, use exceptions! + status = Id.return_value.ok; + } + } catch (IOException 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(Context context, InputData data, + ProgressDialogUpdater progress) throws PgpGeneralException, FileNotFoundException, + PGPException, IOException { + Bundle returnData = new Bundle(); + + updateProgress(progress, R.string.progress_importingSecretKeys, 0, 100); + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new PgpGeneralException(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 { + + // read all available blocks... (asc files can contain many blocks with BEGIN END) + while (bufferedInput.available() > 0) { + InputStream in = PGPUtil.getDecoderStream(bufferedInput); + PGPObjectFactory objectFactory = new PGPObjectFactory(in); + + // go through all objects in this block + Object obj; + while ((obj = objectFactory.nextObject()) != null) { + Log.d(Constants.TAG, "Found class: " + obj.getClass()); + + if (obj instanceof PGPKeyRing) { + PGPKeyRing keyring = (PGPKeyRing) obj; + + int status = Integer.MIN_VALUE; // out of bounds value + + status = storeKeyRingInCache(context, keyring); + + if (status == Id.return_value.error) { + throw new PgpGeneralException( + 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; + } + + updateProgress(progress, + (int) (100 * progressIn.position() / data.getSize()), 100); + } else { + Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); + } + } + } + } catch (Exception e) { + Log.e(Constants.TAG, "Exception on parsing key file!", e); + } + + returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); + returnData.putInt(KeychainIntentService.RESULT_IMPORT_UPDATED, oldKeys); + returnData.putInt(KeychainIntentService.RESULT_IMPORT_BAD, badKeys); + + updateProgress(progress, R.string.progress_done, 100, 100); + + return returnData; + } + + public static Bundle exportKeyRings(Context context, ArrayList keyRingMasterKeyIds, + int keyType, OutputStream outStream, ProgressDialogUpdater progress) + throws PgpGeneralException, FileNotFoundException, PGPException, IOException { + Bundle returnData = new Bundle(); + + if (keyRingMasterKeyIds.size() == 1) { + updateProgress(progress, R.string.progress_exportingKey, 0, 100); + } else { + updateProgress(progress, R.string.progress_exportingKeys, 0, 100); + } + + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new PgpGeneralException(context.getString(R.string.error_externalStorageNotReady)); + } + + // export public keyrings... + ArmoredOutputStream outPub = new ArmoredOutputStream(outStream); + outPub.setHeader("Version", getFullVersion(context)); + + int numKeys = 0; + for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { + // double the needed time if exporting both public and secret parts + if (keyType == Id.type.secret_key) { + updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); + } else { + updateProgress(progress, i * 100 / keyRingMasterKeyIds.size(), 100); + } + + PGPPublicKeyRing publicKeyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId( + context, keyRingMasterKeyIds.get(i)); + + if (publicKeyRing != null) { + publicKeyRing.encode(outPub); + } + ++numKeys; + } + outPub.close(); + + // if we export secret keyrings, append all secret parts after the public parts + if (keyType == Id.type.secret_key) { + ArmoredOutputStream outSec = new ArmoredOutputStream(outStream); + outSec.setHeader("Version", getFullVersion(context)); + + for (int i = 0; i < keyRingMasterKeyIds.size(); ++i) { + updateProgress(progress, i * 100 / keyRingMasterKeyIds.size() / 2, 100); + + PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( + context, keyRingMasterKeyIds.get(i)); + + if (secretKeyRing != null) { + secretKeyRing.encode(outSec); + } + } + outSec.close(); + } + + returnData.putInt(KeychainIntentService.RESULT_EXPORT, numKeys); + + updateProgress(progress, R.string.progress_done, 100, 100); + + return returnData; + } + + /** + * Encrypt and Sign data + * + * @param context + * @param progress + * @param data + * @param outStream + * @param useAsciiArmor + * @param compression + * @param encryptionKeyIds + * @param symmetricEncryptionAlgorithm + * @param encryptionPassphrase + * @param signatureKeyId + * @param signatureHashAlgorithm + * @param signatureForceV3 + * @param signaturePassphrase + * @throws IOException + * @throws PgpGeneralException + * @throws PGPException + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + * @throws SignatureException + */ + public static void encryptAndSign(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean useAsciiArmor, int compression, + long encryptionKeyIds[], String encryptionPassphrase, int symmetricEncryptionAlgorithm, + long signatureKeyId, int signatureHashAlgorithm, boolean signatureForceV3, + String signaturePassphrase) throws IOException, PgpGeneralException, PGPException, + NoSuchProviderException, NoSuchAlgorithmException, SignatureException { + + if (encryptionKeyIds == null) { + encryptionKeyIds = new long[0]; + } + + ArmoredOutputStream armorOut = null; + OutputStream out = null; + OutputStream encryptOut = null; + if (useAsciiArmor) { + 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 && encryptionPassphrase == null) { + throw new PgpGeneralException( + context.getString(R.string.error_noEncryptionKeysOrPassPhrase)); + } + + if (signatureKeyId != Id.key.none) { + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PgpHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassphrase == null) { + throw new PgpGeneralException( + context.getString(R.string.error_noSignaturePassPhrase)); + } + + updateProgress(progress, R.string.progress_extractingSignatureKey, 0, 100); + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + } + updateProgress(progress, R.string.progress_preparingStreams, 5, 100); + + // encrypt and compress input file content + JcePGPDataEncryptorBuilder encryptorBuilder = new JcePGPDataEncryptorBuilder( + symmetricEncryptionAlgorithm).setProvider(BOUNCY_CASTLE_PROVIDER_NAME) + .setWithIntegrityPacket(true); + + PGPEncryptedDataGenerator cPk = new PGPEncryptedDataGenerator(encryptorBuilder); + + if (encryptionKeyIds.length == 0) { + // Symmetric encryption + Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption"); + + JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = new JcePBEKeyEncryptionMethodGenerator( + encryptionPassphrase.toCharArray()); + cPk.addMethod(symmetricEncryptionGenerator); + } else { + // Asymmetric encryption + for (int i = 0; i < encryptionKeyIds.length; ++i) { + PGPPublicKey key = PgpHelper.getEncryptPublicKey(context, encryptionKeyIds[i]); + if (key != null) { + + JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator = new JcePublicKeyKeyEncryptionMethodGenerator( + key); + cPk.addMethod(pubKeyEncryptionGenerator); + } + } + } + encryptOut = cPk.open(out, new byte[1 << 16]); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + if (signatureKeyId != Id.key.none) { + updateProgress(progress, R.string.progress_preparingSignature, 10, 100); + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + signingKey.getPublicKey().getAlgorithm(), signatureHashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (signatureForceV3) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(PGPSignature.BINARY_DOCUMENT, signaturePrivateKey); + + String userId = PgpHelper.getMainUserId(PgpHelper.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 != Id.key.none) { + if (signatureForceV3) { + 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]); + updateProgress(progress, 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 != Id.key.none) { + if (signatureForceV3) { + signatureV3Generator.update(buffer, 0, n); + } else { + signatureGenerator.update(buffer, 0, n); + } + } + done += n; + if (data.getSize() != 0) { + updateProgress(progress, (int) (20 + (95 - 20) * done / data.getSize()), 100); + } + } + + literalGen.close(); + + if (signatureKeyId != Id.key.none) { + updateProgress(progress, R.string.progress_generatingSignature, 95, 100); + if (signatureForceV3) { + signatureV3Generator.generate().encode(pOut); + } else { + signatureGenerator.generate().encode(pOut); + } + } + if (compressGen != null) { + compressGen.close(); + } + encryptOut.close(); + if (useAsciiArmor) { + armorOut.close(); + } + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + public static void signText(Context context, ProgressDialogUpdater progress, InputData data, + OutputStream outStream, long signatureKeyId, String signaturePassphrase, + int signatureHashAlgorithm, boolean forceV3Signature) throws PgpGeneralException, + PGPException, IOException, NoSuchAlgorithmException, SignatureException { + + ArmoredOutputStream armorOut = new ArmoredOutputStream(outStream); + armorOut.setHeader("Version", getFullVersion(context)); + + PGPSecretKey signingKey = null; + PGPSecretKeyRing signingKeyRing = null; + PGPPrivateKey signaturePrivateKey = null; + + if (signatureKeyId == 0) { + armorOut.close(); + throw new PgpGeneralException(context.getString(R.string.error_noSignatureKey)); + } + + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PgpHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + armorOut.close(); + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassphrase == null) { + armorOut.close(); + throw new PgpGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); + } + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassphrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + armorOut.close(); + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + updateProgress(progress, R.string.progress_preparingStreams, 0, 100); + + updateProgress(progress, R.string.progress_preparingSignature, 30, 100); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey + .getPublicKey().getAlgorithm(), signatureHashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (forceV3Signature) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(PGPSignature.CANONICAL_TEXT_DOCUMENT, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + + updateProgress(progress, R.string.progress_signing, 40, 100); + + armorOut.beginClearText(signatureHashAlgorithm); + + 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(); + + updateProgress(progress, R.string.progress_done, 100, 100); + } + + public static void generateSignature(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean armored, boolean binary, + long signatureKeyId, String signaturePassPhrase, int hashAlgorithm, + boolean forceV3Signature) throws PgpGeneralException, PGPException, IOException, + NoSuchAlgorithmException, SignatureException { + + OutputStream out = null; + + // Ascii Armor (Base64) + ArmoredOutputStream armorOut = 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 PgpGeneralException(context.getString(R.string.error_noSignatureKey)); + } + + signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, signatureKeyId); + signingKey = PgpHelper.getSigningKey(context, signatureKeyId); + if (signingKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + if (signaturePassPhrase == null) { + throw new PgpGeneralException(context.getString(R.string.error_noSignaturePassPhrase)); + } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(signaturePassPhrase.toCharArray()); + signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + updateProgress(progress, R.string.progress_preparingStreams, 0, 100); + + updateProgress(progress, R.string.progress_preparingSignature, 30, 100); + + PGPSignatureGenerator signatureGenerator = null; + PGPV3SignatureGenerator signatureV3Generator = null; + + int type = PGPSignature.CANONICAL_TEXT_DOCUMENT; + if (binary) { + type = PGPSignature.BINARY_DOCUMENT; + } + + // content signer based on signing key algorithm and choosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(signingKey + .getPublicKey().getAlgorithm(), hashAlgorithm) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + if (forceV3Signature) { + signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(type, signaturePrivateKey); + } else { + signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(type, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + String userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signingKeyRing)); + spGen.setSignerUserID(false, userId); + signatureGenerator.setHashedSubpackets(spGen.generate()); + } + + updateProgress(progress, 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 PGPPublicKeyRing signKey(Context context, long masterKeyId, long pubKeyId, + String passphrase) throws PgpGeneralException, NoSuchAlgorithmException, + NoSuchProviderException, PGPException, SignatureException { + if (passphrase == null || passphrase.length() <= 0) { + throw new PgpGeneralException("Unable to obtain passphrase"); + } else { + PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByKeyId(context, pubKeyId); + + PGPSecretKey signingKey = PgpHelper.getSigningKey(context, masterKeyId); + if (signingKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_signatureFailed)); + } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + PGPPrivateKey signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor); + if (signaturePrivateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + + // TODO: SHA256 fixed? + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + signingKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator( + contentSignerBuilder); + + signatureGenerator.init(PGPSignature.DIRECT_KEY, signaturePrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + + PGPSignatureSubpacketVector packetVector = spGen.generate(); + signatureGenerator.setHashedSubpackets(packetVector); + + PGPPublicKey signedKey = PGPPublicKey.addCertification(pubring.getPublicKey(pubKeyId), + signatureGenerator.generate()); + pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey); + + return pubring; + } + } + + public static long getDecryptionKeyId(Context context, InputStream inputStream) + throws PgpGeneralException, NoAsymmetricEncryptionException, IOException { + InputStream in = PGPUtil.getDecoderStream(inputStream); + 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 PgpGeneralException(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 = ProviderHelper.getPGPSecretKeyByKeyId(context, 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, InputStream inputStream) + throws PgpGeneralException, IOException { + InputStream in = PGPUtil.getDecoderStream(inputStream); + 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 PgpGeneralException(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 decryptAndVerify(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, String passphrase, boolean assumeSymmetric) + throws IOException, PgpGeneralException, 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 PgpGeneralException(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 PgpGeneralException( + context.getString(R.string.error_noSymmetricEncryptionPacket)); + } + + updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); + + PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(); + PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( + digestCalcProvider).setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build( + passphrase.toCharArray()); + + clear = pbe.getDataStream(decryptorFactory); + + 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 = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); + if (secretKey != null) { + pbe = encData; + break; + } + } + } + + if (secretKey == null) { + throw new PgpGeneralException(context.getString(R.string.error_noSecretKeyFound)); + } + + currentProgress += 5; + updateProgress(progress, R.string.progress_extractingKey, currentProgress, 100); + PGPPrivateKey privateKey = null; + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + privateKey = secretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + throw new PGPException(context.getString(R.string.error_wrongPassPhrase)); + } + if (privateKey == null) { + throw new PgpGeneralException( + context.getString(R.string.error_couldNotExtractPrivateKey)); + } + currentProgress += 5; + updateProgress(progress, R.string.progress_preparingStreams, currentProgress, 100); + + PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey); + + clear = pbe.getDataStream(decryptorFactory); + + 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(KeychainIntentService.RESULT_SIGNATURE, true); + PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; + for (int i = 0; i < sigList.size(); ++i) { + signature = sigList.get(i); + signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); + if (signatureKeyId == 0) { + signatureKeyId = signature.getKeyID(); + } + if (signatureKey == null) { + signature = null; + } else { + signatureIndex = i; + signatureKeyId = signature.getKeyID(); + String userId = null; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId( + context, signatureKeyId); + if (signKeyRing != null) { + userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signKeyRing)); + } + returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); + break; + } + } + + returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + + if (signature != null) { + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + signature.init(contentVerifierBuilderProvider, signatureKey); + } else { + returnData.putBoolean(KeychainIntentService.RESULT_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(KeychainIntentService.RESULT_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)); + } + updateProgress(progress, 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(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, true); + } else { + returnData.putBoolean(KeychainIntentService.RESULT_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 + } + + updateProgress(progress, R.string.progress_done, 100, 100); + return returnData; + } + + public static Bundle verifyText(Context context, ProgressDialogUpdater progress, + InputData data, OutputStream outStream, boolean lookupUnknownKey) throws IOException, + PgpGeneralException, PGPException, SignatureException { + Bundle returnData = new Bundle(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArmoredInputStream aIn = new ArmoredInputStream(data.getInputStream()); + + updateProgress(progress, 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(KeychainIntentService.RESULT_SIGNATURE, true); + + updateProgress(progress, R.string.progress_processingSignature, 60, 100); + PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); + + PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); + if (sigList == null) { + throw new PgpGeneralException(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 = ProviderHelper.getPGPPublicKeyByKeyId(context, signature.getKeyID()); + if (signatureKeyId == 0) { + signatureKeyId = signature.getKeyID(); + } + // if key is not known and we want to lookup unknown ones... + if (signatureKey == null && lookupUnknownKey) { + + returnData = new Bundle(); + returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_LOOKUP_KEY, true); + + // return directly now, decrypt will be done again after importing unknown key + return returnData; + } + + if (signatureKey == null) { + signature = null; + } else { + signatureKeyId = signature.getKeyID(); + String userId = null; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context, + signatureKeyId); + if (signKeyRing != null) { + userId = PgpHelper.getMainUserId(PgpHelper.getMasterKey(signKeyRing)); + } + returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); + break; + } + } + + returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + + if (signature == null) { + returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); + if (progress != null) + progress.setProgress(R.string.progress_done, 100, 100); + return returnData; + } + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() + .setProvider(BOUNCY_CASTLE_PROVIDER_NAME); + + signature.init(contentVerifierBuilderProvider, signatureKey); + + 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(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, signature.verify()); + + updateProgress(progress, 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(Constants.PACKAGE_NAME, 0); + if (pi.versionCode % 100 == 99) { + return true; + } else { + return false; + } + } catch (NameNotFoundException e) { + // impossible! + return false; + } + } + + public static String getVersion(Context context) { + String version = null; + try { + PackageInfo pi = context.getPackageManager().getPackageInfo(Constants.PACKAGE_NAME, 0); + version = pi.versionName; + return version; + } catch (NameNotFoundException e) { + Log.e(Constants.TAG, "Version could not be retrieved!", e); + return "0.0.0"; + } + } + + public static String getFullVersion(Context context) { + return "APG v" + getVersion(context); + } + + /** + * Generate a random filename + * + * @param length + * @return + */ + public static String generateRandomFilename(int length) { + SecureRandom random = new SecureRandom(); + + 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; + } + + /** + * Go once through stream to get length of stream. The length is later used to display progress + * when encrypting/decrypting + * + * @param in + * @return + * @throws IOException + */ + public 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; + } + + /** + * Deletes file securely by overwriting it with random data before deleting it. + * + * TODO: Does this really help on flash storage? + * + * @param context + * @param progress + * @param file + * @throws FileNotFoundException + * @throws IOException + */ + public static void deleteFileSecurely(Context context, ProgressDialogUpdater progress, File file) + 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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/Preferences.java new file mode 100644 index 000000000..121bae6ef --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/Preferences.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.helper; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; + +import android.content.Context; +import android.content.SharedPreferences; + +import java.util.Vector; + +/** + * Singleton Implementation of a Preference Helper + */ +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 long 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 (long) 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 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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java new file mode 100644 index 000000000..3d4dddea5 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.provider; + +import org.sufficientlysecure.keychain.Constants; + +import android.net.Uri; +import android.provider.BaseColumns; + +public class KeychainContract { + + interface KeyRingsColumns { + String MASTER_KEY_ID = "master_key_id"; // not a database id + String TYPE = "type"; // see KeyTypes + String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob + } + + interface KeysColumns { + String KEY_ID = "key_id"; // not a database id + String TYPE = "type"; // see KeyTypes + String IS_MASTER_KEY = "is_master_key"; + String ALGORITHM = "algorithm"; + String KEY_SIZE = "key_size"; + String CAN_SIGN = "can_sign"; + String CAN_ENCRYPT = "can_encrypt"; + String IS_REVOKED = "is_revoked"; + String CREATION = "creation"; + String EXPIRY = "expiry"; + String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID + String KEY_DATA = "key_data"; // PGPPublicKey / PGPSecretKey blob + String RANK = "rank"; + } + + interface UserIdsColumns { + String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID + String USER_ID = "user_id"; // not a database id + String RANK = "rank"; + } + + public static final class KeyTypes { + public static final int PUBLIC = 0; + public static final int SECRET = 1; + } + + public static final String CONTENT_AUTHORITY_EXTERNAL = Constants.PACKAGE_NAME; + public static final String CONTENT_AUTHORITY_INTERNAL = Constants.PACKAGE_NAME + ".internal"; + + private static final Uri BASE_CONTENT_URI_INTERNAL = Uri.parse("content://" + + CONTENT_AUTHORITY_INTERNAL); + + public static final String BASE_KEY_RINGS = "key_rings"; + public static final String BASE_DATA = "data"; + + public static final String PATH_PUBLIC = "public"; + public static final String PATH_SECRET = "secret"; + + public static final String PATH_BY_MASTER_KEY_ID = "master_key_id"; + public static final String PATH_BY_KEY_ID = "key_id"; + public static final String PATH_BY_EMAILS = "emails"; + public static final String PATH_BY_LIKE_EMAIL = "like_email"; + + public static final String PATH_USER_IDS = "user_ids"; + public static final String PATH_KEYS = "keys"; + + public static class KeyRings implements KeyRingsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); + + /** Use if multiple items get returned */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring"; + + /** Use if a single item is returned */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring"; + + public static Uri buildPublicKeyRingsUri() { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build(); + } + + public static Uri buildPublicKeyRingsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId).build(); + } + + public static Uri buildPublicKeyRingsByMasterKeyIdUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC) + .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); + } + + public static Uri buildPublicKeyRingsByKeyIdUri(String keyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_KEY_ID) + .appendPath(keyId).build(); + } + + public static Uri buildPublicKeyRingsByEmailsUri(String emails) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_EMAILS) + .appendPath(emails).build(); + } + + public static Uri buildPublicKeyRingsByLikeEmailUri(String emails) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_LIKE_EMAIL) + .appendPath(emails).build(); + } + + public static Uri buildSecretKeyRingsUri() { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build(); + } + + public static Uri buildSecretKeyRingsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId).build(); + } + + public static Uri buildSecretKeyRingsByMasterKeyIdUri(String masterKeyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET) + .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build(); + } + + public static Uri buildSecretKeyRingsByKeyIdUri(String keyId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_KEY_ID) + .appendPath(keyId).build(); + } + + public static Uri buildSecretKeyRingsByEmailsUri(String emails) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS) + .appendPath(emails).build(); + } + + public static Uri buildSecretKeyRingsByLikeEmails(String emails) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_LIKE_EMAIL) + .appendPath(emails).build(); + } + } + + public static class Keys implements KeysColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); + + /** Use if multiple items get returned */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key"; + + /** Use if a single item is returned */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key"; + + public static Uri buildPublicKeysUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).build(); + } + + public static Uri buildPublicKeysUri(String keyRingRowId, String keyRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).appendPath(keyRowId).build(); + } + + public static Uri buildSecretKeysUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).build(); + } + + public static Uri buildSecretKeysUri(String keyRingRowId, String keyRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_KEYS).appendPath(keyRowId).build(); + } + } + + public static class UserIds implements UserIdsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_KEY_RINGS).build(); + + /** Use if multiple items get returned */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; + + /** Use if a single item is returned */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; + + public static Uri buildPublicUserIdsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).build(); + } + + public static Uri buildPublicUserIdsUri(String keyRingRowId, String userIdRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); + } + + public static Uri buildSecretUserIdsUri(String keyRingRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).build(); + } + + public static Uri buildSecretUserIdsUri(String keyRingRowId, String userIdRowId) { + return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId) + .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build(); + } + } + + public static class DataStream { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_DATA).build(); + + public static Uri buildDataStreamUri(String streamFilename) { + return CONTENT_URI.buildUpon().appendPath(streamFilename).build(); + } + } + + private KeychainContract() { + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java new file mode 100644 index 000000000..4a2aeeb80 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.provider; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.provider.BaseColumns; + + +public class KeychainDatabase extends SQLiteOpenHelper { + private static final String DATABASE_NAME = "apg.db"; + private static final int DATABASE_VERSION = 3; + + public interface Tables { + String KEY_RINGS = "key_rings"; + String KEYS = "keys"; + String USER_IDS = "user_ids"; + } + + private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + KeyRingsColumns.MASTER_KEY_ID + " INT64, " + KeyRingsColumns.TYPE + " INTEGER, " + + KeyRingsColumns.KEY_RING_DATA + " BLOB)"; + + private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KeysColumns.KEY_ID + + " INT64, " + KeysColumns.TYPE + " INTEGER, " + KeysColumns.IS_MASTER_KEY + + " INTEGER, " + KeysColumns.ALGORITHM + " INTEGER, " + KeysColumns.KEY_SIZE + + " INTEGER, " + KeysColumns.CAN_SIGN + " INTEGER, " + KeysColumns.CAN_ENCRYPT + + " INTEGER, " + KeysColumns.IS_REVOKED + " INTEGER, " + KeysColumns.CREATION + + " INTEGER, " + KeysColumns.EXPIRY + " INTEGER, " + KeysColumns.KEY_DATA + " BLOB," + + KeysColumns.RANK + " INTEGER, " + KeysColumns.KEY_RING_ROW_ID + + " INTEGER NOT NULL, FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)"; + + private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, " + + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" + + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" + + BaseColumns._ID + ") ON DELETE CASCADE)"; + + KeychainDatabase(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + Log.w(Constants.TAG, "Creating database..."); + + db.execSQL(CREATE_KEY_RINGS); + db.execSQL(CREATE_KEYS); + db.execSQL(CREATE_USER_IDS); + } + + @Override + public void onOpen(SQLiteDatabase db) { + super.onOpen(db); + if (!db.isReadOnly()) { + // Enable foreign key constraints + db.execSQL("PRAGMA foreign_keys=ON;"); + } + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion); + + // Upgrade from oldVersion through all methods to newest one + for (int version = oldVersion; version < newVersion; ++version) { + Log.w(Constants.TAG, "Upgrading database to version " + version); + + switch (version) { + + default: + break; + + } + } + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java new file mode 100644 index 000000000..9d531dceb --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -0,0 +1,852 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.provider; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.Arrays; +import java.util.HashMap; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteConstraintException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.provider.BaseColumns; +import android.text.TextUtils; + +public class KeychainProvider extends ContentProvider { + private static final int PUBLIC_KEY_RING = 101; + private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102; + private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103; + private static final int PUBLIC_KEY_RING_BY_KEY_ID = 104; + private static final int PUBLIC_KEY_RING_BY_EMAILS = 105; + private static final int PUBLIC_KEY_RING_BY_LIKE_EMAIL = 106; + + private static final int PUBLIC_KEY_RING_KEY = 111; + private static final int PUBLIC_KEY_RING_KEY_BY_ROW_ID = 112; + + private static final int PUBLIC_KEY_RING_USER_ID = 121; + private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122; + + private static final int SECRET_KEY_RING = 201; + private static final int SECRET_KEY_RING_BY_ROW_ID = 202; + private static final int SECRET_KEY_RING_BY_MASTER_KEY_ID = 203; + private static final int SECRET_KEY_RING_BY_KEY_ID = 204; + private static final int SECRET_KEY_RING_BY_EMAILS = 205; + private static final int SECRET_KEY_RING_BY_LIKE_EMAIL = 206; + + private static final int SECRET_KEY_RING_KEY = 211; + private static final int SECRET_KEY_RING_KEY_BY_ROW_ID = 212; + + private static final int SECRET_KEY_RING_USER_ID = 221; + private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; + + private static final int DATA_STREAM = 301; + + protected boolean mInternalProvider; + protected UriMatcher mUriMatcher; + + /** + * Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by + * this {@link ContentProvider}. + */ + protected UriMatcher buildUriMatcher(boolean internalProvider) { + final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); + + String authority; + if (internalProvider) { + authority = KeychainContract.CONTENT_AUTHORITY_INTERNAL; + } else { + authority = KeychainContract.CONTENT_AUTHORITY_EXTERNAL; + } + + /** + * public key rings + * + *
+         * key_rings/public
+         * key_rings/public/#
+         * key_rings/public/master_key_id/_
+         * key_rings/public/key_id/_
+         * key_rings/public/emails/_
+         * key_rings/public/like_email/_
+         * 
+ */ + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC, + PUBLIC_KEY_RING); + matcher.addURI(authority, + KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + "/#", + PUBLIC_KEY_RING_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + "/" + + KeychainContract.PATH_BY_MASTER_KEY_ID + "/*", PUBLIC_KEY_RING_BY_MASTER_KEY_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + "/" + + KeychainContract.PATH_BY_KEY_ID + "/*", PUBLIC_KEY_RING_BY_KEY_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + "/" + + KeychainContract.PATH_BY_EMAILS + "/*", PUBLIC_KEY_RING_BY_EMAILS); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + "/" + + KeychainContract.PATH_BY_EMAILS, PUBLIC_KEY_RING_BY_EMAILS); // without emails + // specified + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + "/" + + KeychainContract.PATH_BY_LIKE_EMAIL + "/*", PUBLIC_KEY_RING_BY_LIKE_EMAIL); + + /** + * public keys + * + *
+         * key_rings/public/#/keys
+         * key_rings/public/#/keys/#
+         * 
+ */ + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + + "/#/" + KeychainContract.PATH_KEYS, PUBLIC_KEY_RING_KEY); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + + "/#/" + KeychainContract.PATH_KEYS + "/#", PUBLIC_KEY_RING_KEY_BY_ROW_ID); + + /** + * public user ids + * + *
+         * key_rings/public/#/user_ids
+         * key_rings/public/#/user_ids/#
+         * 
+ */ + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + + "/#/" + KeychainContract.PATH_USER_IDS, PUBLIC_KEY_RING_USER_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + + "/#/" + KeychainContract.PATH_USER_IDS + "/#", PUBLIC_KEY_RING_USER_ID_BY_ROW_ID); + + /** + * secret key rings + * + *
+         * key_rings/secret
+         * key_rings/secret/#
+         * key_rings/secret/master_key_id/_
+         * key_rings/secret/key_id/_
+         * key_rings/secret/emails/_
+         * key_rings/secret/like_email/_
+         * 
+ */ + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET, + SECRET_KEY_RING); + matcher.addURI(authority, + KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + "/#", + SECRET_KEY_RING_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + "/" + + KeychainContract.PATH_BY_MASTER_KEY_ID + "/*", SECRET_KEY_RING_BY_MASTER_KEY_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + "/" + + KeychainContract.PATH_BY_KEY_ID + "/*", SECRET_KEY_RING_BY_KEY_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + "/" + + KeychainContract.PATH_BY_EMAILS + "/*", SECRET_KEY_RING_BY_EMAILS); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + "/" + + KeychainContract.PATH_BY_EMAILS, SECRET_KEY_RING_BY_EMAILS); // without emails + // specified + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + "/" + + KeychainContract.PATH_BY_LIKE_EMAIL + "/*", SECRET_KEY_RING_BY_LIKE_EMAIL); + + /** + * secret keys + * + *
+         * key_rings/secret/#/keys
+         * key_rings/secret/#/keys/#
+         * 
+ */ + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + + "/#/" + KeychainContract.PATH_KEYS, SECRET_KEY_RING_KEY); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + + "/#/" + KeychainContract.PATH_KEYS + "/#", SECRET_KEY_RING_KEY_BY_ROW_ID); + + /** + * secret user ids + * + *
+         * key_rings/secret/#/user_ids
+         * key_rings/secret/#/user_ids/#
+         * 
+ */ + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + + "/#/" + KeychainContract.PATH_USER_IDS, SECRET_KEY_RING_USER_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_SECRET + + "/#/" + KeychainContract.PATH_USER_IDS + "/#", SECRET_KEY_RING_USER_ID_BY_ROW_ID); + + /** + * data stream + * + *
+         * data / _
+         * 
+ */ + matcher.addURI(authority, KeychainContract.BASE_DATA + "/*", DATA_STREAM); + + return matcher; + } + + private KeychainDatabase mApgDatabase; + + /** {@inheritDoc} */ + @Override + public boolean onCreate() { + mUriMatcher = buildUriMatcher(mInternalProvider); + mApgDatabase = new KeychainDatabase(getContext()); + return true; + } + + /** {@inheritDoc} */ + @Override + public String getType(Uri uri) { + final int match = mUriMatcher.match(uri); + switch (match) { + case PUBLIC_KEY_RING: + case PUBLIC_KEY_RING_BY_EMAILS: + case PUBLIC_KEY_RING_BY_LIKE_EMAIL: + case SECRET_KEY_RING: + case SECRET_KEY_RING_BY_EMAILS: + case SECRET_KEY_RING_BY_LIKE_EMAIL: + return KeyRings.CONTENT_TYPE; + + case PUBLIC_KEY_RING_BY_ROW_ID: + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: + case SECRET_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_KEY_ID: + return KeyRings.CONTENT_ITEM_TYPE; + + case PUBLIC_KEY_RING_KEY: + case SECRET_KEY_RING_KEY: + return Keys.CONTENT_TYPE; + + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + return Keys.CONTENT_ITEM_TYPE; + + case PUBLIC_KEY_RING_USER_ID: + case SECRET_KEY_RING_USER_ID: + return UserIds.CONTENT_TYPE; + + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + return UserIds.CONTENT_ITEM_TYPE; + + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } + + /** + * Returns type of the query (secret/public) + * + * @param uri + * @return + */ + private int getKeyType(int match) { + int type; + switch (match) { + case PUBLIC_KEY_RING: + case PUBLIC_KEY_RING_BY_ROW_ID: + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: + case PUBLIC_KEY_RING_BY_EMAILS: + case PUBLIC_KEY_RING_BY_LIKE_EMAIL: + case PUBLIC_KEY_RING_KEY: + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case PUBLIC_KEY_RING_USER_ID: + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + type = KeyTypes.PUBLIC; + break; + + case SECRET_KEY_RING: + case SECRET_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_KEY_ID: + case SECRET_KEY_RING_BY_EMAILS: + case SECRET_KEY_RING_BY_LIKE_EMAIL: + case SECRET_KEY_RING_KEY: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + type = KeyTypes.SECRET; + break; + + default: + throw new IllegalArgumentException("Unknown match " + match); + + } + + return type; + } + + /** + * Set result of query to specific columns, don't show blob column for external content provider + * + * @return + */ + private HashMap getProjectionMapForKeyRings() { + HashMap projectionMap = new HashMap(); + + projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID); + projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + + KeyRingsColumns.MASTER_KEY_ID); + // only give out keyRing blob when we are using the internal content provider + if (mInternalProvider) { + projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "." + + KeyRingsColumns.KEY_RING_DATA); + } + projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); + + return projectionMap; + } + + /** + * Set result of query to specific columns, don't show blob column for external content provider + * + * @return + */ + private HashMap getProjectionMapForKeys() { + HashMap projectionMap = new HashMap(); + + projectionMap.put(BaseColumns._ID, BaseColumns._ID); + projectionMap.put(KeysColumns.KEY_ID, KeysColumns.KEY_ID); + projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY); + projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM); + projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE); + projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN); + projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT); + projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED); + projectionMap.put(KeysColumns.CREATION, KeysColumns.CREATION); + projectionMap.put(KeysColumns.EXPIRY, KeysColumns.EXPIRY); + projectionMap.put(KeysColumns.KEY_RING_ROW_ID, KeysColumns.KEY_RING_ROW_ID); + // only give out keyRing blob when we are using the internal content provider + if (mInternalProvider) { + projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA); + } + projectionMap.put(KeysColumns.RANK, KeysColumns.RANK); + + return projectionMap; + } + + /** + * Builds default query for keyRings: KeyRings table is joined with UserIds + * + * @param qb + * @param match + * @param isMasterKey + * @param sortOrder + * @return + */ + private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match, String sortOrder) { + // public or secret keyring + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + // join keyrings with userIds to every keyring + qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.USER_IDS + " ON " + "(" + + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." + + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." + + UserIdsColumns.RANK + " = '0')"); + + qb.setProjectionMap(getProjectionMapForKeyRings()); + + return qb; + } + + /** + * Builds default query for keyRings: KeyRings table is joined with Keys and UserIds + * + * @param qb + * @param match + * @param isMasterKey + * @param sortOrder + * @return + */ + private SQLiteQueryBuilder buildKeyRingQueryWithKeys(SQLiteQueryBuilder qb, int match, + String sortOrder) { + // public or secret keyring + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + // join keyrings with keys and userIds to every keyring + qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "(" + + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "." + + KeysColumns.KEY_RING_ROW_ID + ") " + " INNER JOIN " + Tables.USER_IDS + " ON " + + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." + + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "." + + UserIdsColumns.RANK + " = '0')"); + + qb.setProjectionMap(getProjectionMapForKeyRings()); + + return qb; + } + + /** {@inheritDoc} */ + @SuppressWarnings("deprecation") + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); + + SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); + SQLiteDatabase db = mApgDatabase.getReadableDatabase(); + + int match = mUriMatcher.match(uri); + + switch (match) { + case PUBLIC_KEY_RING: + case SECRET_KEY_RING: + qb = buildKeyRingQuery(qb, match, sortOrder); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case PUBLIC_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_ROW_ID: + qb = buildKeyRingQuery(qb, match, sortOrder); + + qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + qb = buildKeyRingQuery(qb, match, sortOrder); + + qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case SECRET_KEY_RING_BY_KEY_ID: + case PUBLIC_KEY_RING_BY_KEY_ID: + qb = buildKeyRingQueryWithKeys(qb, match, sortOrder); + + qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; + + case SECRET_KEY_RING_BY_EMAILS: + case PUBLIC_KEY_RING_BY_EMAILS: + qb = buildKeyRingQuery(qb, match, sortOrder); + + String emails = uri.getLastPathSegment(); + String chunks[] = emails.split(" *, *"); + boolean gotCondition = false; + String emailWhere = ""; + for (int i = 0; i < chunks.length; ++i) { + if (chunks[i].length() == 0) { + continue; + } + if (i != 0) { + emailWhere += " OR "; + } + emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE "; + // match '*', so it has to be at the *end* of the user id + emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); + gotCondition = true; + } + + if (gotCondition) { + qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " + + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID + + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere + + "))"); + } + + break; + + case SECRET_KEY_RING_BY_LIKE_EMAIL: + case PUBLIC_KEY_RING_BY_LIKE_EMAIL: + qb = buildKeyRingQuery(qb, match, sortOrder); + + String likeEmail = uri.getLastPathSegment(); + + String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE " + + DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>"); + + qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM " + + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID + + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere + + "))"); + + break; + + case PUBLIC_KEY_RING_KEY: + case SECRET_KEY_RING_KEY: + qb.setTables(Tables.KEYS); + qb.appendWhere(KeysColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + qb.setProjectionMap(getProjectionMapForKeys()); + + break; + + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + qb.setTables(Tables.KEYS); + qb.appendWhere(KeysColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + + qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + qb.appendWhere(" AND " + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + qb.setProjectionMap(getProjectionMapForKeys()); + + break; + + case PUBLIC_KEY_RING_USER_ID: + case SECRET_KEY_RING_USER_ID: + qb.setTables(Tables.USER_IDS); + qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + break; + + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + qb.setTables(Tables.USER_IDS); + qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + qb.appendWhere(" AND " + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + break; + + default: + throw new IllegalArgumentException("Unknown URI " + uri); + + } + + // If no sort order is specified use the default + String orderBy; + if (TextUtils.isEmpty(sortOrder)) { + orderBy = null; + } else { + orderBy = sortOrder; + } + + Cursor c = qb.query(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); + + if (Constants.DEBUG) { + Log.d(Constants.TAG, + "Query: " + + qb.buildQuery(projection, selection, selectionArgs, null, null, + orderBy, null)); + Log.d(Constants.TAG, "Cursor: " + DatabaseUtils.dumpCursorToString(c)); + } + + return c; + } + + /** {@inheritDoc} */ + @Override + public Uri insert(Uri uri, ContentValues values) { + Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); + + final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + + Uri rowUri = null; + long rowId = -1; + try { + final int match = mUriMatcher.match(uri); + + switch (match) { + case PUBLIC_KEY_RING: + values.put(KeyRings.TYPE, KeyTypes.PUBLIC); + + rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); + rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); + + break; + case PUBLIC_KEY_RING_KEY: + values.put(Keys.TYPE, KeyTypes.PUBLIC); + + rowId = db.insertOrThrow(Tables.KEYS, null, values); + rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); + + break; + case PUBLIC_KEY_RING_USER_ID: + rowId = db.insertOrThrow(Tables.USER_IDS, null, values); + rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); + + break; + case SECRET_KEY_RING: + values.put(KeyRings.TYPE, KeyTypes.SECRET); + + rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values); + rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); + + break; + case SECRET_KEY_RING_KEY: + values.put(Keys.TYPE, KeyTypes.SECRET); + + rowId = db.insertOrThrow(Tables.KEYS, null, values); + rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); + + break; + case SECRET_KEY_RING_USER_ID: + rowId = db.insertOrThrow(Tables.USER_IDS, null, values); + rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); + + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } catch (SQLiteConstraintException e) { + Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?"); + } + + // notify of changes in db + getContext().getContentResolver().notifyChange(uri, null); + + return rowUri; + } + + /** {@inheritDoc} */ + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + Log.v(Constants.TAG, "delete(uri=" + uri + ")"); + + final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + + int count; + final int match = mUriMatcher.match(uri); + + String defaultSelection = null; + switch (match) { + case PUBLIC_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_ROW_ID: + defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); + // corresponding keys and userIds are deleted by ON DELETE CASCADE + count = db.delete(Tables.KEY_RINGS, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), + selectionArgs); + break; + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); + // corresponding keys and userIds are deleted by ON DELETE CASCADE + count = db.delete(Tables.KEY_RINGS, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection), + selectionArgs); + break; + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + count = db.delete(Tables.KEYS, + buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs); + break; + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), + selectionArgs); + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + + // notify of changes in db + getContext().getContentResolver().notifyChange(uri, null); + + return count; + } + + /** {@inheritDoc} */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); + + final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + + String defaultSelection = null; + int count = 0; + try { + final int match = mUriMatcher.match(uri); + switch (match) { + case PUBLIC_KEY_RING_BY_ROW_ID: + case SECRET_KEY_RING_BY_ROW_ID: + defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment(); + + count = db.update( + Tables.KEY_RINGS, + values, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), + selection), selectionArgs); + break; + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: + case SECRET_KEY_RING_BY_MASTER_KEY_ID: + defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment(); + + count = db.update( + Tables.KEY_RINGS, + values, + buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), + selection), selectionArgs); + break; + case PUBLIC_KEY_RING_KEY_BY_ROW_ID: + case SECRET_KEY_RING_KEY_BY_ROW_ID: + count = db + .update(Tables.KEYS, values, + buildDefaultKeysSelection(uri, getKeyType(match), selection), + selectionArgs); + break; + case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: + case SECRET_KEY_RING_USER_ID_BY_ROW_ID: + count = db.update(Tables.USER_IDS, values, + buildDefaultUserIdsSelection(uri, selection), selectionArgs); + break; + default: + throw new UnsupportedOperationException("Unknown uri: " + uri); + } + } catch (SQLiteConstraintException e) { + Log.e(Constants.TAG, "Constraint exception on update! Entry already existing?"); + } + + // notify of changes in db + getContext().getContentResolver().notifyChange(uri, null); + + return count; + } + + /** + * Build default selection statement for KeyRings. If no extra selection is specified only build + * where clause with rowId + * + * @param uri + * @param selection + * @return + */ + private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType, + String selection) { + String andType = ""; + if (keyType != null) { + andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType; + } + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return defaultSelection + andType + andSelection; + } + + /** + * Build default selection statement for Keys. If no extra selection is specified only build + * where clause with rowId + * + * @param uri + * @param selection + * @return + */ + private String buildDefaultKeysSelection(Uri uri, Integer keyType, String selection) { + String rowId = uri.getLastPathSegment(); + + String foreignKeyRingRowId = uri.getPathSegments().get(2); + String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " + + foreignKeyRingRowId; + + String andType = ""; + if (keyType != null) { + andType = " AND " + KeysColumns.TYPE + "=" + keyType; + } + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andType + andSelection; + } + + /** + * Build default selection statement for UserIds. If no extra selection is specified only build + * where clause with rowId + * + * @param uri + * @param selection + * @return + */ + private String buildDefaultUserIdsSelection(Uri uri, String selection) { + String rowId = uri.getLastPathSegment(); + + String foreignKeyRingRowId = uri.getPathSegments().get(2); + String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = " + + foreignKeyRingRowId; + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection; + } + + @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.getLastPathSegment(); + File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName); + return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderExternal.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderExternal.java new file mode 100644 index 000000000..a62b85621 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderExternal.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.provider; + +/** + * The same content provider as ApgProviderInternal except that it does not give out keyRing and key + * blob data when querying. + * + * This provider is exported with a readPermission in AndroidManifest.xml + */ +public class KeychainProviderExternal extends KeychainProvider { + + public KeychainProviderExternal() { + mInternalProvider = false; + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderInternal.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderInternal.java new file mode 100644 index 000000000..ffac2f7cc --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainProviderInternal.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.provider; + +/** + * This provider is NOT exported in AndroidManifest.xml as it also return the actual secret keys + * from the database + */ +public class KeychainProviderInternal extends KeychainProvider { + + public KeychainProviderInternal() { + mInternalProvider = true; + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java new file mode 100644 index 000000000..340d3c788 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.provider; + +import org.sufficientlysecure.keychain.Constants; + +import android.net.Uri; +import android.provider.BaseColumns; + +public class KeychainServiceBlobContract { + + interface BlobsColumns { + String KEY = "key"; + } + + public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".blobs"; + + private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); + + public static class Blobs implements BlobsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI; + } + + private KeychainServiceBlobContract() { + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java new file mode 100644 index 000000000..386349c0e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.provider; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.provider.BaseColumns; + +import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns; + +public class KeychainServiceBlobDatabase extends SQLiteOpenHelper { + private static final String DATABASE_NAME = "apg_blob.db"; + private static final int DATABASE_VERSION = 2; + + public static final String TABLE = "data"; + + public KeychainServiceBlobDatabase(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + TABLE + " ( " + BaseColumns._ID + + " INTEGER PRIMARY KEY AUTOINCREMENT, " + BlobsColumns.KEY + " TEXT NOT NULL)"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // no upgrade necessary yet + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java new file mode 100644 index 000000000..761bfb2fe --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.provider; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.Blobs; +import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.net.Uri; +import android.os.ParcelFileDescriptor; +import android.provider.BaseColumns; + + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +public class KeychainServiceBlobProvider extends ContentProvider { + private static final String STORE_PATH = Constants.path.APP_DIR + "/ApgBlobs"; + + private KeychainServiceBlobDatabase mBlobDatabase = null; + + public KeychainServiceBlobProvider() { + File dir = new File(STORE_PATH); + dir.mkdirs(); + } + + @Override + public boolean onCreate() { + mBlobDatabase = new KeychainServiceBlobDatabase(getContext()); + return true; + } + + /** {@inheritDoc} */ + @Override + public Uri insert(Uri uri, ContentValues ignored) { + // 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(BlobsColumns.KEY, password); + + SQLiteDatabase db = mBlobDatabase.getWritableDatabase(); + long newRowId = db.insert(KeychainServiceBlobDatabase.TABLE, null, vals); + Uri insertedUri = ContentUris.withAppendedId(Blobs.CONTENT_URI, newRowId); + + return Uri.withAppendedPath(insertedUri, password); + } + + /** {@inheritDoc} */ + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, + FileNotFoundException { + Log.d(Constants.TAG, "openFile() called with uri: " + uri.toString() + " and 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); + + Log.d(Constants.TAG, "Got id: " + id + " and key: " + key); + + // get the data + SQLiteDatabase db = mBlobDatabase.getReadableDatabase(); + Cursor result = db.query(KeychainServiceBlobDatabase.TABLE, new String[] { BaseColumns._ID }, + BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?", + new String[] { id, key }, null, null, null); + + 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")) { + Log.d(Constants.TAG, "Try to open file w"); + if (!targetFile.exists()) { + try { + targetFile.createNewFile(); + } catch (IOException e) { + Log.e(Constants.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")) { + Log.d(Constants.TAG, "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; + } + + /** {@inheritDoc} */ + @Override + public String getType(Uri uri) { + return null; + } + + /** {@inheritDoc} */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + /** {@inheritDoc} */ + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + /** {@inheritDoc} */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java new file mode 100644 index 000000000..36049a64c --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -0,0 +1,663 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.provider; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.PgpConversionHelper; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; + +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.RemoteException; + +public class ProviderHelper { + + /** + * Private helper method to get PGPKeyRing from database + * + * @param context + * @param queryUri + * @return + */ + private static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) { + Cursor cursor = context.getContentResolver().query(queryUri, + new String[] { KeyRings._ID, KeyRings.KEY_RING_DATA }, null, null, null); + + PGPKeyRing keyRing = null; + if (cursor != null && cursor.moveToFirst()) { + int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); + + byte[] data = cursor.getBlob(keyRingDataCol); + if (data != null) { + keyRing = PgpConversionHelper.BytesToPGPKeyRing(data); + } + } + + if (cursor != null) { + cursor.close(); + } + + return keyRing; + } + + /** + * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId + * + * @param context + * @param rowId + * @return + */ + public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) { + Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)); + return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId + * + * @param context + * @param masterKeyId + * @return + */ + public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context, + long masterKeyId) { + Uri queryUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key + * with this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) { + Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId)); + return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPPublicKey object from the database blob associated with a key with + * this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) { + PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId); + if (keyRing == null) { + return null; + } + + return keyRing.getPublicKey(keyId); + } + + /** + * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId + * + * @param context + * @param rowId + * @return + */ + public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); + return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId + * + * @param context + * @param masterKeyId + * @return + */ + public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context, + long masterKeyId) { + Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key + * with this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) { + Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId)); + return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri); + } + + /** + * Retrieves the actual PGPSecretKey object from the database blob associated with a key with + * this keyId + * + * @param context + * @param keyId + * @return + */ + public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) { + PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId); + if (keyRing == null) { + return null; + } + + return keyRing.getSecretKey(keyId); + } + + /** + * Saves PGPPublicKeyRing with its keys and userIds in DB + * + * @param context + * @param keyRing + * @return + * @throws IOException + * @throws GeneralException + */ + @SuppressWarnings("unchecked") + public static void saveKeyRing(Context context, PGPPublicKeyRing keyRing) throws IOException { + PGPPublicKey masterKey = keyRing.getPublicKey(); + long masterKeyId = masterKey.getKeyID(); + + // delete old version of this keyRing, which also deletes all keys and userIds on cascade + Uri deleteUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + + try { + context.getContentResolver().delete(deleteUri, null, null); + } catch (UnsupportedOperationException e) { + Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + } + + ContentValues values = new ContentValues(); + values.put(KeyRings.MASTER_KEY_ID, masterKeyId); + values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); + + // insert new version of this keyRing + Uri uri = KeyRings.buildPublicKeyRingsUri(); + Uri insertedUri = context.getContentResolver().insert(uri, values); + long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); + + // save all keys and userIds included in keyRing object in database + ArrayList operations = new ArrayList(); + + int rank = 0; + for (PGPPublicKey key : new IterableIterator(keyRing.getPublicKeys())) { + operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank)); + ++rank; + } + + int userIdRank = 0; + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank)); + ++userIdRank; + } + + try { + context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY_INTERNAL, + operations); + } catch (RemoteException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } catch (OperationApplicationException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } + } + + /** + * Saves PGPSecretKeyRing with its keys and userIds in DB + * + * @param context + * @param keyRing + * @return + * @throws IOException + * @throws GeneralException + */ + @SuppressWarnings("unchecked") + public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException { + PGPSecretKey masterKey = keyRing.getSecretKey(); + long masterKeyId = masterKey.getKeyID(); + + // delete old version of this keyRing, which also deletes all keys and userIds on cascade + Uri deleteUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + + try { + context.getContentResolver().delete(deleteUri, null, null); + } catch (UnsupportedOperationException e) { + Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e); + } + + ContentValues values = new ContentValues(); + values.put(KeyRings.MASTER_KEY_ID, masterKeyId); + values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); + + // insert new version of this keyRing + Uri uri = KeyRings.buildSecretKeyRingsUri(); + Uri insertedUri = context.getContentResolver().insert(uri, values); + long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment()); + + // save all keys and userIds included in keyRing object in database + ArrayList operations = new ArrayList(); + + int rank = 0; + for (PGPSecretKey key : new IterableIterator(keyRing.getSecretKeys())) { + operations.add(buildSecretKeyOperations(context, keyRingRowId, key, rank)); + ++rank; + } + + int userIdRank = 0; + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + operations.add(buildSecretUserIdOperations(context, keyRingRowId, userId, userIdRank)); + ++userIdRank; + } + + try { + context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY_INTERNAL, + operations); + } catch (RemoteException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } catch (OperationApplicationException e) { + Log.e(Constants.TAG, "applyBatch failed!", e); + } + } + + /** + * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildPublicKeyOperations(Context context, + long keyRingRowId, PGPPublicKey key, int rank) throws IOException { + ContentValues values = new ContentValues(); + values.put(Keys.KEY_ID, key.getKeyID()); + 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, PgpHelper.isSigningKey(key)); + values.put(Keys.CAN_ENCRYPT, PgpHelper.isEncryptionKey(key)); + values.put(Keys.IS_REVOKED, key.isRevoked()); + values.put(Keys.CREATION, PgpHelper.getCreationDate(key).getTime() / 1000); + Date expiryDate = PgpHelper.getExpiryDate(key); + if (expiryDate != null) { + values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); + } + values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); + values.put(Keys.KEY_DATA, key.getEncoded()); + values.put(Keys.RANK, rank); + + Uri uri = Keys.buildPublicKeysUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildPublicUserIdOperations(Context context, + long keyRingRowId, String userId, int rank) { + ContentValues values = new ContentValues(); + values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); + values.put(UserIds.USER_ID, userId); + values.put(UserIds.RANK, rank); + + Uri uri = UserIds.buildPublicUserIdsUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildSecretKeyOperations(Context context, + long keyRingRowId, PGPSecretKey key, int rank) throws IOException { + ContentValues values = new ContentValues(); + values.put(Keys.KEY_ID, key.getKeyID()); + 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, PgpHelper.isSigningKey(key)); + values.put(Keys.CAN_ENCRYPT, PgpHelper.isEncryptionKey(key)); + values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); + values.put(Keys.CREATION, PgpHelper.getCreationDate(key).getTime() / 1000); + Date expiryDate = PgpHelper.getExpiryDate(key); + if (expiryDate != null) { + values.put(Keys.EXPIRY, expiryDate.getTime() / 1000); + } + values.put(Keys.KEY_RING_ROW_ID, keyRingRowId); + values.put(Keys.KEY_DATA, key.getEncoded()); + values.put(Keys.RANK, rank); + + Uri uri = Keys.buildSecretKeysUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing + * + * @param context + * @param keyRingRowId + * @param key + * @param rank + * @return + * @throws IOException + */ + private static ContentProviderOperation buildSecretUserIdOperations(Context context, + long keyRingRowId, String userId, int rank) { + ContentValues values = new ContentValues(); + values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); + values.put(UserIds.USER_ID, userId); + values.put(UserIds.RANK, rank); + + Uri uri = UserIds.buildSecretUserIdsUri(Long.toString(keyRingRowId)); + + return ContentProviderOperation.newInsert(uri).withValues(values).build(); + } + + /** + * Private helper method + * + * @param context + * @param queryUri + * @return + */ + private static ArrayList getKeyRingsMasterKeyIds(Context context, Uri queryUri) { + Cursor cursor = context.getContentResolver().query(queryUri, + new String[] { KeyRings.MASTER_KEY_ID }, null, null, null); + + ArrayList masterKeyIds = new ArrayList(); + if (cursor != null) { + int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); + if (cursor.moveToFirst()) { + do { + masterKeyIds.add(cursor.getLong(masterKeyIdCol)); + } while (cursor.moveToNext()); + } + } + + if (cursor != null) { + cursor.close(); + } + + return masterKeyIds; + } + + /** + * Retrieves ids of all SecretKeyRings + * + * @param context + * @return + */ + public static ArrayList getSecretKeyRingsMasterKeyIds(Context context) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(); + return getKeyRingsMasterKeyIds(context, queryUri); + } + + /** + * Retrieves ids of all PublicKeyRings + * + * @param context + * @return + */ + public static ArrayList getPublicKeyRingsMasterKeyIds(Context context) { + Uri queryUri = KeyRings.buildPublicKeyRingsUri(); + return getKeyRingsMasterKeyIds(context, queryUri); + } + + public static void deletePublicKeyRing(Context context, long rowId) { + ContentResolver cr = context.getContentResolver(); + cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null); + } + + public static void deleteSecretKeyRing(Context context, long rowId) { + ContentResolver cr = context.getContentResolver(); + cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); + } + + /** + * Get master key id of keyring by its row id + * + * @param context + * @param keyRingRowId + * @return + */ + public static long getPublicMasterKeyId(Context context, long keyRingRowId) { + Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId)); + return getMasterKeyId(context, queryUri, keyRingRowId); + } + + /** + * Get master key id of keyring by its row id + * + * @param context + * @param keyRingRowId + * @return + */ + public static long getSecretMasterKeyId(Context context, long keyRingRowId) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); + return getMasterKeyId(context, queryUri, keyRingRowId); + } + + /** + * Private helper method to get master key id of keyring by its row id + * + * @param context + * @param queryUri + * @param keyRingRowId + * @return + */ + private static long getMasterKeyId(Context context, Uri queryUri, long keyRingRowId) { + String[] projection = new String[] { KeyRings.MASTER_KEY_ID }; + + ContentResolver cr = context.getContentResolver(); + Cursor cursor = cr.query(queryUri, projection, null, null, null); + + long masterKeyId = -1; + if (cursor != null && cursor.moveToFirst()) { + int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); + + masterKeyId = cursor.getLong(masterKeyIdCol); + } + + if (cursor != null) { + cursor.close(); + } + + return masterKeyId; + } + + public static ArrayList getPublicKeyRingsAsArmoredString(Context context, + long[] masterKeyIds) { + return getKeyRingsAsArmoredString(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds); + } + + public static ArrayList getSecretKeyRingsAsArmoredString(Context context, + long[] masterKeyIds) { + return getKeyRingsAsArmoredString(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds); + } + + private static ArrayList getKeyRingsAsArmoredString(Context context, Uri uri, + long[] masterKeyIds) { + ArrayList output = new ArrayList(); + + if (masterKeyIds != null && masterKeyIds.length > 0) { + + Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds); + + if (cursor != null) { + int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); + int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); + if (cursor.moveToFirst()) { + do { + Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol)); + + // get actual keyring data blob and write it to ByteArrayOutputStream + try { + Object keyRing = null; + byte[] data = cursor.getBlob(dataCol); + if (data != null) { + keyRing = PgpConversionHelper.BytesToPGPKeyRing(data); + } + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ArmoredOutputStream aos = new ArmoredOutputStream(bos); + aos.setHeader("Version", PgpMain.getFullVersion(context)); + + if (keyRing instanceof PGPSecretKeyRing) { + aos.write(((PGPSecretKeyRing) keyRing).getEncoded()); + } else if (keyRing instanceof PGPPublicKeyRing) { + aos.write(((PGPPublicKeyRing) keyRing).getEncoded()); + } + aos.close(); + + String armoredKey = bos.toString("UTF-8"); + + Log.d(Constants.TAG, "armouredKey:" + armoredKey); + + output.add(armoredKey); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException", e); + } + } while (cursor.moveToNext()); + } + } + + if (cursor != null) { + cursor.close(); + } + + } else { + Log.e(Constants.TAG, "No master keys given!"); + } + + if (output.size() > 0) { + return output; + } else { + return null; + } + } + + public static byte[] getPublicKeyRingsAsByteArray(Context context, long[] masterKeyIds) { + return getKeyRingsAsByteArray(context, KeyRings.buildPublicKeyRingsUri(), masterKeyIds); + } + + public static byte[] getSecretKeyRingsAsByteArray(Context context, long[] masterKeyIds) { + return getKeyRingsAsByteArray(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds); + } + + private static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + if (masterKeyIds != null && masterKeyIds.length > 0) { + + Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds); + + if (cursor != null) { + int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID); + int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA); + if (cursor.moveToFirst()) { + do { + Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol)); + + // get actual keyring data blob and write it to ByteArrayOutputStream + try { + bos.write(cursor.getBlob(dataCol)); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException", e); + } + } while (cursor.moveToNext()); + } + } + + if (cursor != null) { + cursor.close(); + } + + } else { + Log.e(Constants.TAG, "No master keys given!"); + } + + return bos.toByteArray(); + } + + private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, Uri baseUri, + long[] masterKeyIds) { + Cursor cursor = null; + if (masterKeyIds != null && masterKeyIds.length > 0) { + + String inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN ("; + for (int i = 0; i < masterKeyIds.length; ++i) { + if (i != 0) { + inMasterKeyList += ", "; + } + inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]); + } + inMasterKeyList += ")"; + + cursor = context.getContentResolver().query(baseUri, + new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA }, + inMasterKeyList, null, null); + } + + return cursor; + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainApiService.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainApiService.aidl new file mode 100644 index 000000000..ac8327b8e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainApiService.aidl @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service; + +import org.sufficientlysecure.keychain.service.handler.IKeychainEncryptHandler; +import org.sufficientlysecure.keychain.service.handler.IKeychainDecryptHandler; +import org.sufficientlysecure.keychain.service.handler.IKeychainGetDecryptionKeyIdHandler; + +/** + * All methods are oneway, which means they are asynchronous and non-blocking. + * Results are returned into given Handler, which has to be implemented on client side. + */ +interface IKeychainApiService { + + /** + * Encrypt + * + * Either inputBytes or inputUri is given, the other should be null. + * + * @param inputBytes + * Byte array you want to encrypt + * @param inputUri + * Blob in ContentProvider you want to encrypt + * @param useAsciiArmor + * Convert bytes to ascii armored text to guard against encoding problems + * @param compression + * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 + * @param encryptionKeyIds + * Ids of public keys used for encryption + * @param symmetricEncryptionAlgorithm + * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, + * 6: DES, 2: Triple DES, 1: IDEA + * @param handler + * Results are returned to this IKeychainEncryptDecryptHandler Handler + * to onSuccessEncrypt(in byte[] output), after successful encryption + */ + oneway void encryptAsymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, + in int compression, in long[] encryptionKeyIds, in int symmetricEncryptionAlgorithm, + in IKeychainEncryptHandler handler); + + /** + * Same as encryptAsymmetric but using a passphrase for symmetric encryption + * + * @param encryptionPassphrase + * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm + */ + oneway void encryptSymmetric(in byte[] inputBytes, in String inputUri, in boolean useAsciiArmor, + in int compression, in String encryptionPassphrase, in int symmetricEncryptionAlgorithm, + in IKeychainEncryptHandler handler); + + /** + * Encrypt and sign + * + * Either inputBytes or inputUri is given, the other should be null. + * + * @param inputBytes + * Byte array you want to encrypt + * @param inputUri + * Blob in ContentProvider you want to encrypt + * @param useAsciiArmor + * Convert bytes to ascii armored text to guard against encoding problems + * @param compression + * Compression: 0x21070001: none, 1: Zip, 2: Zlib, 3: BZip2 + * @param encryptionKeyIds + * Ids of public keys used for encryption + * @param symmetricEncryptionAlgorithm + * 7: AES-128, 8: AES-192, 9: AES-256, 4: Blowfish, 10: Twofish, 3: CAST5, + * 6: DES, 2: Triple DES, 1: IDEA + * @param signatureKeyId + * Key id of key to sign with + * @param signatureHashAlgorithm + * 1: MD5, 3: RIPEMD-160, 2: SHA-1, 11: SHA-224, 8: SHA-256, 9: SHA-384, + * 10: SHA-512 + * @param signatureForceV3 + * Force V3 signatures + * @param signaturePassphrase + * Passphrase to unlock signature key + * @param handler + * Results are returned to this IKeychainEncryptDecryptHandler Handler + * to onSuccessEncrypt(in byte[] output), after successful encryption and signing + */ + oneway void encryptAndSignAsymmetric(in byte[] inputBytes, in String inputUri, + in boolean useAsciiArmor, in int compression, in long[] encryptionKeyIds, + in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, + in boolean signatureForceV3, in String signaturePassphrase, + in IKeychainEncryptHandler handler); + + /** + * Same as encryptAndSignAsymmetric but using a passphrase for symmetric encryption + * + * @param encryptionPassphrase + * Passphrase for direct symmetric encryption using symmetricEncryptionAlgorithm + */ + oneway void encryptAndSignSymmetric(in byte[] inputBytes, in String inputUri, + in boolean useAsciiArmor, in int compression, in String encryptionPassphrase, + in int symmetricEncryptionAlgorithm, in long signatureKeyId, in int signatureHashAlgorithm, + in boolean signatureForceV3, in String signaturePassphrase, + in IKeychainEncryptHandler handler); + + /** + * Decrypts and verifies given input bytes. If no signature is present this method + * will only decrypt. + * + * @param inputBytes + * Byte array you want to decrypt and verify + * @param inputUri + * Blob in ContentProvider you want to decrypt and verify + * @param keyPassphrase + * Passphrase to unlock secret key for decryption. + * @param handler + * Handler where to return results to after successful encryption + */ + oneway void decryptAndVerifyAsymmetric(in byte[] inputBytes, in String inputUri, + in String keyPassphrase, in IKeychainDecryptHandler handler); + + /** + * Same as decryptAndVerifyAsymmetric but for symmetric decryption. + * + * @param encryptionPassphrase + * Passphrase to decrypt + */ + oneway void decryptAndVerifySymmetric(in byte[] inputBytes, in String inputUri, + in String encryptionPassphrase, in IKeychainDecryptHandler handler); + + /** + * + */ + oneway void getDecryptionKeyId(in byte[] inputBytes, in String inputUri, + in IKeychainGetDecryptionKeyIdHandler handler); + + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl new file mode 100644 index 000000000..ecea2b8ff --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/IKeychainKeyService.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service; + +import org.sufficientlysecure.keychain.service.handler.IKeychainGetKeyringsHandler; + +/** + * All methods are oneway, which means they are asynchronous and non-blocking. + * Results are returned into given Handler, which has to be implemented on client side. + */ +interface IKeychainKeyService { + + oneway void getPublicKeyRings(in long[] masterKeyIds, in boolean asAsciiArmoredStringArray, + in IKeychainGetKeyringsHandler handler); + + oneway void getSecretKeyRings(in long[] masterKeyIds, in boolean asAsciiArmoredStringArray, + in IKeychainGetKeyringsHandler handler); +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainApiService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainApiService.java new file mode 100644 index 000000000..7c70c3c68 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainApiService.java @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SignatureException; + +import org.spongycastle.openpgp.PGPException; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.helper.PgpMain.PgpGeneralException; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.IKeychainApiService; +import org.sufficientlysecure.keychain.service.handler.IKeychainDecryptHandler; +import org.sufficientlysecure.keychain.service.handler.IKeychainEncryptHandler; +import org.sufficientlysecure.keychain.service.handler.IKeychainGetDecryptionKeyIdHandler; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; + +public class KeychainApiService extends Service { + Context mContext; + + @Override + public void onCreate() { + super.onCreate(); + mContext = this; + Log.d(Constants.TAG, "KeychainApiService, onCreate()"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(Constants.TAG, "KeychainApiService, onDestroy()"); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + // 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 synchronized void encryptAndSignSafe(byte[] inputBytes, String inputUri, + boolean useAsciiArmor, int compression, long[] encryptionKeyIds, + String encryptionPassphrase, int symmetricEncryptionAlgorithm, long signatureKeyId, + int signatureHashAlgorithm, boolean signatureForceV3, String signaturePassphrase, + IKeychainEncryptHandler handler) throws RemoteException { + + try { + // TODO use inputUri + + // 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? + + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData input = new InputData(inputStream, inputLength); + + OutputStream output = new ByteArrayOutputStream(); + + PgpMain.encryptAndSign(mContext, null, input, output, useAsciiArmor, compression, + encryptionKeyIds, encryptionPassphrase, symmetricEncryptionAlgorithm, + signatureKeyId, signatureHashAlgorithm, signatureForceV3, signaturePassphrase); + + output.close(); + + // 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()); + // } + + byte[] outputBytes = ((ByteArrayOutputStream) output).toByteArray(); + + // return over handler on client side + handler.onSuccess(outputBytes, null); + } catch (Exception e) { + Log.e(Constants.TAG, "KeychainService, Exception!", e); + + try { + handler.onException(getExceptionId(e), e.getMessage()); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + private synchronized void decryptAndVerifySafe(byte[] inputBytes, String inputUri, + String passphrase, boolean assumeSymmetric, IKeychainDecryptHandler handler) + throws RemoteException { + + try { + // build InputData and write into OutputStream + InputStream inputStream = new ByteArrayInputStream(inputBytes); + long inputLength = inputBytes.length; + InputData inputData = new InputData(inputStream, inputLength); + + OutputStream outputStream = new ByteArrayOutputStream(); + + Bundle outputBundle = PgpMain.decryptAndVerify(mContext, null, inputData, outputStream, + passphrase, assumeSymmetric); + + outputStream.close(); + + byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray(); + + // get signature informations from bundle + boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE); + long signatureKeyId = outputBundle + .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID); + String signatureUserId = outputBundle + .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID); + boolean signatureSuccess = outputBundle + .getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS); + boolean signatureUnknown = outputBundle + .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN); + + // return over handler on client side + handler.onSuccess(outputBytes, null, signature, signatureKeyId, signatureUserId, + signatureSuccess, signatureUnknown); + } catch (Exception e) { + Log.e(Constants.TAG, "KeychainService, Exception!", e); + + try { + handler.onException(getExceptionId(e), e.getMessage()); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + private synchronized void getDecryptionKeySafe(byte[] inputBytes, String inputUri, + IKeychainGetDecryptionKeyIdHandler handler) { + + // TODO: implement inputUri + + try { + InputStream inputStream = new ByteArrayInputStream(inputBytes); + + long secretKeyId = Id.key.none; + boolean symmetric; + + try { + secretKeyId = PgpMain.getDecryptionKeyId(KeychainApiService.this, inputStream); + if (secretKeyId == Id.key.none) { + throw new PgpGeneralException(getString(R.string.error_noSecretKeyFound)); + } + symmetric = false; + } catch (PgpMain.NoAsymmetricEncryptionException e) { + secretKeyId = Id.key.symmetric; + if (!PgpMain.hasSymmetricEncryption(KeychainApiService.this, inputStream)) { + throw new PgpGeneralException(getString(R.string.error_noKnownEncryptionFound)); + } + symmetric = true; + } + + handler.onSuccess(secretKeyId, symmetric); + + } catch (Exception e) { + Log.e(Constants.TAG, "KeychainService, Exception!", e); + + try { + handler.onException(getExceptionId(e), e.getMessage()); + } catch (Exception t) { + Log.e(Constants.TAG, "Error returning exception to client", t); + } + } + } + + /** + * This is the implementation of the interface IKeychainService. All methods are oneway, meaning + * asynchronous and return to the client using IKeychainHandler. + * + * The real PGP code is located in PGPMain. + */ + private final IKeychainApiService.Stub mBinder = new IKeychainApiService.Stub() { + + @Override + public void encryptAsymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, + int compression, long[] encryptionKeyIds, int symmetricEncryptionAlgorithm, + IKeychainEncryptHandler handler) throws RemoteException { + + encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, encryptionKeyIds, + null, symmetricEncryptionAlgorithm, Id.key.none, 0, false, null, handler); + } + + @Override + public void encryptSymmetric(byte[] inputBytes, String inputUri, boolean useAsciiArmor, + int compression, String encryptionPassphrase, int symmetricEncryptionAlgorithm, + IKeychainEncryptHandler handler) throws RemoteException { + + encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, null, + encryptionPassphrase, symmetricEncryptionAlgorithm, Id.key.none, 0, false, + null, handler); + } + + @Override + public void encryptAndSignAsymmetric(byte[] inputBytes, String inputUri, + boolean useAsciiArmor, int compression, long[] encryptionKeyIds, + int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, + boolean signatureForceV3, String signaturePassphrase, + IKeychainEncryptHandler handler) throws RemoteException { + + encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, encryptionKeyIds, + null, symmetricEncryptionAlgorithm, signatureKeyId, signatureHashAlgorithm, + signatureForceV3, signaturePassphrase, handler); + } + + @Override + public void encryptAndSignSymmetric(byte[] inputBytes, String inputUri, + boolean useAsciiArmor, int compression, String encryptionPassphrase, + int symmetricEncryptionAlgorithm, long signatureKeyId, int signatureHashAlgorithm, + boolean signatureForceV3, String signaturePassphrase, + IKeychainEncryptHandler handler) throws RemoteException { + + encryptAndSignSafe(inputBytes, inputUri, useAsciiArmor, compression, null, + encryptionPassphrase, symmetricEncryptionAlgorithm, signatureKeyId, + signatureHashAlgorithm, signatureForceV3, signaturePassphrase, handler); + } + + @Override + public void decryptAndVerifyAsymmetric(byte[] inputBytes, String inputUri, + String keyPassphrase, IKeychainDecryptHandler handler) throws RemoteException { + + decryptAndVerifySafe(inputBytes, inputUri, keyPassphrase, false, handler); + } + + @Override + public void decryptAndVerifySymmetric(byte[] inputBytes, String inputUri, + String encryptionPassphrase, IKeychainDecryptHandler handler) + throws RemoteException { + + decryptAndVerifySafe(inputBytes, inputUri, encryptionPassphrase, true, handler); + } + + @Override + public void getDecryptionKeyId(byte[] inputBytes, String inputUri, + IKeychainGetDecryptionKeyIdHandler handler) throws RemoteException { + + getDecryptionKeySafe(inputBytes, inputUri, handler); + } + + }; + + /** + * As we can not throw an exception through Android RPC, we assign identifiers to the exception + * types. + * + * @param e + * @return + */ + private int getExceptionId(Exception e) { + if (e instanceof NoSuchProviderException) { + return 0; + } else if (e instanceof NoSuchAlgorithmException) { + return 1; + } else if (e instanceof SignatureException) { + return 2; + } else if (e instanceof IOException) { + return 3; + } else if (e instanceof PgpGeneralException) { + return 4; + } else if (e instanceof PGPException) { + return 5; + } else { + return -1; + } + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java new file mode 100644 index 000000000..e525fe96e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -0,0 +1,884 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.PgpConversionHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.helper.PgpMain.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream; +import org.sufficientlysecure.keychain.util.HkpKeyServer; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; +import org.sufficientlysecure.keychain.util.KeyServer.KeyInfo; +import org.sufficientlysecure.keychain.R; + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + + +/** + * This Service contains all important long lasting operations for APG. It receives Intents with + * data from the activities or other apps, queues these intents, executes them, and stops itself + * after doing them. + */ +public class KeychainIntentService extends IntentService implements ProgressDialogUpdater { + + /* extras that can be given by intent */ + public static final String EXTRA_MESSENGER = "messenger"; + public static final String EXTRA_ACTION = "action"; + public static final String EXTRA_DATA = "data"; + + /* possible EXTRA_ACTIONs */ + public static final int ACTION_ENCRYPT_SIGN = 10; + + public static final int ACTION_DECRYPT_VERIFY = 20; + + public static final int ACTION_SAVE_KEYRING = 30; + public static final int ACTION_GENERATE_KEY = 31; + public static final int ACTION_GENERATE_DEFAULT_RSA_KEYS = 32; + + public static final int ACTION_DELETE_FILE_SECURELY = 40; + + public static final int ACTION_IMPORT_KEYRING = 50; + public static final int ACTION_EXPORT_KEYRING = 51; + + public static final int ACTION_UPLOAD_KEYRING = 60; + public static final int ACTION_QUERY_KEYRING = 61; + + public static final int ACTION_SIGN_KEYRING = 70; + + /* keys for data bundle */ + + // encrypt, decrypt, import export + public static final String TARGET = "target"; + // possible targets: + public static final int TARGET_BYTES = 1; + public static final int TARGET_FILE = 2; + public static final int TARGET_STREAM = 3; + + // encrypt + public static final String ENCRYPT_SECRET_KEY_ID = "secretKeyId"; + public static final String ENCRYPT_USE_ASCII_AMOR = "useAsciiAmor"; + public static final String ENCRYPT_ENCRYPTION_KEYS_IDS = "encryptionKeysIds"; + public static final String ENCRYPT_COMPRESSION_ID = "compressionId"; + public static final String ENCRYPT_GENERATE_SIGNATURE = "generateSignature"; + public static final String ENCRYPT_SIGN_ONLY = "signOnly"; + public static final String ENCRYPT_MESSAGE_BYTES = "messageBytes"; + public static final String ENCRYPT_INPUT_FILE = "inputFile"; + public static final String ENCRYPT_OUTPUT_FILE = "outputFile"; + public static final String ENCRYPT_PROVIDER_URI = "providerUri"; + + // decrypt/verify + public static final String DECRYPT_SIGNED_ONLY = "signedOnly"; + public static final String DECRYPT_RETURN_BYTES = "returnBinary"; + public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertextBytes"; + public static final String DECRYPT_ASSUME_SYMMETRIC = "assumeSymmetric"; + public static final String DECRYPT_LOOKUP_UNKNOWN_KEY = "lookupUnknownKey"; + + // save keyring + public static final String SAVE_KEYRING_NEW_PASSPHRASE = "newPassphrase"; + public static final String SAVE_KEYRING_CURRENT_PASSPHRASE = "currentPassphrase"; + public static final String SAVE_KEYRING_USER_IDS = "userIds"; + public static final String SAVE_KEYRING_KEYS = "keys"; + public static final String SAVE_KEYRING_KEYS_USAGES = "keysUsages"; + public static final String SAVE_KEYRING_MASTER_KEY_ID = "masterKeyId"; + + // generate key + public static final String GENERATE_KEY_ALGORITHM = "algorithm"; + public static final String GENERATE_KEY_KEY_SIZE = "keySize"; + public static final String GENERATE_KEY_SYMMETRIC_PASSPHRASE = "passphrase"; + public static final String GENERATE_KEY_MASTER_KEY = "masterKey"; + + // delete file securely + public static final String DELETE_FILE = "deleteFile"; + + // import key + public static final String IMPORT_INPUT_STREAM = "importInputStream"; + public static final String IMPORT_FILENAME = "importFilename"; + public static final String IMPORT_BYTES = "importBytes"; + // public static final String IMPORT_KEY_TYPE = "importKeyType"; + + // export key + public static final String EXPORT_OUTPUT_STREAM = "exportOutputStream"; + public static final String EXPORT_FILENAME = "exportFilename"; + public static final String EXPORT_KEY_TYPE = "exportKeyType"; + public static final String EXPORT_ALL = "exportAll"; + public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "exportKeyRingId"; + + // upload key + public static final String UPLOAD_KEY_SERVER = "uploadKeyServer"; + public static final String UPLOAD_KEY_KEYRING_ROW_ID = "uploadKeyRingId"; + + // query key + public static final String QUERY_KEY_SERVER = "queryKeyServer"; + public static final String QUERY_KEY_TYPE = "queryKeyType"; + public static final String QUERY_KEY_STRING = "queryKeyString"; + public static final String QUERY_KEY_ID = "queryKeyId"; + + // sign key + public static final String SIGN_KEY_MASTER_KEY_ID = "signKeyMasterKeyId"; + public static final String SIGN_KEY_PUB_KEY_ID = "signKeyPubKeyId"; + + /* + * possible data keys as result send over messenger + */ + // keys + public static final String RESULT_NEW_KEY = "newKey"; + public static final String RESULT_NEW_KEY2 = "newKey2"; + + // encrypt + public static final String RESULT_SIGNATURE_BYTES = "signatureData"; + public static final String RESULT_SIGNATURE_STRING = "signatureText"; + public static final String RESULT_ENCRYPTED_STRING = "encryptedMessage"; + public static final String RESULT_ENCRYPTED_BYTES = "encryptedData"; + public static final String RESULT_URI = "resultUri"; + + // decrypt/verify + public static final String RESULT_DECRYPTED_STRING = "decryptedMessage"; + public static final String RESULT_DECRYPTED_BYTES = "decryptedData"; + public static final String RESULT_SIGNATURE = "signature"; + public static final String RESULT_SIGNATURE_KEY_ID = "signatureKeyId"; + public static final String RESULT_SIGNATURE_USER_ID = "signatureUserId"; + + public static final String RESULT_SIGNATURE_SUCCESS = "signatureSuccess"; + public static final String RESULT_SIGNATURE_UNKNOWN = "signatureUnknown"; + public static final String RESULT_SIGNATURE_LOOKUP_KEY = "lookupKey"; + + // import + public static final String RESULT_IMPORT_ADDED = "added"; + public static final String RESULT_IMPORT_UPDATED = "updated"; + public static final String RESULT_IMPORT_BAD = "bad"; + + // export + public static final String RESULT_EXPORT = "exported"; + + // query + public static final String RESULT_QUERY_KEY_KEY_DATA = "queryKeyKeyData"; + public static final String RESULT_QUERY_KEY_SEARCH_RESULT = "queryKeySearchResult"; + + Messenger mMessenger; + + public KeychainIntentService() { + super("ApgService"); + } + + /** + * The IntentService calls this method from the default worker thread with the intent that + * started the service. When this method returns, IntentService stops the service, as + * appropriate. + */ + @Override + protected void onHandleIntent(Intent intent) { + Bundle extras = intent.getExtras(); + if (extras == null) { + Log.e(Constants.TAG, "Extras bundle is null!"); + return; + } + + if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || extras + .containsKey(EXTRA_ACTION))) { + Log.e(Constants.TAG, + "Extra bundle must contain a messenger, a data bundle, and an action!"); + return; + } + + mMessenger = (Messenger) extras.get(EXTRA_MESSENGER); + Bundle data = extras.getBundle(EXTRA_DATA); + + OtherHelper.logDebugBundle(data, "EXTRA_DATA"); + + int action = extras.getInt(EXTRA_ACTION); + + // execute action from extra bundle + switch (action) { + case ACTION_ENCRYPT_SIGN: + + try { + /* Input */ + int target = data.getInt(TARGET); + + long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID); + String encryptionPassphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); + + boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_AMOR); + long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS); + int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID); + boolean generateSignature = data.getBoolean(ENCRYPT_GENERATE_SIGNATURE); + boolean signOnly = data.getBoolean(ENCRYPT_SIGN_ONLY); + + InputStream inStream = null; + long inLength = -1; + InputData inputData = null; + OutputStream outStream = null; + String streamFilename = null; + switch (target) { + case TARGET_BYTES: /* encrypting bytes directly */ + byte[] bytes = data.getByteArray(ENCRYPT_MESSAGE_BYTES); + + inStream = new ByteArrayInputStream(bytes); + inLength = bytes.length; + + inputData = new InputData(inStream, inLength); + outStream = new ByteArrayOutputStream(); + + break; + case TARGET_FILE: /* encrypting file */ + String inputFile = data.getString(ENCRYPT_INPUT_FILE); + String outputFile = data.getString(ENCRYPT_OUTPUT_FILE); + + // check if storage is ready + if (!FileHelper.isStorageMounted(inputFile) + || !FileHelper.isStorageMounted(outputFile)) { + throw new PgpGeneralException( + getString(R.string.error_externalStorageNotReady)); + } + + inStream = new FileInputStream(inputFile); + File file = new File(inputFile); + inLength = file.length(); + inputData = new InputData(inStream, inLength); + + outStream = new FileOutputStream(outputFile); + + break; + + case TARGET_STREAM: /* Encrypting stream from content uri */ + Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI); + + // InputStream + InputStream in = getContentResolver().openInputStream(providerUri); + inLength = PgpMain.getLengthOfStream(in); + inputData = new InputData(in, inLength); + + // OutputStream + try { + while (true) { + streamFilename = PgpMain.generateRandomFilename(32); + if (streamFilename == null) { + throw new PgpMain.PgpGeneralException( + "couldn't generate random file name"); + } + openFileInput(streamFilename).close(); + } + } catch (FileNotFoundException e) { + // found a name that isn't used yet + } + outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); + + break; + + default: + throw new PgpMain.PgpGeneralException("No target choosen!"); + + } + + /* Operation */ + + if (generateSignature) { + Log.d(Constants.TAG, "generating signature..."); + PgpMain.generateSignature(this, this, inputData, outStream, useAsciiArmor, + false, secretKeyId, PassphraseCacheService.getCachedPassphrase(this, + secretKeyId), Preferences.getPreferences(this) + .getDefaultHashAlgorithm(), Preferences.getPreferences(this) + .getForceV3Signatures()); + } else if (signOnly) { + Log.d(Constants.TAG, "sign only..."); + PgpMain.signText(this, this, inputData, outStream, secretKeyId, + PassphraseCacheService.getCachedPassphrase(this, secretKeyId), + Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences + .getPreferences(this).getForceV3Signatures()); + } else { + Log.d(Constants.TAG, "encrypt..."); + PgpMain.encryptAndSign(this, this, inputData, outStream, useAsciiArmor, + compressionId, encryptionKeyIds, encryptionPassphrase, Preferences + .getPreferences(this).getDefaultEncryptionAlgorithm(), + secretKeyId, + Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences + .getPreferences(this).getForceV3Signatures(), + PassphraseCacheService.getCachedPassphrase(this, secretKeyId)); + } + + outStream.close(); + + /* Output */ + + Bundle resultData = new Bundle(); + + switch (target) { + case TARGET_BYTES: + if (useAsciiArmor) { + String output = new String( + ((ByteArrayOutputStream) outStream).toByteArray()); + if (generateSignature) { + resultData.putString(RESULT_SIGNATURE_STRING, output); + } else { + resultData.putString(RESULT_ENCRYPTED_STRING, output); + } + } else { + byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); + if (generateSignature) { + resultData.putByteArray(RESULT_SIGNATURE_BYTES, output); + } else { + resultData.putByteArray(RESULT_ENCRYPTED_BYTES, output); + } + } + + break; + case TARGET_FILE: + // nothing, file was written, just send okay + + break; + case TARGET_STREAM: + String uri = DataStream.buildDataStreamUri(streamFilename).toString(); + resultData.putString(RESULT_URI, uri); + + break; + } + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_DECRYPT_VERIFY: + try { + /* Input */ + int target = data.getInt(TARGET); + + long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID); + byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES); + boolean signedOnly = data.getBoolean(DECRYPT_SIGNED_ONLY); + boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES); + boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC); + + boolean lookupUnknownKey = data.getBoolean(DECRYPT_LOOKUP_UNKNOWN_KEY); + + InputStream inStream = null; + long inLength = -1; + InputData inputData = null; + OutputStream outStream = null; + String streamFilename = null; + switch (target) { + case TARGET_BYTES: /* decrypting bytes directly */ + inStream = new ByteArrayInputStream(bytes); + inLength = bytes.length; + + inputData = new InputData(inStream, inLength); + outStream = new ByteArrayOutputStream(); + + break; + + case TARGET_FILE: /* decrypting file */ + String inputFile = data.getString(ENCRYPT_INPUT_FILE); + String outputFile = data.getString(ENCRYPT_OUTPUT_FILE); + + // check if storage is ready + if (!FileHelper.isStorageMounted(inputFile) + || !FileHelper.isStorageMounted(outputFile)) { + throw new PgpGeneralException( + getString(R.string.error_externalStorageNotReady)); + } + + // InputStream + inLength = -1; + inStream = new FileInputStream(inputFile); + File file = new File(inputFile); + inLength = file.length(); + inputData = new InputData(inStream, inLength); + + // OutputStream + outStream = new FileOutputStream(outputFile); + + break; + + case TARGET_STREAM: /* decrypting stream from content uri */ + Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI); + + // InputStream + InputStream in = getContentResolver().openInputStream(providerUri); + inLength = PgpMain.getLengthOfStream(in); + inputData = new InputData(in, inLength); + + // OutputStream + try { + while (true) { + streamFilename = PgpMain.generateRandomFilename(32); + if (streamFilename == null) { + throw new PgpMain.PgpGeneralException( + "couldn't generate random file name"); + } + openFileInput(streamFilename).close(); + } + } catch (FileNotFoundException e) { + // found a name that isn't used yet + } + outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE); + + break; + + default: + throw new PgpMain.PgpGeneralException("No target choosen!"); + + } + + /* Operation */ + + Bundle resultData = new Bundle(); + + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + if (signedOnly) { + resultData = PgpMain.verifyText(this, this, inputData, outStream, + lookupUnknownKey); + } else { + resultData = PgpMain.decryptAndVerify(this, this, inputData, outStream, + PassphraseCacheService.getCachedPassphrase(this, secretKeyId), + assumeSymmetricEncryption); + } + + outStream.close(); + + /* Output */ + + switch (target) { + case TARGET_BYTES: + if (returnBytes) { + byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); + resultData.putByteArray(RESULT_DECRYPTED_BYTES, output); + } else { + String output = new String( + ((ByteArrayOutputStream) outStream).toByteArray()); + resultData.putString(RESULT_DECRYPTED_STRING, output); + } + + break; + case TARGET_FILE: + // nothing, file was written, just send okay and verification bundle + + break; + case TARGET_STREAM: + String uri = DataStream.buildDataStreamUri(streamFilename).toString(); + resultData.putString(RESULT_URI, uri); + + break; + } + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_SAVE_KEYRING: + + try { + /* Input */ + String oldPassPhrase = data.getString(SAVE_KEYRING_CURRENT_PASSPHRASE); + String newPassPhrase = data.getString(SAVE_KEYRING_NEW_PASSPHRASE); + if (newPassPhrase == null) { + newPassPhrase = oldPassPhrase; + } + ArrayList userIds = data.getStringArrayList(SAVE_KEYRING_USER_IDS); + ArrayList keys = PgpConversionHelper.BytesToPGPSecretKeyList(data + .getByteArray(SAVE_KEYRING_KEYS)); + ArrayList keysUsages = data.getIntegerArrayList(SAVE_KEYRING_KEYS_USAGES); + long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); + + /* Operation */ + PgpMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase, + newPassPhrase, this); + PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); + + /* Output */ + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_GENERATE_KEY: + + try { + /* Input */ + int algorithm = data.getInt(GENERATE_KEY_ALGORITHM); + String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); + int keysize = data.getInt(GENERATE_KEY_KEY_SIZE); + PGPSecretKey masterKey = null; + if (data.containsKey(GENERATE_KEY_MASTER_KEY)) { + masterKey = PgpConversionHelper.BytesToPGPSecretKey(data + .getByteArray(GENERATE_KEY_MASTER_KEY)); + } + + /* Operation */ + PGPSecretKeyRing newKeyRing = PgpMain.createKey(this, algorithm, keysize, + passphrase, masterKey); + + /* Output */ + Bundle resultData = new Bundle(); + resultData.putByteArray(RESULT_NEW_KEY, + PgpConversionHelper.PGPSecretKeyRingToBytes(newKeyRing)); + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_GENERATE_DEFAULT_RSA_KEYS: + // generate one RSA 2048 key for signing and one subkey for encrypting! + try { + /* Input */ + String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE); + + /* Operation */ + PGPSecretKeyRing masterKeyRing = PgpMain.createKey(this, Id.choice.algorithm.rsa, + 2048, passphrase, null); + + PGPSecretKeyRing subKeyRing = PgpMain.createKey(this, Id.choice.algorithm.rsa, + 2048, passphrase, masterKeyRing.getSecretKey()); + + /* Output */ + Bundle resultData = new Bundle(); + resultData.putByteArray(RESULT_NEW_KEY, + PgpConversionHelper.PGPSecretKeyRingToBytes(masterKeyRing)); + resultData.putByteArray(RESULT_NEW_KEY2, + PgpConversionHelper.PGPSecretKeyRingToBytes(subKeyRing)); + + OtherHelper.logDebugBundle(resultData, "resultData"); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_DELETE_FILE_SECURELY: + try { + /* Input */ + String deleteFile = data.getString(DELETE_FILE); + + /* Operation */ + try { + PgpMain.deleteFileSecurely(this, this, new File(deleteFile)); + } catch (FileNotFoundException e) { + throw new PgpMain.PgpGeneralException(getString(R.string.error_fileNotFound, + deleteFile)); + } catch (IOException e) { + throw new PgpMain.PgpGeneralException(getString( + R.string.error_fileDeleteFailed, deleteFile)); + } + + /* Output */ + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_IMPORT_KEYRING: + try { + + /* Input */ + int target = data.getInt(TARGET); + + // int keyType = Id.type.public_key; + // if (data.containsKey(IMPORT_KEY_TYPE)) { + // keyType = data.getInt(IMPORT_KEY_TYPE); + // } + + /* Operation */ + InputStream inStream = null; + long inLength = -1; + InputData inputData = null; + switch (target) { + case TARGET_BYTES: /* import key from bytes directly */ + byte[] bytes = data.getByteArray(IMPORT_BYTES); + + inStream = new ByteArrayInputStream(bytes); + inLength = bytes.length; + + inputData = new InputData(inStream, inLength); + + break; + case TARGET_FILE: /* import key from file */ + String inputFile = data.getString(IMPORT_FILENAME); + + inStream = new FileInputStream(inputFile); + File file = new File(inputFile); + inLength = file.length(); + inputData = new InputData(inStream, inLength); + + break; + + case TARGET_STREAM: + // TODO: not implemented + break; + } + + Bundle resultData = new Bundle(); + resultData = PgpMain.importKeyRings(this, inputData, this); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_EXPORT_KEYRING: + try { + + /* Input */ + int keyType = Id.type.public_key; + if (data.containsKey(EXPORT_KEY_TYPE)) { + keyType = data.getInt(EXPORT_KEY_TYPE); + } + + String outputFile = data.getString(EXPORT_FILENAME); + + boolean exportAll = data.getBoolean(EXPORT_ALL); + long keyRingMasterKeyId = -1; + if (!exportAll) { + keyRingMasterKeyId = data.getLong(EXPORT_KEY_RING_MASTER_KEY_ID); + } + + /* Operation */ + + // check if storage is ready + if (!FileHelper.isStorageMounted(outputFile)) { + throw new PgpGeneralException(getString(R.string.error_externalStorageNotReady)); + } + + // OutputStream + FileOutputStream outStream = new FileOutputStream(outputFile); + + ArrayList keyRingMasterKeyIds = new ArrayList(); + if (exportAll) { + // get all key ring row ids based on export type + + if (keyType == Id.type.public_key) { + keyRingMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this); + } else { + keyRingMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this); + } + } else { + keyRingMasterKeyIds.add(keyRingMasterKeyId); + } + + Bundle resultData = new Bundle(); + resultData = PgpMain.exportKeyRings(this, keyRingMasterKeyIds, keyType, outStream, + this); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_UPLOAD_KEYRING: + try { + + /* Input */ + int keyRingRowId = data.getInt(UPLOAD_KEY_KEYRING_ROW_ID); + String keyServer = data.getString(UPLOAD_KEY_SERVER); + + /* Operation */ + HkpKeyServer server = new HkpKeyServer(keyServer); + + PGPPublicKeyRing keyring = ProviderHelper.getPGPPublicKeyRingByRowId(this, + keyRingRowId); + if (keyring != null) { + boolean uploaded = PgpMain.uploadKeyRingToServer(server, + (PGPPublicKeyRing) keyring); + if (!uploaded) { + throw new PgpGeneralException("Unable to export key to selected server"); + } + } + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_QUERY_KEYRING: + try { + + /* Input */ + int queryType = data.getInt(QUERY_KEY_TYPE); + String keyServer = data.getString(QUERY_KEY_SERVER); + + String queryString = data.getString(QUERY_KEY_STRING); + long keyId = data.getLong(QUERY_KEY_ID); + + /* Operation */ + Bundle resultData = new Bundle(); + + HkpKeyServer server = new HkpKeyServer(keyServer); + if (queryType == Id.keyserver.search) { + ArrayList searchResult = server.search(queryString); + + resultData.putParcelableArrayList(RESULT_QUERY_KEY_SEARCH_RESULT, searchResult); + } else if (queryType == Id.keyserver.get) { + String keyData = server.get(keyId); + + resultData.putString(RESULT_QUERY_KEY_KEY_DATA, keyData); + } + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + case ACTION_SIGN_KEYRING: + try { + + /* Input */ + long masterKeyId = data.getLong(SIGN_KEY_MASTER_KEY_ID); + long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID); + + /* Operation */ + String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this, + masterKeyId); + + PGPPublicKeyRing signedPubKeyRing = PgpMain.signKey(this, masterKeyId, pubKeyId, + signaturePassPhrase); + + // store the signed key in our local cache + int retval = PgpMain.storeKeyRingInCache(this, signedPubKeyRing); + if (retval != Id.return_value.ok && retval != Id.return_value.updated) { + throw new PgpGeneralException("Failed to store signed key in local cache"); + } + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); + } catch (Exception e) { + sendErrorToHandler(e); + } + + break; + + default: + break; + } + + } + + private void sendErrorToHandler(Exception e) { + Log.e(Constants.TAG, "ApgService Exception: ", e); + e.printStackTrace(); + + Bundle data = new Bundle(); + data.putString(KeychainIntentServiceHandler.DATA_ERROR, e.getMessage()); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_EXCEPTION, null, data); + } + + private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) { + Message msg = Message.obtain(); + msg.arg1 = arg1; + if (arg2 != null) { + msg.arg2 = arg2; + } + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } + + private void sendMessageToHandler(Integer arg1, Bundle data) { + sendMessageToHandler(arg1, null, data); + } + + private void sendMessageToHandler(Integer arg1) { + sendMessageToHandler(arg1, null, null); + } + + /** + * Set progress of ProgressDialog by sending message to handler on UI thread + */ + public void setProgress(String message, int progress, int max) { + Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max=" + + max); + + Bundle data = new Bundle(); + if (message != null) { + data.putString(KeychainIntentServiceHandler.DATA_MESSAGE, message); + } + data.putInt(KeychainIntentServiceHandler.DATA_PROGRESS, progress); + data.putInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX, max); + + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS, null, data); + } + + public void setProgress(int resourceId, int progress, int max) { + setProgress(getString(resourceId), progress, max); + } + + public void setProgress(int progress, int max) { + setProgress(null, progress, max); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java new file mode 100644 index 000000000..4e43f2b3e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service; + +import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; +import org.sufficientlysecure.keychain.R; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.support.v4.app.FragmentActivity; +import android.widget.Toast; + +public class KeychainIntentServiceHandler extends Handler { + + // possible messages send from this service to handler on ui + public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_EXCEPTION = 2; + public static final int MESSAGE_UPDATE_PROGRESS = 3; + + // possible data keys for messages + public static final String DATA_ERROR = "error"; + public static final String DATA_PROGRESS = "progress"; + public static final String DATA_PROGRESS_MAX = "max"; + public static final String DATA_MESSAGE = "message"; + public static final String DATA_MESSAGE_ID = "message_id"; + + Activity mActivity; + ProgressDialogFragment mProgressDialogFragment; + + public KeychainIntentServiceHandler(Activity activity) { + this.mActivity = activity; + } + + public KeychainIntentServiceHandler(Activity activity, ProgressDialogFragment progressDialogFragment) { + this.mActivity = activity; + this.mProgressDialogFragment = progressDialogFragment; + } + + public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) { + this.mActivity = activity; + this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId, + progressDialogStyle); + } + + public void showProgressDialog(FragmentActivity activity) { + mProgressDialogFragment.show(activity.getSupportFragmentManager(), "progressDialog"); + } + + @Override + public void handleMessage(Message message) { + Bundle data = message.getData(); + + switch (message.arg1) { + case MESSAGE_OKAY: + mProgressDialogFragment.dismiss(); + + break; + + case MESSAGE_EXCEPTION: + mProgressDialogFragment.dismiss(); + + // show error from service + if (data.containsKey(DATA_ERROR)) { + Toast.makeText(mActivity, + mActivity.getString(R.string.errorMessage, data.getString(DATA_ERROR)), + Toast.LENGTH_SHORT).show(); + } + + break; + + case MESSAGE_UPDATE_PROGRESS: + if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) { + + // update progress from service + if (data.containsKey(DATA_MESSAGE)) { + mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE), + data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); + } else if (data.containsKey(DATA_MESSAGE_ID)) { + mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID), + data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX)); + } else { + mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS), + data.getInt(DATA_PROGRESS_MAX)); + } + } + + break; + + default: + break; + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java new file mode 100644 index 000000000..c0be8040f --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/KeychainKeyService.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service; + +import java.util.ArrayList; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.service.IKeychainKeyService; +import org.sufficientlysecure.keychain.service.handler.IKeychainGetKeyringsHandler; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; + +public class KeychainKeyService extends Service { + Context mContext; + + @Override + public void onCreate() { + super.onCreate(); + mContext = this; + Log.d(Constants.TAG, "ApgKeyService, onCreate()"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.d(Constants.TAG, "ApgKeyService, onDestroy()"); + } + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + /** + * Synchronized implementation of getPublicKeyRings + */ + private synchronized void getPublicKeyRingsSafe(long[] masterKeyIds, + boolean asAsciiArmoredStringArray, IKeychainGetKeyringsHandler handler) + throws RemoteException { + if (asAsciiArmoredStringArray) { + ArrayList output = ProviderHelper.getPublicKeyRingsAsArmoredString(mContext, + masterKeyIds); + + handler.onSuccess(null, output); + } else { + byte[] outputBytes = ProviderHelper + .getPublicKeyRingsAsByteArray(mContext, masterKeyIds); + handler.onSuccess(outputBytes, null); + } + } + + /** + * Synchronized implementation of getSecretKeyRings + */ + private synchronized void getSecretKeyRingsSafe(long[] masterKeyIds, + boolean asAsciiArmoredStringArray, IKeychainGetKeyringsHandler handler) + throws RemoteException { + if (asAsciiArmoredStringArray) { + ArrayList output = ProviderHelper.getSecretKeyRingsAsArmoredString(mContext, + masterKeyIds); + + handler.onSuccess(null, output); + } else { + byte[] outputBytes = ProviderHelper + .getSecretKeyRingsAsByteArray(mContext, masterKeyIds); + handler.onSuccess(outputBytes, null); + } + + } + + /** + * This is the implementation of the interface IApgKeyService. All methods are oneway, meaning + * asynchronous and return to the client using handlers. + * + * The real PGP code is located in PGPMain. + */ + private final IKeychainKeyService.Stub mBinder = new IKeychainKeyService.Stub() { + + @Override + public void getPublicKeyRings(long[] masterKeyIds, boolean asAsciiArmoredStringArray, + IKeychainGetKeyringsHandler handler) throws RemoteException { + getPublicKeyRingsSafe(masterKeyIds, asAsciiArmoredStringArray, handler); + } + + @Override + public void getSecretKeyRings(long[] masterKeyIds, boolean asAsciiArmoredStringArray, + IKeychainGetKeyringsHandler handler) throws RemoteException { + getSecretKeyRingsSafe(masterKeyIds, asAsciiArmoredStringArray, handler); + } + + }; + + /** + * As we can not throw an exception through Android RPC, we assign identifiers to the exception + * types. + * + * @param e + * @return + */ + // private int getExceptionId(Exception e) { + // if (e instanceof NoSuchProviderException) { + // return 0; + // } else if (e instanceof NoSuchAlgorithmException) { + // return 1; + // } else if (e instanceof SignatureException) { + // return 2; + // } else if (e instanceof IOException) { + // return 3; + // } else if (e instanceof ApgGeneralException) { + // return 4; + // } else if (e instanceof PGPException) { + // return 5; + // } else { + // return -1; + // } + // } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java new file mode 100644 index 000000000..eb1232769 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service; + +import java.util.Date; +import java.util.HashMap; + +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.provider.ProviderHelper; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +public class PassphraseCacheService extends Service { + public static final String TAG = Constants.TAG + ": PassphraseCacheService"; + + public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX + + "PASSPHRASE_CACHE_SERVICE"; + + public static final String EXTRA_TTL = "ttl"; + public static final String EXTRA_KEY_ID = "keyId"; + public static final String EXTRA_PASSPHRASE = "passphrase"; + + private static final int REQUEST_ID = 0; + private static final long DEFAULT_TTL = 15; + + private BroadcastReceiver mIntentReceiver; + + // This is static to be easily retrieved by getCachedPassphrase() without the need of callback + // functions + private static HashMap mPassphraseCache = new HashMap(); + + /** + * This caches a new passphrase by sending a new command to the service. An android service is + * only run once. Thus, when the service is already started, new commands just add new events to + * the alarm manager for new passphrases to let them timeout in the future. + * + * @param context + * @param keyId + * @param passphrase + */ + public static void addCachedPassphrase(Context context, long keyId, String passphrase) { + Log.d(TAG, "cacheNewPassphrase() for " + keyId); + + Intent intent = new Intent(context, PassphraseCacheService.class); + intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl()); + intent.putExtra(EXTRA_PASSPHRASE, passphrase); + intent.putExtra(EXTRA_KEY_ID, keyId); + + context.startService(intent); + } + + /** + * Gets a cached passphrase from memory + * + * @param context + * @param keyId + * @return + */ + public static String getCachedPassphrase(Context context, long keyId) { + // try to get master key id which is used as an identifier for cached passphrases + long masterKeyId = keyId; + if (masterKeyId != Id.key.symmetric) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, keyId); + if (keyRing == null) { + return null; + } + PGPSecretKey masterKey = PgpHelper.getMasterKey(keyRing); + if (masterKey == null) { + return null; + } + masterKeyId = masterKey.getKeyID(); + } + + // get cached passphrase + String cachedPassphrase = mPassphraseCache.get(masterKeyId); + if (cachedPassphrase == null) { + return null; + } + // set it again to reset the cache life cycle + Log.d(TAG, "Cache passphrase again when getting it!"); + addCachedPassphrase(context, masterKeyId, cachedPassphrase); + + return cachedPassphrase; + } + + /** + * Register BroadcastReceiver that is unregistered when service is destroyed. This + * BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout + * specific passphrases in memory. + */ + private void registerReceiver() { + if (mIntentReceiver == null) { + mIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + Log.d(TAG, "Received broadcast..."); + + if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) { + long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); + timeout(context, keyId); + } + } + }; + + IntentFilter filter = new IntentFilter(); + filter.addAction(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); + registerReceiver(mIntentReceiver, filter); + } + } + + /** + * Build pending intent that is executed by alarm manager to time out a specific passphrase + * + * @param context + * @param keyId + * @return + */ + private static PendingIntent buildIntent(Context context, long keyId) { + Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE); + intent.putExtra(EXTRA_KEY_ID, keyId); + PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + + return sender; + } + + @Override + public void onCreate() { + Log.d(TAG, "onCreate()"); + } + + /** + * Executed when service is started by intent + */ + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "onStartCommand()"); + + // register broadcastreceiver + registerReceiver(); + + if (intent != null) { + long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL); + long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); + String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); + + Log.d(TAG, "Received intent in onStartCommand() with keyId: " + keyId + ", ttl: " + ttl); + + // add keyId and passphrase to memory + mPassphraseCache.put(keyId, passphrase); + + // register new alarm with keyId for this passphrase + long triggerTime = new Date().getTime() + (ttl * 1000); + AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE); + am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId)); + } + + return START_STICKY; + } + + /** + * Called when one specific passphrase for keyId timed out + * + * @param context + * @param keyId + */ + private void timeout(Context context, long keyId) { + // remove passphrase corresponding to keyId from memory + mPassphraseCache.remove(keyId); + + Log.d(TAG, "Timeout of " + keyId + ", removed from memory!"); + + // stop whole service if no cached passphrases remaining + if (mPassphraseCache.isEmpty()) { + Log.d(TAG, "No passphrases remaining in memory, stopping service!"); + stopSelf(); + } + } + + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy()"); + + unregisterReceiver(mIntentReceiver); + } + + public class PassphraseCacheBinder extends Binder { + public PassphraseCacheService getService() { + return PassphraseCacheService.this; + } + } + + private final IBinder mBinder = new PassphraseCacheBinder(); + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainDecryptHandler.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainDecryptHandler.aidl new file mode 100644 index 000000000..31ead701d --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainDecryptHandler.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service.handler; + +interface IKeychainDecryptHandler { + + oneway void onSuccess(in byte[] outputBytes, in String outputUri, in boolean signature, + in long signatureKeyId, in String signatureUserId, in boolean signatureSuccess, + in boolean signatureUnknown); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainEncryptHandler.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainEncryptHandler.aidl new file mode 100644 index 000000000..5b21a0613 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainEncryptHandler.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service.handler; + +interface IKeychainEncryptHandler { + /** + * Either output or streamUri is given. One of them is null + * + */ + oneway void onSuccess(in byte[] outputBytes, in String outputUri); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetDecryptionKeyIdHandler.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetDecryptionKeyIdHandler.aidl new file mode 100644 index 000000000..2fff74a92 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetDecryptionKeyIdHandler.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service.handler; + +interface IKeychainGetDecryptionKeyIdHandler { + + oneway void onSuccess(in long secretKeyId, in boolean symmetric); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl new file mode 100644 index 000000000..c3a7d1faf --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainGetKeyringsHandler.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service.handler; + +interface IKeychainGetKeyringsHandler { + /** + * Either outputBytes or outputString is given. One of them is null + * + */ + oneway void onSuccess(in byte[] outputBytes, in List outputString); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainSignHandler.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainSignHandler.aidl new file mode 100644 index 000000000..69badab4c --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainSignHandler.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service.handler; + +interface IKeychainSignHandler { + /** + * Either output or streamUri is given. One of them is null + * + */ + oneway void onSuccess(in byte[] outputBytes, in String outputUri); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainVerifyHandler.aidl b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainVerifyHandler.aidl new file mode 100644 index 000000000..aaa9a7f6a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/handler/IKeychainVerifyHandler.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.service.handler; + +interface IKeychainVerifyHandler { + + oneway void onSuccess(in boolean signature, in long signatureKeyId, + in String signatureUserId, in boolean signatureSuccess, in boolean signatureUnknown); + + + oneway void onException(in int exceptionNumber, in String message); +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java new file mode 100644 index 000000000..87b1204a4 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -0,0 +1,946 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.LookupUnknownKeyDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.animation.AnimationUtils; +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.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.regex.Matcher; + +public class DecryptActivity extends SherlockFragmentActivity { + + /* Intents */ + // without permission + public static final String ACTION_DECRYPT = Constants.INTENT_PREFIX + "DECRYPT"; + public static final String ACTION_DECRYPT_FILE = Constants.INTENT_PREFIX + "DECRYPT_FILE"; + + // with permission + public static final String ACTION_DECRYPT_AND_RETURN = Constants.INTENT_PREFIX + + "DECRYPT_AND_RETURN"; + public static final String ACTION_DECRYPT_STREAM_AND_RETURN = Constants.INTENT_PREFIX + + "DECRYPT_STREAM_AND_RETURN"; + + /* EXTRA keys for input */ + public static final String EXTRA_TEXT = "text"; + public static final String EXTRA_DATA = "data"; + public static final String EXTRA_REPLY_TO = "replyTo"; + public static final String EXTRA_SUBJECT = "subject"; + public static final String EXTRA_BINARY = "binary"; + + private long mSignatureKeyId = 0; + + 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 boolean mDecryptEnabled = true; + private String mDecryptString = ""; + private boolean mReplyEnabled = true; + private String mReplyString = ""; + + 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[] mDataBytes = null; + private boolean mReturnBinary = false; + + private long mUnknownSignatureKeyId = 0; + + private long mSecretKeyId = Id.key.none; + + private FileDialogFragment mFileDialog; + + private boolean mLookupUnknownKey = true; + + private boolean mDecryptImmediately = false; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + if (mDecryptEnabled) { + menu.add(1, Id.menu.option.decrypt, 0, mDecryptString).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + if (mReplyEnabled) { + menu.add(1, Id.menu.option.reply, 1, mReplyString).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.decrypt: { + decryptClicked(); + + return true; + } + case Id.menu.option.reply: { + replyClicked(); + + return true; + } + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + private void initView() { + 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); + 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) { + FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*", + Id.request.filename); + } + }); + + mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterDecryption); + + // default: message source + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceMessage) { + mSource.showNext(); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check permissions for intent actions without user interaction + String[] restrictedActions = new String[] { ACTION_DECRYPT_AND_RETURN }; + OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), + Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); + + setContentView(R.layout.decrypt); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + initView(); + + // Handle intent actions + handleActions(getIntent()); + + if (mSource.getCurrentView().getId() == R.id.sourceMessage + && mMessage.getText().length() == 0) { + + CharSequence clipboardText = ClipboardReflection.getClipboardText(this); + + String data = ""; + if (clipboardText != null) { + Matcher matcher = PgpMain.PGP_MESSAGE.matcher(clipboardText); + if (!matcher.matches()) { + matcher = PgpMain.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 = ProviderHelper.getPGPPublicKeyRingByKeyId( + DecryptActivity.this, mSignatureKeyId); + if (key != null) { + Intent intent = new Intent(DecryptActivity.this, KeyServerQueryActivity.class); + intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); + intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, mSignatureKeyId); + startActivity(intent); + } + } + }); + + mReplyEnabled = false; + + // build new actionbar + invalidateOptionsMenu(); + + 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 (mDecryptImmediately + || (mSource.getCurrentView().getId() == R.id.sourceMessage && (mMessage.getText() + .length() > 0 || mDataBytes != null || mContentUri != null))) { + decryptClicked(); + } + } + + /** + * Handles all actions with this intent + * + * @param intent + */ + private void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + String type = intent.getType(); + Uri uri = intent.getData(); + + if (extras == null) { + extras = new Bundle(); + } + + /* + * Android's Action + */ + if (Intent.ACTION_SEND.equals(action) && type != null) { + // When sending to APG Encrypt via share menu + if ("text/plain".equals(type)) { + // Plain text + String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); + if (sharedText != null) { + // handle like normal text decryption, override action and extras to later + // execute ACTION_DECRYPT in main actions + extras.putString(EXTRA_TEXT, sharedText); + action = ACTION_DECRYPT; + } + } else { + // Binary via content provider (could also be files) + // override uri to get stream from send + uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); + action = ACTION_DECRYPT_FILE; + } + } else if (Intent.ACTION_VIEW.equals(action)) { + // Android's Action when opening file associated to APG (see AndroidManifest.xml) + + // override action + action = ACTION_DECRYPT_FILE; + + // EVERYTHING ELSE IS OLD CODE + // This gets the Uri, where an inputStream can be opened from + // mContentUri = intent.getData(); + + // TODO: old implementation of ACTION_VIEW. Is this used in K9? + // 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 + // } + + // same as ACTION_DECRYPT_FILE but decrypt it immediately + // handleActionDecryptFile(intent); + // mDecryptImmediately = true; + } + + /** + * Main Actions + */ + if (ACTION_DECRYPT.equals(action)) { + mDataBytes = extras.getByteArray(EXTRA_DATA); + String textData = null; + if (mDataBytes == null) { + Log.d(Constants.TAG, "EXTRA_DATA was null"); + textData = extras.getString(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 = PgpMain.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 = PgpMain.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); + + mDecryptString = getString(R.string.btn_verify); + // build new action bar + invalidateOptionsMenu(); + } else { + Log.d(Constants.TAG, "Nothing matched!"); + } + } + } + mReplyTo = extras.getString(EXTRA_REPLY_TO); + mSubject = extras.getString(EXTRA_SUBJECT); + } else if (ACTION_DECRYPT_FILE.equals(action)) { + // get file path from uri + String path = FileHelper.getPath(this, uri); + + if (path != null) { + mInputFilename = path; + mFilename.setText(mInputFilename); + guessOutputFilename(); + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceFile) { + mSource.showNext(); + } + } else { + Log.e(Constants.TAG, + "Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_DECRYPT_STREAM_AND_RETURN."); + Toast.makeText(this, R.string.error_onlyFilesAreSupported, Toast.LENGTH_LONG) + .show(); + // end activity + finish(); + } + } else if (ACTION_DECRYPT_AND_RETURN.equals(action)) { + mReturnBinary = extras.getBoolean(EXTRA_BINARY, false); + + if (mContentUri == null) { + mDataBytes = extras.getByteArray(EXTRA_DATA); + String data = extras.getString(EXTRA_TEXT); + if (data != null) { + Matcher matcher = PgpMain.PGP_MESSAGE.matcher(data); + if (matcher.matches()) { + data = matcher.group(1); + // replace non breakable spaces + data = data.replaceAll("\\xa0", " "); + mMessage.setText(data); + } else { + matcher = PgpMain.PGP_SIGNED_MESSAGE.matcher(data); + if (matcher.matches()) { + data = matcher.group(1); + // replace non breakable spaces + data = data.replaceAll("\\xa0", " "); + mMessage.setText(data); + mDecryptString = getString(R.string.btn_verify); + + // build new action bar + invalidateOptionsMenu(); + } + } + } + } + mReturnResult = true; + } else if (ACTION_DECRYPT_STREAM_AND_RETURN.equals(action)) { + // TODO: Implement decrypt stream + } + } + + 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); + mDecryptString = getString(R.string.btn_decrypt); + + // build new action bar + invalidateOptionsMenu(); + break; + } + + case R.id.sourceMessage: { + mSourceLabel.setText(R.string.label_message); + mDecryptString = getString(R.string.btn_decrypt); + + // build new action bar + invalidateOptionsMenu(); + 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 = PgpMain.PGP_SIGNED_MESSAGE.matcher(messageData); + if (matcher.matches()) { + mSignedOnly = true; + decryptStart(); + return; + } + } + + // else treat it as an decrypted message/file + mSignedOnly = false; + + getDecryptionKeyFromInputStream(); + + // if we need a symmetric passphrase or a passphrase to use a secret key ask for it + if (mSecretKeyId == Id.key.symmetric + || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { + showPassphraseDialog(); + } else { + if (mDecryptTarget == Id.target.file) { + askForOutputFilename(); + } else { + decryptStart(); + } + } + } + + /** + * Shows passphrase dialog to cache a new passphrase the user enters for using it later for + * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks + * for a symmetric passphrase + */ + private void showPassphraseDialog() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + if (mDecryptTarget == Id.target.file) { + askForOutputFilename(); + } else { + decryptStart(); + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, + messenger, mSecretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PgpMain.PgpGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + /** + * TODO: Rework function, remove global variables + */ + private void getDecryptionKeyFromInputStream() { + InputStream inStream = null; + if (mContentUri != null) { + try { + inStream = getContentResolver().openInputStream(mContentUri); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "File not found!", e); + Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } else if (mDecryptTarget == Id.target.file) { + // check if storage is ready + if (!FileHelper.isStorageMounted(mInputFilename)) { + Toast.makeText(this, getString(R.string.error_externalStorageNotReady), + Toast.LENGTH_SHORT).show(); + return; + } + + try { + inStream = new FileInputStream(mInputFilename); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "File not found!", e); + Toast.makeText(this, getString(R.string.error_fileNotFound, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } else { + if (mDataBytes != null) { + inStream = new ByteArrayInputStream(mDataBytes); + } else { + inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); + } + } + + // get decryption key for this inStream + try { + try { + mSecretKeyId = PgpMain.getDecryptionKeyId(this, inStream); + if (mSecretKeyId == Id.key.none) { + throw new PgpMain.PgpGeneralException( + getString(R.string.error_noSecretKeyFound)); + } + mAssumeSymmetricEncryption = false; + } catch (PgpMain.NoAsymmetricEncryptionException e) { + mSecretKeyId = Id.key.symmetric; + if (!PgpMain.hasSymmetricEncryption(this, inStream)) { + throw new PgpMain.PgpGeneralException( + getString(R.string.error_noKnownEncryptionFound)); + } + mAssumeSymmetricEncryption = true; + } + } catch (Exception e) { + Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } + + private void replyClicked() { + Intent intent = new Intent(this, EncryptActivity.class); + intent.setAction(EncryptActivity.ACTION_ENCRYPT); + String data = mMessage.getText().toString(); + data = data.replaceAll("(?m)^", "> "); + data = "\n\n" + data; + intent.putExtra(EncryptActivity.EXTRA_TEXT, data); + intent.putExtra(EncryptActivity.EXTRA_SUBJECT, "Re: " + mSubject); + intent.putExtra(EncryptActivity.EXTRA_SEND_TO, mReplyTo); + intent.putExtra(EncryptActivity.EXTRA_SIGNATURE_KEY_ID, mSecretKeyId); + intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[] { mSignatureKeyId }); + startActivity(intent); + } + + private void askForOutputFilename() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + decryptStart(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + mFileDialog = FileDialogFragment.newInstance(messenger, + getString(R.string.title_decryptToFile), + getString(R.string.specifyFileToDecryptTo), mOutputFilename, null, + Id.request.output_filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + + private void lookupUnknownKey(long unknownKeyId) { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_OKAY) { + // the result is handled by onActivityResult() as LookupUnknownKeyDialogFragment + // starts a new Intent which then returns data + } else if (message.what == LookupUnknownKeyDialogFragment.MESSAGE_CANCEL) { + // decrypt again, but don't lookup unknown keys! + mLookupUnknownKey = false; + decryptStart(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + LookupUnknownKeyDialogFragment lookupKeyDialog = LookupUnknownKeyDialogFragment + .newInstance(messenger, unknownKeyId); + + lookupKeyDialog.show(getSupportFragmentManager(), "unknownKeyDialog"); + } + + private void decryptStart() { + Log.d(Constants.TAG, "decryptStart"); + + // Send all information needed to service to decrypt in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_DECRYPT_VERIFY); + + // choose action based on input: decrypt stream, file or bytes + if (mContentUri != null) { + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_STREAM); + + data.putParcelable(KeychainIntentService.ENCRYPT_PROVIDER_URI, mContentUri); + } else if (mDecryptTarget == Id.target.file) { + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_FILE); + + Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" + + mOutputFilename); + + data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename); + data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename); + } else { + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES); + + if (mDataBytes != null) { + data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mDataBytes); + } else { + String message = mMessage.getText().toString(); + data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, message.getBytes()); + } + } + + data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId); + + data.putBoolean(KeychainIntentService.DECRYPT_SIGNED_ONLY, mSignedOnly); + data.putBoolean(KeychainIntentService.DECRYPT_LOOKUP_UNKNOWN_KEY, mLookupUnknownKey); + data.putBoolean(KeychainIntentService.DECRYPT_RETURN_BYTES, mReturnBinary); + data.putBoolean(KeychainIntentService.DECRYPT_ASSUME_SYMMETRIC, mAssumeSymmetricEncryption); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after encrypting is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, + R.string.progress_decrypting, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + // if key is unknown show lookup dialog + if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_LOOKUP_KEY) + && mLookupUnknownKey) { + mUnknownSignatureKeyId = returnData + .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID); + lookupUnknownKey(mUnknownSignatureKeyId); + return; + } + + mSignatureKeyId = 0; + mSignatureLayout.setVisibility(View.GONE); + mReplyEnabled = false; + + // build new action bar + invalidateOptionsMenu(); + + Toast.makeText(DecryptActivity.this, R.string.decryptionSuccessful, + Toast.LENGTH_SHORT).show(); + if (mReturnResult) { + Intent intent = new Intent(); + intent.putExtras(returnData); + setResult(RESULT_OK, intent); + finish(); + return; + } + + switch (mDecryptTarget) { + case Id.target.message: + String decryptedMessage = returnData + .getString(KeychainIntentService.RESULT_DECRYPTED_STRING); + mMessage.setText(decryptedMessage); + mMessage.setHorizontallyScrolling(false); + mReplyEnabled = false; + + // build new action bar + invalidateOptionsMenu(); + break; + + case Id.target.file: + if (mDeleteAfter.isChecked()) { + // Create and show dialog to delete original file + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment + .newInstance(mInputFilename); + deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); + } + break; + + default: + // shouldn't happen + break; + + } + + if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE)) { + String userId = returnData + .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID); + mSignatureKeyId = returnData + .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID); + mUserIdRest + .setText("id: " + PgpHelper.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 (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) { + mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); + } else if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) { + mSignatureStatusImage.setImageResource(R.drawable.overlay_error); + Toast.makeText(DecryptActivity.this, + R.string.unknownSignatureKeyTouchToLookUp, Toast.LENGTH_LONG) + .show(); + } else { + mSignatureStatusImage.setImageResource(R.drawable.overlay_error); + } + mSignatureLayout.setVisibility(View.VISIBLE); + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = FileHelper.getPath(this, data.getData()); + Log.d(Constants.TAG, "path=" + path); + + mFilename.setText(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + case Id.request.output_filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + mFileDialog.setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + // this request is returned after LookupUnknownKeyDialogFragment started + // KeyServerQueryActivity and user looked uo key + case Id.request.look_up_key_id: { + Log.d(Constants.TAG, "Returning from Lookup Key..."); + // decrypt again without lookup + mLookupUnknownKey = false; + decryptStart(); + return; + } + + default: { + break; + } + } + + super.onActivityResult(requestCode, resultCode, data); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java new file mode 100644 index 000000000..73a1234c4 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.PgpConversionHelper; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.helper.PgpMain.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.KeyEditor; +import org.sufficientlysecure.keychain.ui.widget.SectionView; +import org.sufficientlysecure.keychain.ui.widget.UserIdEditor; +import org.sufficientlysecure.keychain.util.IterableIterator; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.LinearLayout; +import android.widget.Toast; +import android.widget.CompoundButton.OnCheckedChangeListener; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Vector; + +public class EditKeyActivity extends SherlockFragmentActivity { + + // possible intent actions for this activity + public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY"; + public static final String ACTION_EDIT_KEY = Constants.INTENT_PREFIX + "EDIT_KEY"; + + // possible extra keys + public static final String EXTRA_USER_IDS = "userIds"; + public static final String EXTRA_NO_PASSPHRASE = "noPassphrase"; + public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generateDefaultKeys"; + public static final String EXTRA_KEY_ID = "keyId"; + + // results when saving key + public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; + public static final String RESULT_EXTRA_USER_ID = "userId"; + + private ActionBar mActionBar; + + private PGPSecretKeyRing mKeyRing = null; + + private SectionView mUserIdsView; + private SectionView mKeysView; + + private String mCurrentPassPhrase = null; + private String mNewPassPhrase = null; + + private Button mChangePassPhrase; + + private CheckBox mNoPassphrase; + + Vector mUserIds; + Vector mKeys; + Vector mKeysUsages; + + // will be set to false to build layout later in handler + private boolean mBuildLayout = true; + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.save, 1, R.string.btn_save).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, KeyListSecretActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.save: + saveClicked(); + return true; + + case Id.menu.option.cancel: + cancelClicked(); + return true; + + default: + break; + + } + return false; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check permissions for intent actions without user interaction + String[] restrictedActions = new String[] { ACTION_CREATE_KEY }; + OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), + Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); + + setContentView(R.layout.edit_key); + + mActionBar = getSupportActionBar(); + mActionBar.setDisplayShowTitleEnabled(true); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + // find views + mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase); + mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase); + + mUserIds = new Vector(); + mKeys = new Vector(); + mKeysUsages = new Vector(); + + // Catch Intents opened from other apps + Intent intent = getIntent(); + String action = intent.getAction(); + if (ACTION_CREATE_KEY.equals(action)) { + handleActionCreateKey(intent); + } else if (ACTION_EDIT_KEY.equals(action)) { + handleActionEditKey(intent); + } + + mChangePassPhrase.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + showSetPassphraseDialog(); + } + }); + + // disable passphrase when no passphrase checkobox is checked! + mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) { + // remove passphrase + mNewPassPhrase = null; + + mChangePassPhrase.setVisibility(View.GONE); + } else { + mChangePassPhrase.setVisibility(View.VISIBLE); + } + } + }); + + if (mBuildLayout) { + buildLayout(); + } + } + + /** + * Handle intent action to create new key + * + * @param intent + */ + private void handleActionCreateKey(Intent intent) { + Bundle extras = intent.getExtras(); + + mActionBar.setTitle(R.string.title_createKey); + + mCurrentPassPhrase = ""; + + if (extras != null) { + // if userId is given, prefill the fields + if (extras.containsKey(EXTRA_USER_IDS)) { + Log.d(Constants.TAG, "UserIds are given!"); + mUserIds.add(extras.getString(EXTRA_USER_IDS)); + } + + // if no passphrase is given + if (extras.containsKey(EXTRA_NO_PASSPHRASE)) { + boolean noPassphrase = extras.getBoolean(EXTRA_NO_PASSPHRASE); + if (noPassphrase) { + // check "no passphrase" checkbox and remove button + mNoPassphrase.setChecked(true); + mChangePassPhrase.setVisibility(View.GONE); + } + } + + // generate key + if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) { + boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS); + if (generateDefaultKeys) { + + // build layout in handler after generating keys not directly in onCreate + mBuildLayout = false; + + // Send all information needed to service generate keys in other thread + Intent serviceIntent = new Intent(this, KeychainIntentService.class); + serviceIntent.putExtra(KeychainIntentService.EXTRA_ACTION, + KeychainIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, + mCurrentPassPhrase); + + serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after generating is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, + R.string.progress_generating, ProgressDialog.STYLE_SPINNER) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get new key from data bundle returned from service + Bundle data = message.getData(); + PGPSecretKeyRing masterKeyRing = (PGPSecretKeyRing) PgpConversionHelper + .BytesToPGPKeyRing(data + .getByteArray(KeychainIntentService.RESULT_NEW_KEY)); + PGPSecretKeyRing subKeyRing = (PGPSecretKeyRing) PgpConversionHelper + .BytesToPGPKeyRing(data + .getByteArray(KeychainIntentService.RESULT_NEW_KEY2)); + + // add master key + @SuppressWarnings("unchecked") + Iterator masterIt = masterKeyRing.getSecretKeys(); + mKeys.add(masterIt.next()); + mKeysUsages.add(Id.choice.usage.sign_only); + + // add sub key + @SuppressWarnings("unchecked") + Iterator subIt = subKeyRing.getSecretKeys(); + subIt.next(); // masterkey + mKeys.add(subIt.next()); + mKeysUsages.add(Id.choice.usage.encrypt_only); + + buildLayout(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + serviceIntent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + saveHandler.showProgressDialog(this); + + // start service with intent + startService(serviceIntent); + } + } + } + } + + /** + * Handle intent action to edit existing key + * + * @param intent + */ + @SuppressWarnings("unchecked") + private void handleActionEditKey(Intent intent) { + Bundle extras = intent.getExtras(); + + mActionBar.setTitle(R.string.title_editKey); + + mCurrentPassPhrase = PgpMain.getEditPassPhrase(); + if (mCurrentPassPhrase == null) { + mCurrentPassPhrase = ""; + } + + if (mCurrentPassPhrase.equals("")) { + // check "no passphrase" checkbox and remove button + mNoPassphrase.setChecked(true); + mChangePassPhrase.setVisibility(View.GONE); + } + + if (extras != null) { + if (extras.containsKey(EXTRA_KEY_ID)) { + long keyId = extras.getLong(EXTRA_KEY_ID); + + if (keyId != 0) { + PGPSecretKey masterKey = null; + mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, keyId); + if (mKeyRing != null) { + masterKey = PgpHelper.getMasterKey(mKeyRing); + for (PGPSecretKey key : new IterableIterator( + mKeyRing.getSecretKeys())) { + mKeys.add(key); + mKeysUsages.add(-1); // get usage when view is created + } + } + if (masterKey != null) { + for (String userId : new IterableIterator(masterKey.getUserIDs())) { + Log.d(Constants.TAG, "Added userId " + userId); + mUserIds.add(userId); + } + } + } + } + } + } + + /** + * Shows the dialog to set a new passphrase + */ + private void showSetPassphraseDialog() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + + // set new returned passphrase! + mNewPassPhrase = data + .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); + + updatePassPhraseButtonText(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + // set title based on isPassphraseSet() + int title = -1; + if (isPassphraseSet()) { + title = R.string.title_changePassPhrase; + } else { + title = R.string.title_setPassPhrase; + } + + SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance( + messenger, title); + + setPassphraseDialog.show(getSupportFragmentManager(), "setPassphraseDialog"); + } + + /** + * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user + * id and key. + */ + private void buildLayout() { + // Build layout based on given userIds and keys + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); + mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); + mUserIdsView.setType(Id.type.user_id); + mUserIdsView.setUserIds(mUserIds); + container.addView(mUserIdsView); + mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); + mKeysView.setType(Id.type.key); + mKeysView.setKeys(mKeys, mKeysUsages); + container.addView(mKeysView); + + updatePassPhraseButtonText(); + } + + private long getMasterKeyId() { + if (mKeysView.getEditors().getChildCount() == 0) { + return 0; + } + return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyID(); + } + + public boolean isPassphraseSet() { + if (mNoPassphrase.isChecked()) { + return true; + } else if ((!mCurrentPassPhrase.equals("")) + || (mNewPassPhrase != null && !mNewPassPhrase.equals(""))) { + return true; + } else { + return false; + } + } + + private void saveClicked() { + try { + if (!isPassphraseSet()) { + throw new PgpMain.PgpGeneralException(this.getString(R.string.setAPassPhrase)); + } + + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_SAVE_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE, mCurrentPassPhrase); + data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase); + data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS, + getUserIds(mUserIdsView)); + ArrayList keys = getKeys(mKeysView); + data.putByteArray(KeychainIntentService.SAVE_KEYRING_KEYS, + PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys)); + data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES, + getKeysUsages(mKeysView)); + data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId()); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after saving is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, + R.string.progress_saving, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + Intent data = new Intent(); + data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, getMasterKeyId()); + ArrayList userIds = null; + try { + userIds = getUserIds(mUserIdsView); + } catch (PgpGeneralException e) { + Log.e(Constants.TAG, "exception while getting user ids", e); + } + data.putExtra(RESULT_EXTRA_USER_ID, userIds.get(0)); + setResult(RESULT_OK, data); + finish(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } catch (PgpMain.PgpGeneralException e) { + Toast.makeText(this, getString(R.string.errorMessage, e.getMessage()), + Toast.LENGTH_SHORT).show(); + } + } + + private void cancelClicked() { + setResult(RESULT_CANCELED); + finish(); + } + + /** + * Returns user ids from the SectionView + * + * @param userIdsView + * @return + */ + private ArrayList getUserIds(SectionView userIdsView) + throws PgpMain.PgpGeneralException { + ArrayList userIds = new ArrayList(); + + ViewGroup userIdEditors = userIdsView.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 PgpMain.PgpGeneralException( + this.getString(R.string.error_userIdNeedsAName)); + } catch (UserIdEditor.NoEmailException e) { + throw new PgpMain.PgpGeneralException( + this.getString(R.string.error_userIdNeedsAnEmailAddress)); + } catch (UserIdEditor.InvalidEmailException e) { + throw new PgpMain.PgpGeneralException(e.getMessage()); + } + + if (userId.equals("")) { + continue; + } + + if (editor.isMainUserId()) { + userIds.add(0, userId); + gotMainUserId = true; + } else { + userIds.add(userId); + } + } + + if (userIds.size() == 0) { + throw new PgpMain.PgpGeneralException(getString(R.string.error_keyNeedsAUserId)); + } + + if (!gotMainUserId) { + throw new PgpMain.PgpGeneralException( + getString(R.string.error_mainUserIdMustNotBeEmpty)); + } + + return userIds; + } + + /** + * Returns keys from the SectionView + * + * @param keysView + * @return + */ + private ArrayList getKeys(SectionView keysView) + throws PgpMain.PgpGeneralException { + ArrayList keys = new ArrayList(); + + ViewGroup keyEditors = keysView.getEditors(); + + if (keyEditors.getChildCount() == 0) { + throw new PgpMain.PgpGeneralException(getString(R.string.error_keyNeedsMasterKey)); + } + + for (int i = 0; i < keyEditors.getChildCount(); ++i) { + KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); + keys.add(editor.getValue()); + } + + return keys; + } + + /** + * Returns usage selections of keys from the SectionView + * + * @param keysView + * @return + */ + private ArrayList getKeysUsages(SectionView keysView) + throws PgpMain.PgpGeneralException { + ArrayList getKeysUsages = new ArrayList(); + + ViewGroup keyEditors = keysView.getEditors(); + + if (keyEditors.getChildCount() == 0) { + throw new PgpMain.PgpGeneralException(getString(R.string.error_keyNeedsMasterKey)); + } + + for (int i = 0; i < keyEditors.getChildCount(); ++i) { + KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i); + getKeysUsages.add(editor.getUsage()); + } + + return getKeysUsages; + } + + private void updatePassPhraseButtonText() { + mChangePassPhrase.setText(isPassphraseSet() ? R.string.btn_changePassPhrase + : R.string.btn_setPassPhrase); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java new file mode 100644 index 000000000..bb61fb890 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -0,0 +1,1105 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; +import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.util.Choice; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +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.File; +import java.util.Vector; + +public class EncryptActivity extends SherlockFragmentActivity { + + /* Intents */ + // without permission + public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT"; + public static final String ACTION_ENCRYPT_FILE = Constants.INTENT_PREFIX + "ENCRYPT_FILE"; + + // with permission + public static final String ACTION_ENCRYPT_AND_RETURN = Constants.INTENT_PREFIX + + "ENCRYPT_AND_RETURN"; + public static final String ACTION_GENERATE_SIGNATURE_AND_RETURN = Constants.INTENT_PREFIX + + "GENERATE_SIGNATURE_AND_RETURN"; + public static final String ACTION_ENCRYPT_STREAM_AND_RETURN = Constants.INTENT_PREFIX + + "ENCRYPT_STREAM_AND_RETURN"; + + /* EXTRA keys for input */ + public static final String EXTRA_TEXT = "text"; + public static final String EXTRA_DATA = "data"; + public static final String EXTRA_ASCII_ARMOUR = "asciiArmour"; + public static final String EXTRA_SEND_TO = "sendTo"; + public static final String EXTRA_SUBJECT = "subject"; + public static final String EXTRA_SIGNATURE_KEY_ID = "signatureKeyId"; + public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryptionKeyIds"; + + private String mSubject = null; + private String mSendTo = null; + + private long mEncryptionKeyIds[] = null; + + private boolean mEncryptImmediately = false; + private EditText mMessage = null; + private Button mSelectKeysButton = null; + + private boolean mEncryptEnabled = false; + private String mEncryptString = ""; + private boolean mEncryptToClipboardEnabled = false; + private String mEncryptToClipboardString = ""; + + 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 mAsciiArmorDemand = false; + private boolean mOverrideAsciiArmour = false; + private Uri mStreamAndReturnUri = null; + private byte[] mData = null; + + private boolean mGenerateSignature = false; + + private long mSecretKeyId = Id.key.none; + + private FileDialogFragment mFileDialog; + + /** + * ActionBar menu is created based on class variables to change it at runtime + * + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (mEncryptToClipboardEnabled) { + menu.add(1, Id.menu.option.encrypt_to_clipboard, 0, mEncryptToClipboardString) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + if (mEncryptEnabled) { + menu.add(1, Id.menu.option.encrypt, 1, mEncryptString).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.encrypt_to_clipboard: + encryptToClipboardClicked(); + + return true; + + case Id.menu.option.encrypt: + encryptClicked(); + + return true; + + default: + return super.onOptionsItemSelected(item); + + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check permissions for intent actions without user interaction + String[] restrictedActions = new String[] { ACTION_ENCRYPT_AND_RETURN, + ACTION_GENERATE_SIGNATURE_AND_RETURN, ACTION_ENCRYPT_STREAM_AND_RETURN }; + OtherHelper.checkPackagePermissionForActions(this, this.getCallingPackage(), + Constants.PERMISSION_ACCESS_API, getIntent().getAction(), restrictedActions); + + setContentView(R.layout.encrypt); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + initView(); + + // Handle intent actions + handleActions(getIntent()); + + updateView(); + updateSource(); + updateMode(); + + if (mEncryptImmediately) { + 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); + } + + updateActionBarButtons(); + + if (mEncryptImmediately + && (mMessage.getText().length() > 0 || mData != null) + && ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) || mSecretKeyId != 0)) { + encryptClicked(); + } + } + + /** + * Handles all actions with this intent + * + * @param intent + */ + private void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + String type = intent.getType(); + Uri uri = intent.getData(); + + if (extras == null) { + extras = new Bundle(); + } + + /* + * Android's Action + */ + if (Intent.ACTION_SEND.equals(action) && type != null) { + // When sending to APG Encrypt via share menu + if ("text/plain".equals(type)) { + // Plain text + String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); + if (sharedText != null) { + // handle like normal text encryption, override action and extras to later + // execute ACTION_ENCRYPT in main actions + extras.putString(EXTRA_TEXT, sharedText); + extras.putBoolean(EXTRA_ASCII_ARMOUR, true); + action = ACTION_ENCRYPT; + } + } else { + // Files via content provider, override uri and action + uri = (Uri) intent.getParcelableExtra(Intent.EXTRA_STREAM); + action = ACTION_ENCRYPT_FILE; + } + } + + if (ACTION_ENCRYPT_AND_RETURN.equals(action) + || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { + mEncryptImmediately = true; + } + + if (ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { + mGenerateSignature = true; + mOverrideAsciiArmour = true; + mAsciiArmorDemand = false; + } + + if (extras.containsKey(EXTRA_ASCII_ARMOUR)) { + mAsciiArmorDemand = extras.getBoolean(EXTRA_ASCII_ARMOUR, true); + mOverrideAsciiArmour = true; + mAsciiArmour.setChecked(mAsciiArmorDemand); + } + + mData = extras.getByteArray(EXTRA_DATA); + String textData = null; + if (mData == null) { + textData = extras.getString(EXTRA_TEXT); + } + mSendTo = extras.getString(EXTRA_SEND_TO); + mSubject = extras.getString(EXTRA_SUBJECT); + long signatureKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); + long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); + + // preselect keys given by intent + preselectKeys(signatureKeyId, encryptionKeyIds); + + /** + * Main Actions + */ + if (ACTION_ENCRYPT.equals(action) || ACTION_ENCRYPT_AND_RETURN.equals(action) + || ACTION_GENERATE_SIGNATURE_AND_RETURN.equals(action)) { + if (textData != null) { + mMessage.setText(textData); + } + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceMessage) { + mSource.showNext(); + } + } else if (ACTION_ENCRYPT_FILE.equals(action)) { + // get file path from uri + String path = FileHelper.getPath(this, uri); + + if (path != null) { + mInputFilename = path; + mFilename.setText(mInputFilename); + + mSource.setInAnimation(null); + mSource.setOutAnimation(null); + while (mSource.getCurrentView().getId() != R.id.sourceFile) { + mSource.showNext(); + } + } else { + Log.e(Constants.TAG, + "Direct binary data without actual file in filesystem is not supported. This is only supported by ACTION_ENCRYPT_STREAM_AND_RETURN."); + Toast.makeText(this, R.string.error_onlyFilesAreSupported, Toast.LENGTH_LONG) + .show(); + // end activity + finish(); + } + } else if (ACTION_ENCRYPT_STREAM_AND_RETURN.equals(action)) { + // TODO: Set mStreamAndReturnUri that is used later to encrypt a stream! + + mStreamAndReturnUri = uri; + } + } + + /** + * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those! + * + * @param preselectedSignatureKeyId + * @param preselectedEncryptionKeyIds + */ + private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) { + if (preselectedSignatureKeyId != 0) { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, + preselectedSignatureKeyId); + PGPSecretKey masterKey = null; + if (keyRing != null) { + masterKey = PgpHelper.getMasterKey(keyRing); + if (masterKey != null) { + Vector signKeys = PgpHelper.getUsableSigningKeys(keyRing); + if (signKeys.size() > 0) { + mSecretKeyId = masterKey.getKeyID(); + } + } + } + } + + if (preselectedEncryptionKeyIds != null) { + Vector goodIds = new Vector(); + for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) { + PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, + preselectedEncryptionKeyIds[i]); + PGPPublicKey masterKey = null; + if (keyRing == null) { + continue; + } + masterKey = PgpHelper.getMasterKey(keyRing); + if (masterKey == null) { + continue; + } + Vector encryptKeys = PgpHelper.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); + } + } + } + } + + /** + * Guess output filename based on input path + * + * @param path + * @return Suggestion for output filename + */ + private String guessOutputFilename(String path) { + // output in the same directory but with additional ending + File file = new File(path); + String ending = (mAsciiArmour.isChecked() ? ".asc" : ".gpg"); + String outputFilename = file.getParent() + File.separator + file.getName() + ending; + + return outputFilename; + } + + 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; + } + } + updateActionBarButtons(); + } + + /** + * Set ActionBar buttons based on parameters + * + * @param encryptEnabled + * @param encryptStringRes + * @param encryptToClipboardEnabled + * @param encryptToClipboardStringRes + */ + private void setActionbarButtons(boolean encryptEnabled, int encryptStringRes, + boolean encryptToClipboardEnabled, int encryptToClipboardStringRes) { + mEncryptEnabled = encryptEnabled; + if (encryptEnabled) { + mEncryptString = getString(encryptStringRes); + } + mEncryptToClipboardEnabled = encryptToClipboardEnabled; + if (encryptToClipboardEnabled) { + mEncryptToClipboardString = getString(encryptToClipboardStringRes); + } + + // build new action bar based on these class variables + invalidateOptionsMenu(); + } + + /** + * Update ActionBar buttons based on current selection in view + */ + private void updateActionBarButtons() { + switch (mSource.getCurrentView().getId()) { + case R.id.sourceFile: { + setActionbarButtons(true, R.string.btn_encryptFile, false, 0); + + break; + } + + case R.id.sourceMessage: { + mSourceLabel.setText(R.string.label_message); + + if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { + if (mEncryptImmediately) { + setActionbarButtons(true, R.string.btn_encrypt, false, 0); + } else { + setActionbarButtons(true, R.string.btn_encryptAndEmail, true, + R.string.btn_encryptToClipboard); + } + } else { + if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) { + if (mSecretKeyId == 0) { + setActionbarButtons(false, 0, false, 0); + } else { + if (mEncryptImmediately) { + setActionbarButtons(true, R.string.btn_sign, false, 0); + } else { + setActionbarButtons(true, R.string.btn_signAndEmail, true, + R.string.btn_signToClipboard); + } + } + } else { + if (mEncryptImmediately) { + setActionbarButtons(true, R.string.btn_encrypt, false, 0); + } else { + setActionbarButtons(true, R.string.btn_encryptAndEmail, true, + R.string.btn_encryptToClipboard); + } + } + } + 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; + } + } + updateActionBarButtons(); + } + + private void encryptToClipboardClicked() { + mEncryptTarget = Id.target.clipboard; + initiateEncryption(); + } + + private void encryptClicked() { + Log.d(Constants.TAG, "encryptClicked invoked!"); + + 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)) { + mInputFilename = mFilename.getText().toString(); + } + + mOutputFilename = guessOutputFilename(mInputFilename); + + 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 && mSecretKeyId == 0) { + Toast.makeText(this, R.string.selectEncryptionOrSignatureKey, Toast.LENGTH_SHORT) + .show(); + return; + } + + if (mSecretKeyId != 0 + && PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { + showPassphraseDialog(); + + return; + } + } + + if (mEncryptTarget == Id.target.file) { + showOutputFileDialog(); + } else { + encryptStart(); + } + } + + /** + * Shows passphrase dialog to cache a new passphrase the user enters for using it later for + * encryption + */ + private void showPassphraseDialog() { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + if (mEncryptTarget == Id.target.file) { + showOutputFileDialog(); + } else { + encryptStart(); + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( + EncryptActivity.this, messenger, mSecretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PgpMain.PgpGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + private void showOutputFileDialog() { + // Message is received after file is selected + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + encryptStart(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + mFileDialog = FileDialogFragment.newInstance(messenger, + getString(R.string.title_encryptToFile), + getString(R.string.specifyFileToEncryptTo), mOutputFilename, null, + Id.request.output_filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + + private void encryptStart() { + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + boolean useAsciiArmor = true; + long encryptionKeyIds[] = null; + int compressionId = 0; + boolean signOnly = false; + + if (mMode.getCurrentView().getId() == R.id.modeSymmetric) { + Log.d(Constants.TAG, "Symmetric encryption enabled!"); + String passPhrase = mPassPhrase.getText().toString(); + if (passPhrase.length() == 0) { + passPhrase = null; + } + + data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase); + } else { + encryptionKeyIds = mEncryptionKeyIds; + signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0); + } + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_ENCRYPT_SIGN); + + // choose default settings, target and data bundle by target + if (mStreamAndReturnUri != null) { + // mIntentDataUri is only defined when ACTION_ENCRYPT_STREAM_AND_RETURN is used + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_STREAM); + data.putParcelable(KeychainIntentService.ENCRYPT_PROVIDER_URI, mStreamAndReturnUri); + + } else if (mEncryptTarget == Id.target.file) { + useAsciiArmor = mAsciiArmour.isChecked(); + compressionId = ((Choice) mFileCompression.getSelectedItem()).getId(); + + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_FILE); + + Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename=" + + mOutputFilename); + + data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename); + data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename); + + } else { + useAsciiArmor = true; + compressionId = Preferences.getPreferences(this).getDefaultMessageCompression(); + + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES); + + if (mData != null) { + data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mData); + } else { + String message = mMessage.getText().toString(); + if (signOnly && !mEncryptImmediately) { + fixBadCharactersForGmail(message); + } + data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes()); + } + } + + if (mOverrideAsciiArmour) { + useAsciiArmor = mAsciiArmorDemand; + } + + data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId); + data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_AMOR, useAsciiArmor); + data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, encryptionKeyIds); + data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId); + data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature); + data.putBoolean(KeychainIntentService.ENCRYPT_SIGN_ONLY, signOnly); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after encrypting is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, + R.string.progress_encrypting, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle data = message.getData(); + + String output; + switch (mEncryptTarget) { + case Id.target.clipboard: + output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING); + Log.d(Constants.TAG, "output: " + output); + ClipboardReflection.copyToClipboard(EncryptActivity.this, output); + Toast.makeText(EncryptActivity.this, + R.string.encryptionToClipboardSuccessful, Toast.LENGTH_SHORT) + .show(); + break; + + case Id.target.email: + if (mEncryptImmediately) { + Intent intent = new Intent(); + intent.putExtras(data); + setResult(RESULT_OK, intent); + finish(); + return; + } + + output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING); + Log.d(Constants.TAG, "output: " + output); + + Intent emailIntent = new Intent(Intent.ACTION_SEND); + emailIntent.setType("text/plain; charset=utf-8"); + emailIntent.putExtra(Intent.EXTRA_TEXT, output); + if (mSubject != null) { + emailIntent.putExtra(Intent.EXTRA_SUBJECT, mSubject); + } + if (mSendTo != null) { + emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { mSendTo }); + } + startActivity(Intent.createChooser(emailIntent, + getString(R.string.title_sendEmail))); + break; + + case Id.target.file: + Toast.makeText(EncryptActivity.this, R.string.encryptionSuccessful, + Toast.LENGTH_SHORT).show(); + + if (mDeleteAfter.isChecked()) { + // Create and show dialog to delete original file + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment + .newInstance(mInputFilename); + deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); + } + break; + + default: + // shouldn't happen + break; + + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + /** + * Fixes bad message characters for gmail + * + * @param message + * @return + */ + private String fixBadCharactersForGmail(String message) { + // 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"); + + return message; + } + + private void initView() { + 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); + 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) { + FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*", + Id.request.filename); + } + }); + + 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 = Preferences.getPreferences(this).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(Preferences.getPreferences(this).getDefaultAsciiArmour()); + + 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 { + mSecretKeyId = Id.key.none; + updateView(); + } + } + }); + } + + 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 (mSecretKeyId == Id.key.none) { + mSign.setChecked(false); + mMainUserId.setText(""); + mMainUserIdRest.setText(""); + } else { + String uid = getResources().getString(R.string.unknownUserId); + String uidExtra = ""; + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, + mSecretKeyId); + if (keyRing != null) { + PGPSecretKey key = PgpHelper.getMasterKey(keyRing); + if (key != null) { + String userId = PgpHelper.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); + } + + updateActionBarButtons(); + } + + private void selectPublicKeys() { + Intent intent = new Intent(this, SelectPublicKeyActivity.class); + Vector keyIds = new Vector(); + if (mSecretKeyId != 0) { + keyIds.add(mSecretKeyId); + } + 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(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds); + startActivityForResult(intent, Id.request.public_keys); + } + + private void selectSecretKey() { + Intent intent = new Intent(this, SelectSecretKeyActivity.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) { + try { + String path = FileHelper.getPath(this, data.getData()); + Log.d(Constants.TAG, "path=" + path); + + mFilename.setText(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + case Id.request.output_filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + mFileDialog.setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!"); + } + } + return; + } + + case Id.request.public_keys: { + if (resultCode == RESULT_OK) { + Bundle bundle = data.getExtras(); + mEncryptionKeyIds = bundle + .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS); + } + updateView(); + break; + } + + case Id.request.secret_keys: { + if (resultCode == RESULT_OK) { + Bundle bundle = data.getExtras(); + mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); + } else { + mSecretKeyId = Id.key.none; + } + updateView(); + break; + } + + default: { + break; + } + } + + super.onActivityResult(requestCode, resultCode, data); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java new file mode 100644 index 000000000..5b2750742 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.R; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.widget.TextView; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.ActionBar.Tab; +import com.actionbarsherlock.view.MenuItem; + +import java.util.ArrayList; + +import android.content.Context; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +public class HelpActivity extends SherlockFragmentActivity { + public static final String EXTRA_SELECTED_TAB = "selectedTab"; + + ViewPager mViewPager; + TabsAdapter mTabsAdapter; + TextView tabCenter; + TextView tabText; + + /** + * Menu Items + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mViewPager = new ViewPager(this); + mViewPager.setId(R.id.pager); + + setContentView(mViewPager); + ActionBar bar = getSupportActionBar(); + bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + bar.setDisplayShowTitleEnabled(true); + bar.setDisplayHomeAsUpEnabled(true); + + mTabsAdapter = new TabsAdapter(this, mViewPager); + + int selectedTab = 0; + Intent intent = getIntent(); + if (intent.getExtras() != null && intent.getExtras().containsKey(EXTRA_SELECTED_TAB)) { + selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB); + } + + Bundle startBundle = new Bundle(); + startBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_start); + mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_start)), + HelpFragmentHtml.class, startBundle, (selectedTab == 0 ? true : false)); + + Bundle nfcBundle = new Bundle(); + nfcBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_nfc_beam); + mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_nfc_beam)), + HelpFragmentHtml.class, nfcBundle, (selectedTab == 1 ? true : false)); + + Bundle changelogBundle = new Bundle(); + changelogBundle.putInt(HelpFragmentHtml.ARG_HTML_FILE, R.raw.help_changelog); + mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_changelog)), + HelpFragmentHtml.class, changelogBundle, (selectedTab == 2 ? true : false)); + + mTabsAdapter.addTab(bar.newTab().setText(getString(R.string.help_tab_about)), + HelpFragmentAbout.class, null, (selectedTab == 3 ? true : false)); + } + + public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, + ViewPager.OnPageChangeListener { + private final Context mContext; + private final ActionBar mActionBar; + private final ViewPager mViewPager; + private final ArrayList mTabs = new ArrayList(); + + static final class TabInfo { + private final Class clss; + private final Bundle args; + + TabInfo(Class _class, Bundle _args) { + clss = _class; + args = _args; + } + } + + public TabsAdapter(SherlockFragmentActivity activity, ViewPager pager) { + super(activity.getSupportFragmentManager()); + mContext = activity; + mActionBar = activity.getSupportActionBar(); + mViewPager = pager; + mViewPager.setAdapter(this); + mViewPager.setOnPageChangeListener(this); + } + + public void addTab(ActionBar.Tab tab, Class clss, Bundle args, boolean selected) { + TabInfo info = new TabInfo(clss, args); + tab.setTag(info); + tab.setTabListener(this); + mTabs.add(info); + mActionBar.addTab(tab, selected); + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mTabs.size(); + } + + @Override + public Fragment getItem(int position) { + TabInfo info = mTabs.get(position); + return Fragment.instantiate(mContext, info.clss.getName(), info.args); + } + + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + public void onPageSelected(int position) { + mActionBar.setSelectedNavigationItem(position); + } + + public void onPageScrollStateChanged(int state) { + } + + public void onTabSelected(Tab tab, FragmentTransaction ft) { + Object tag = tab.getTag(); + for (int i = 0; i < mTabs.size(); i++) { + if (mTabs.get(i) == tag) { + mViewPager.setCurrentItem(i); + } + } + } + + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + } + + public void onTabReselected(Tab tab, FragmentTransaction ft) { + } + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java new file mode 100644 index 000000000..05891b053 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui; + +import java.io.IOException; +import java.io.InputStream; + +import net.nightwhistler.htmlspanner.HtmlSpanner; +import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import com.actionbarsherlock.app.SherlockFragment; + +public class HelpFragmentAbout extends SherlockFragment { + + /** + * Workaround for Android Bug. See + * http://stackoverflow.com/questions/8748064/starting-activity-from + * -fragment-causes-nullpointerexception + */ + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + setUserVisibleHint(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.help_fragment_about, container, false); + + // load html from html file from /res/raw + InputStream inputStreamText = getResources().openRawResource(R.raw.help_about); + + TextView versionText = (TextView) view.findViewById(R.id.help_about_version); + versionText.setText(getString(R.string.help_about_version) + " " + getVersion()); + + JellyBeanSpanFixTextView aboutTextView = (JellyBeanSpanFixTextView) view + .findViewById(R.id.help_about_text); + + // load html into textview + HtmlSpanner htmlSpanner = new HtmlSpanner(); + htmlSpanner.setStripExtraWhiteSpace(true); + try { + aboutTextView.setText(htmlSpanner.fromHtml(inputStreamText)); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while reading raw resources as stream", e); + } + + // make links work + aboutTextView.setMovementMethod(LinkMovementMethod.getInstance()); + + // no flickering when clicking textview for Android < 4 + aboutTextView.setTextColor(getResources().getColor(android.R.color.black)); + + return view; + } + + /** + * Get the current package version. + * + * @return The current version. + */ + private String getVersion() { + String result = ""; + try { + PackageManager manager = getActivity().getPackageManager(); + PackageInfo info = manager.getPackageInfo(getActivity().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; + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentHtml.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentHtml.java new file mode 100644 index 000000000..9c0bb6ded --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentHtml.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui; + +import java.io.IOException; +import java.io.InputStream; + +import net.nightwhistler.htmlspanner.HtmlSpanner; +import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.app.Activity; +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ScrollView; + +import com.actionbarsherlock.app.SherlockFragment; + +public class HelpFragmentHtml extends SherlockFragment { + private Activity mActivity; + + private int htmlFile; + + public static final String ARG_HTML_FILE = "htmlFile"; + + /** + * Create a new instance of HelpFragmentHtml, providing "htmlFile" as an argument. + */ + static HelpFragmentHtml newInstance(int htmlFile) { + HelpFragmentHtml f = new HelpFragmentHtml(); + + // Supply html raw file input as an argument. + Bundle args = new Bundle(); + args.putInt(ARG_HTML_FILE, htmlFile); + f.setArguments(args); + + return f; + } + + /** + * Workaround for Android Bug. See + * http://stackoverflow.com/questions/8748064/starting-activity-from + * -fragment-causes-nullpointerexception + */ + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + setUserVisibleHint(true); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + htmlFile = getArguments().getInt(ARG_HTML_FILE); + + // load html from html file from /res/raw + InputStream inputStreamText = getResources().openRawResource(htmlFile); + + mActivity = getActivity(); + + ScrollView scroller = new ScrollView(mActivity); + JellyBeanSpanFixTextView text = new JellyBeanSpanFixTextView(mActivity); + + // padding + int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, mActivity + .getResources().getDisplayMetrics()); + text.setPadding(padding, padding, padding, 0); + + scroller.addView(text); + + // load html into textview + HtmlSpanner htmlSpanner = new HtmlSpanner(); + htmlSpanner.setStripExtraWhiteSpace(true); + try { + text.setText(htmlSpanner.fromHtml(inputStreamText)); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while reading raw resources as stream", e); + } + + // make links work + text.setMovementMethod(LinkMovementMethod.getInstance()); + + // no flickering when clicking textview for Android < 4 + text.setTextColor(getResources().getColor(android.R.color.black)); + + return scroller; + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java new file mode 100644 index 000000000..cd5a12df0 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2011 Senecaso + * + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; + + +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.View; +import android.widget.Toast; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.google.zxing.integration.android.IntentIntegrator; +import com.google.zxing.integration.android.IntentResult; + +public class ImportKeysActivity extends SherlockFragmentActivity { + public static final String ACTION_IMPORT = Constants.INTENT_PREFIX + "IMPORT"; + public static final String ACTION_IMPORT_FROM_FILE = Constants.INTENT_PREFIX + + "IMPORT_FROM_FILE"; + public static final String ACTION_IMPORT_FROM_QR_CODE = Constants.INTENT_PREFIX + + "IMPORT_FROM_QR_CODE"; + public static final String ACTION_IMPORT_FROM_NFC = Constants.INTENT_PREFIX + "IMPORT_FROM_NFC"; + + // only used by IMPORT + public static final String EXTRA_TEXT = "text"; + public static final String EXTRA_KEYRING_BYTES = "keyringBytes"; + + // public static final String EXTRA_KEY_ID = "keyId"; + + protected String mImportFilename; + protected byte[] mImportData; + + protected boolean mDeleteAfterImport = false; + + FileDialogFragment mFileDialog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.import_keys); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + + handleActions(getIntent()); + } + + /** + * ActionBar menu is created based on class variables to change it at runtime + * + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(1, Id.menu.option.import_from_file, 0, R.string.menu_importFromFile) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.import_from_qr_code, 1, R.string.menu_importFromQrCode) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.import_from_nfc, 2, R.string.menu_importFromNfc) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.import_from_file: + showImportFromFileDialog(); + return true; + + case Id.menu.option.import_from_qr_code: + importFromQrCode(); + return true; + + case Id.menu.option.import_from_nfc: + importFromNfc(); + return true; + + default: + return super.onOptionsItemSelected(item); + + } + } + + protected void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + + if (extras == null) { + extras = new Bundle(); + } + + /** + * Android Standard Actions + */ + if (Intent.ACTION_VIEW.equals(action)) { + // Android's Action when opening file associated to APG (see AndroidManifest.xml) + // override action to delegate it to APGs ACTION_IMPORT + action = ACTION_IMPORT; + } + + /** + * APG's own Actions + */ + if (ACTION_IMPORT.equals(action)) { + if ("file".equals(intent.getScheme()) && intent.getDataString() != null) { + mImportFilename = intent.getData().getPath(); + mImportData = null; + } else if (extras.containsKey(EXTRA_TEXT)) { + mImportData = intent.getStringExtra(EXTRA_TEXT).getBytes(); + mImportFilename = null; + } else if (extras.containsKey(EXTRA_KEYRING_BYTES)) { + mImportData = intent.getByteArrayExtra(EXTRA_KEYRING_BYTES); + mImportFilename = null; + } + loadKeyListFragment(); + } else if (ACTION_IMPORT_FROM_FILE.equals(action)) { + if ("file".equals(intent.getScheme()) && intent.getDataString() != null) { + mImportFilename = intent.getData().getPath(); + mImportData = null; + } + showImportFromFileDialog(); + } else if (ACTION_IMPORT_FROM_QR_CODE.equals(action)) { + importFromQrCode(); + } else if (ACTION_IMPORT_FROM_NFC.equals(action)) { + importFromNfc(); + } + } + + public void loadKeyListFragment() { + if (mImportData != null || mImportFilename != null) { + // generate list of keyrings + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + + ImportKeysListFragment listFragment = new ImportKeysListFragment(); + Bundle args = new Bundle(); + args.putByteArray(ImportKeysListFragment.ARG_KEYRING_BYTES, mImportData); + args.putString(ImportKeysListFragment.ARG_IMPORT_FILENAME, mImportFilename); + listFragment.setArguments(args); + // replace container in view with fragment + fragmentTransaction.replace(R.id.import_keys_list_container, listFragment); + fragmentTransaction.commit(); + } + } + + private void importFromQrCode() { + new IntentIntegrator(this).initiateScan(); + } + + private void importFromNfc() { + // show nfc help + Intent intent = new Intent(this, HelpActivity.class); + intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 1); + startActivityForResult(intent, 0); + } + + /** + * Show to dialog from where to import keys + */ + public void showImportFromFileDialog() { + // Message is received after file is selected + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mImportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + mDeleteAfterImport = data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED); + + Log.d(Constants.TAG, "mImportFilename: " + mImportFilename); + Log.d(Constants.TAG, "mDeleteAfterImport: " + mDeleteAfterImport); + + loadKeyListFragment(); + } + } + }; + + // Create a new Messenger for the communication back + final Messenger messenger = new Messenger(returnHandler); + + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { + public void run() { + mFileDialog = FileDialogFragment.newInstance(messenger, + getString(R.string.title_importKeys), + getString(R.string.specifyFileToImportFrom), Constants.path.APP_DIR + "/", + null, Id.request.filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + }); + } + + // private void importAndSignOld(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 + // + // // TODO: there should be only 1 + // HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]); + // String encodedKey = server.get(keyId); + // + // PGPKeyRing keyring = PGPHelper.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 = PGPHelper.convertFingerprintToHex(publicKeyRing + // .getPublicKey().getFingerprint()); + // if (expectedFingerprint.equals(actualFingerprint)) { + // // store the signed key in our local cache + // int retval = PGPMain.storeKeyRingInCache(publicKeyRing); + // if (retval != Id.return_value.ok + // && retval != Id.return_value.updated) { + // status.putString(EXTRA_ERROR, + // "Failed to store signed key in local cache"); + // } else { + // Intent intent = new Intent(ImportFromQRCodeActivity.this, + // SignKeyActivity.class); + // intent.putExtra(EXTRA_KEY_ID, keyId); + // startActivityForResult(intent, Id.request.sign_key); + // } + // } else { + // status.putString( + // 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(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(EXTRA_ERROR, "Failed to query KeyServer"); + // status.putInt(Constants.extras.STATUS, Id.message.done); + // } + // } + // }; + // + // t.setName("KeyExchange Download Thread"); + // t.setDaemon(true); + // t.start(); + // } + // } + + public void scanAgainOnClick(View view) { + new IntentIntegrator(this).initiateScan(); + } + + public void finishOnClick(View view) { + finish(); + } + + public void importOnClick(View view) { + Log.d(Constants.TAG, "Import key button clicked!"); + + importKeys(); + } + + /** + * Import keys with mImportData + */ + public void importKeys() { + if (mImportData != null || mImportFilename != null) { + Log.d(Constants.TAG, "importKeys started"); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_IMPORT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + + // TODO: check for key type? + // data.putInt(ApgIntentService.IMPORT_KEY_TYPE, Id.type.secret_key); + + if (mImportData != null) { + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES); + data.putByteArray(KeychainIntentService.IMPORT_BYTES, mImportData); + } else { + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_FILE); + data.putString(KeychainIntentService.IMPORT_FILENAME, mImportFilename); + } + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after importing is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, + R.string.progress_importing, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + int added = returnData.getInt(KeychainIntentService.RESULT_IMPORT_ADDED); + int updated = returnData.getInt(KeychainIntentService.RESULT_IMPORT_UPDATED); + int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD); + String toastMessage; + if (added > 0 && updated > 0) { + toastMessage = getString(R.string.keysAddedAndUpdated, added, updated); + } else if (added > 0) { + toastMessage = getString(R.string.keysAdded, added); + } else if (updated > 0) { + toastMessage = getString(R.string.keysUpdated, updated); + } else { + toastMessage = getString(R.string.noKeysAddedOrUpdated); + } + Toast.makeText(ImportKeysActivity.this, toastMessage, Toast.LENGTH_SHORT) + .show(); + if (bad > 0) { + AlertDialog.Builder alert = new AlertDialog.Builder( + ImportKeysActivity.this); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.warning); + alert.setMessage(ImportKeysActivity.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 + DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment + .newInstance(mImportFilename); + deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog"); + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } else { + Toast.makeText(this, R.string.error_nothingImport, Toast.LENGTH_LONG).show(); + } + } + + public void signAndUploadOnClick(View view) { + // first, import! + importOnClick(view); + + // TODO: implement sign and upload! + Toast.makeText(ImportKeysActivity.this, "Not implemented right now!", Toast.LENGTH_SHORT) + .show(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + // set filename used in export/import dialogs + mFileDialog.setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); + } + } + return; + } + case IntentIntegrator.REQUEST_CODE: { + IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, + data); + if (scanResult != null && scanResult.getFormatName() != null) { + + // mScannedContent = scanResult.getContents(); + + mImportData = scanResult.getContents().getBytes(); + mImportFilename = null; + + // mContentView.setText(mScannedContent); + // String[] bits = 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; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + } + } + } + + @Override + protected void onResume() { + super.onResume(); + + loadKeyListFragment(); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java new file mode 100644 index 000000000..170b459be --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.ui.widget.ImportKeysListLoader; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockListFragment; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.View; +import android.widget.ListView; +import android.widget.SimpleAdapter; + +public class ImportKeysListFragment extends SherlockListFragment implements + LoaderManager.LoaderCallbacks>> { + public static String ARG_KEYRING_BYTES = "bytes"; + public static String ARG_IMPORT_FILENAME = "filename"; + + byte[] mKeyringBytes; + String mImportFilename; + + private Activity mActivity; + private SimpleAdapter mAdapter; + + @Override + public void onListItemClick(ListView listView, View view, int position, long id) { + // Map item = (Map) listView.getItemAtPosition(position); + // String userId = item.get(ImportKeysListLoader.MAP_ATTR_USER_ID); + } + + /** + * Resume is called after rotating + */ + @Override + public void onResume() { + super.onResume(); + + // Start out with a progress indicator. + setListShown(false); + + // reload list + getLoaderManager().restartLoader(0, null, this); + } + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mActivity = this.getActivity(); + + mKeyringBytes = getArguments().getByteArray(ARG_KEYRING_BYTES); + mImportFilename = getArguments().getString(ARG_IMPORT_FILENAME); + + // register long press context menu + registerForContextMenu(getListView()); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(mActivity.getString(R.string.error_nothingImport)); + + // Create an empty adapter we will use to display the loaded data. + String[] from = new String[] {}; + int[] to = new int[] {}; + List> data = new ArrayList>(); + mAdapter = new SimpleAdapter(getActivity(), data, android.R.layout.two_line_list_item, + from, to); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override + public Loader>> onCreateLoader(int id, Bundle args) { + return new ImportKeysListLoader(mActivity, mKeyringBytes, mImportFilename); + } + + @Override + public void onLoadFinished(Loader>> loader, + List> data) { + // Set the new data in the adapter. + // for (String item : data) { + // mAdapter.add(item); + // } + Log.d(Constants.TAG, "data: " + data); + // TODO: real swapping the data to the already defined adapter doesn't work + // Workaround: recreate adapter! + // http://stackoverflow.com/questions/2356091/android-add-function-of-arrayadapter-not-working + // mAdapter = new ArrayAdapter(mActivity, android.R.layout.two_line_list_item, + // data); + + String[] from = new String[] { ImportKeysListLoader.MAP_ATTR_USER_ID, + ImportKeysListLoader.MAP_ATTR_FINGERPINT }; + int[] to = new int[] { android.R.id.text1, android.R.id.text2 }; + mAdapter = new SimpleAdapter(getActivity(), data, android.R.layout.two_line_list_item, + from, to); + + mAdapter.notifyDataSetChanged(); + setListAdapter(mAdapter); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader>> loader) { + // Clear the data in the adapter. + // Not available in SimpleAdapter! + // mAdapter.clear(); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java new file mode 100644 index 000000000..a128263c1 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.app.ProgressDialog; +import android.app.SearchManager; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.widget.Toast; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +public class KeyListActivity extends SherlockFragmentActivity { + + protected String mExportFilename = Constants.path.APP_DIR + "/"; + + protected String mImportData; + protected boolean mDeleteAfterImport = false; + + protected int mKeyType; + + FileDialogFragment mFileDialog; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + handleActions(getIntent()); + } + + // TODO: needed? + // @Override + // protected void onNewIntent(Intent intent) { + // super.onNewIntent(intent); + // handleActions(intent); + // } + + protected void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + + if (extras == null) { + extras = new Bundle(); + } + + /** + * Android Standard Actions + */ + String searchString = null; + if (Intent.ACTION_SEARCH.equals(action)) { + searchString = extras.getString(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); + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.filename: { + if (resultCode == RESULT_OK && data != null) { + try { + String path = data.getData().getPath(); + Log.d(Constants.TAG, "path=" + path); + + // set filename used in export/import dialogs + mFileDialog.setFilename(path); + } catch (NullPointerException e) { + Log.e(Constants.TAG, "Nullpointer while retrieving path!", e); + } + } + return; + } + + default: { + break; + } + } + super.onActivityResult(requestCode, resultCode, data); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + // TODO: reimplement! + // menu.add(3, Id.menu.option.search, 0, R.string.menu_search) + // .setIcon(R.drawable.ic_menu_search).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + menu.add(0, Id.menu.option.import_from_file, 5, R.string.menu_importFromFile) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(0, Id.menu.option.export_keys, 6, R.string.menu_exportKeys).setShowAsAction( + MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.import_from_file: { + Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); + intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_FILE); + startActivityForResult(intentImportFromFile, 0); + return true; + } + + case Id.menu.option.export_keys: { + showExportKeysDialog(-1); + return true; + } + + // case Id.menu.option.search: + // startSearch("", false, null, false); + // return true; + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + /** + * Show dialog where to export keys + * + * @param keyRingMasterKeyId + * if -1 export all keys + */ + public void showExportKeysDialog(final long keyRingMasterKeyId) { + // Message is received after file is selected + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == FileDialogFragment.MESSAGE_OKAY) { + Bundle data = message.getData(); + mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); + + exportKeys(keyRingMasterKeyId); + } + } + }; + + // Create a new Messenger for the communication back + final Messenger messenger = new Messenger(returnHandler); + + DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { + public void run() { + String title = null; + if (keyRingMasterKeyId != -1) { + // single key export + title = getString(R.string.title_exportKey); + } else { + title = getString(R.string.title_exportKeys); + } + + String message = null; + if (mKeyType == Id.type.public_key) { + message = getString(R.string.specifyFileToExportTo); + } else { + message = getString(R.string.specifyFileToExportSecretKeysTo); + } + + mFileDialog = FileDialogFragment.newInstance(messenger, title, message, + mExportFilename, null, Id.request.filename); + + mFileDialog.show(getSupportFragmentManager(), "fileDialog"); + } + }); + } + + /** + * Show dialog to delete key + * + * @param keyRingId + */ + public void showDeleteKeyDialog(long keyRingId) { + // Message is received after key is deleted + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { + // no further actions needed + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, + keyRingId, mKeyType); + + deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); + } + + /** + * Export keys + * + * @param keyRingMasterKeyId + * if -1 export all keys + */ + public void exportKeys(long keyRingMasterKeyId) { + Log.d(Constants.TAG, "exportKeys started"); + + // Send all information needed to service to export key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_EXPORT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + + data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename); + data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, mKeyType); + + if (keyRingMasterKeyId == -1) { + data.putBoolean(KeychainIntentService.EXPORT_ALL, true); + } else { + data.putLong(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, keyRingMasterKeyId); + } + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after exporting is done in ApgService + KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(this, + R.string.progress_exporting, ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + int exported = returnData.getInt(KeychainIntentService.RESULT_EXPORT); + String toastMessage; + if (exported == 1) { + toastMessage = getString(R.string.keyExported); + } else if (exported > 0) { + toastMessage = getString(R.string.keysExported, exported); + } else { + toastMessage = getString(R.string.noKeysExported); + } + Toast.makeText(KeyListActivity.this, toastMessage, Toast.LENGTH_SHORT).show(); + + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(exportHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + exportHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java new file mode 100644 index 000000000..9e90e254c --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.widget.ExpandableListFragment; +import org.sufficientlysecure.keychain.R; + +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.View; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; + +public class KeyListFragment extends ExpandableListFragment { + protected KeyListActivity mKeyListActivity; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mKeyListActivity = (KeyListActivity) getActivity(); + + // register long press context menu + registerForContextMenu(getListView()); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.listEmpty)); + } + + /** + * Context Menu on Long Click + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(0, Id.menu.export, 5, R.string.menu_exportKey); + menu.add(0, Id.menu.delete, 111, R.string.menu_deleteKey); + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); + + // expInfo.id would also return row id of childs, but we always want to get the row id of + // the group item, thus we are using the following way + int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); + long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); + + switch (item.getItemId()) { + case Id.menu.export: + long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); + if (masterKeyId == -1) { + masterKeyId = ProviderHelper.getSecretMasterKeyId(mKeyListActivity, keyRingRowId); + } + + mKeyListActivity.showExportKeysDialog(masterKeyId); + return true; + + case Id.menu.delete: + mKeyListActivity.showDeleteKeyDialog(keyRingRowId); + return true; + + default: + return super.onContextItemSelected(item); + + } + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java new file mode 100644 index 000000000..adc8656d0 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; + +public class KeyListPublicActivity extends KeyListActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mKeyType = Id.type.public_key; + + setContentView(R.layout.key_list_public_activity); + + mExportFilename = Constants.path.APP_DIR + "/pubexport.asc"; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(1, Id.menu.option.key_server, 1, R.string.menu_keyServer) + .setIcon(R.drawable.ic_menu_search_list) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS); + menu.add(1, Id.menu.option.import_from_qr_code, 2, R.string.menu_importFromQrCode) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.import_from_nfc, 3, R.string.menu_importFromNfc) + .setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case Id.menu.option.key_server: { + startActivityForResult(new Intent(this, KeyServerQueryActivity.class), 0); + + return true; + } + case Id.menu.option.import_from_file: { + Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); + intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_FILE); + startActivityForResult(intentImportFromFile, 0); + + return true; + } + + case Id.menu.option.import_from_qr_code: { + Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); + intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_QR_CODE); + startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code); + + return true; + } + + case Id.menu.option.import_from_nfc: { + Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class); + intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_FROM_NFC); + startActivityForResult(intentImportFromFile, 0); + + 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(KeyServerQueryActivity.RESULT_EXTRA_TEXT) == null) { + // return; + // } + // + // Intent intent = new Intent(this, KeyListPublicActivity.class); + // intent.setAction(KeyListPublicActivity.ACTION_IMPORT); + // intent.putExtra(KeyListPublicActivity.EXTRA_TEXT, + // data.getStringExtra(KeyListActivity.EXTRA_TEXT)); + // handleActions(intent); + // break; + // } + // + // default: { + // super.onActivityResult(requestCode, resultCode, data); + // break; + // } + // } + // } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java new file mode 100644 index 000000000..648b09ef7 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.ui.widget.KeyListAdapter; +import org.sufficientlysecure.keychain.R; + +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.ContextMenu; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; + +public class KeyListPublicFragment extends KeyListFragment implements + LoaderManager.LoaderCallbacks { + + private KeyListPublicActivity mKeyListPublicActivity; + + private KeyListAdapter mAdapter; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mKeyListPublicActivity = (KeyListPublicActivity) getActivity(); + + mAdapter = new KeyListAdapter(mKeyListPublicActivity, null, Id.type.public_key); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + // id is -1 as the child cursors are numbered 0,...,n + getLoaderManager().initLoader(-1, null, this); + } + + /** + * Context Menu on Long Click + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(0, Id.menu.update, 1, R.string.menu_updateKey); + menu.add(0, Id.menu.signKey, 2, R.string.menu_signKey); + menu.add(0, Id.menu.exportToServer, 3, R.string.menu_exportKeyToServer); + menu.add(0, Id.menu.share, 6, R.string.menu_share); + menu.add(0, Id.menu.share_qr_code, 7, R.string.menu_shareQrCode); + menu.add(0, Id.menu.share_nfc, 8, R.string.menu_shareNfc); + + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); + + // expInfo.id would also return row id of childs, but we always want to get the row id of + // the group item, thus we are using the following way + int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); + long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); + + switch (item.getItemId()) { + case Id.menu.update: + long updateKeyId = 0; + PGPPublicKeyRing updateKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId( + mKeyListActivity, keyRingRowId); + if (updateKeyRing != null) { + updateKeyId = PgpHelper.getMasterKey(updateKeyRing).getKeyID(); + } + if (updateKeyId == 0) { + // this shouldn't happen + return true; + } + + Intent queryIntent = new Intent(mKeyListActivity, KeyServerQueryActivity.class); + queryIntent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID_AND_RETURN); + queryIntent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, updateKeyId); + + // TODO: lookup?? + startActivityForResult(queryIntent, Id.request.look_up_key_id); + + return true; + + case Id.menu.exportToServer: + Intent uploadIntent = new Intent(mKeyListActivity, KeyServerUploadActivity.class); + uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER); + uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, keyRingRowId); + startActivityForResult(uploadIntent, Id.request.export_to_server); + + return true; + + case Id.menu.signKey: + long keyId = 0; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByRowId( + mKeyListActivity, keyRingRowId); + if (signKeyRing != null) { + keyId = PgpHelper.getMasterKey(signKeyRing).getKeyID(); + } + if (keyId == 0) { + // this shouldn't happen + return true; + } + + Intent signIntent = new Intent(mKeyListActivity, SignKeyActivity.class); + signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId); + startActivity(signIntent); + + return true; + + case Id.menu.share_qr_code: + // get master key id using row id + long masterKeyId = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); + + Intent qrCodeIntent = new Intent(mKeyListActivity, ShareActivity.class); + qrCodeIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING_WITH_QR_CODE); + qrCodeIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId); + startActivityForResult(qrCodeIntent, 0); + + return true; + + case Id.menu.share_nfc: + // get master key id using row id + long masterKeyId2 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); + + Intent nfcIntent = new Intent(mKeyListActivity, ShareNfcBeamActivity.class); + nfcIntent.setAction(ShareNfcBeamActivity.ACTION_SHARE_KEYRING_WITH_NFC); + nfcIntent.putExtra(ShareNfcBeamActivity.EXTRA_MASTER_KEY_ID, masterKeyId2); + startActivityForResult(nfcIntent, 0); + + return true; + + case Id.menu.share: + // get master key id using row id + long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId); + + Intent shareIntent = new Intent(mKeyListActivity, ShareActivity.class); + shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING); + shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3); + startActivityForResult(shareIntent, 0); + + return true; + + default: + return super.onContextItemSelected(item); + + } + } + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, + UserIds.USER_ID }; + + static final String SORT_ORDER = UserIds.USER_ID + " ASC"; + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.setGroupCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.setGroupCursor(null); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java new file mode 100644 index 000000000..63d06edaf --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; + +public class KeyListSecretActivity extends KeyListActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mKeyType = Id.type.secret_key; + + setContentView(R.layout.key_list_secret_activity); + + mExportFilename = Constants.path.APP_DIR + "/secexport.asc"; + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + menu.add(1, Id.menu.option.create, 1, R.string.menu_createKey).setShowAsAction( + MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case Id.menu.option.create: { + createKey(); + return true; + } + + default: { + return super.onOptionsItemSelected(item); + } + } + } + + public void checkPassPhraseAndEdit(long keyId) { + String passPhrase = PassphraseCacheService.getCachedPassphrase(this, keyId); + if (passPhrase == null) { + showPassphraseDialog(keyId); + } else { + PgpMain.setEditPassPhrase(passPhrase); + editKey(keyId); + } + } + + private void showPassphraseDialog(final long secretKeyId) { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + String passPhrase = PassphraseCacheService.getCachedPassphrase( + KeyListSecretActivity.this, secretKeyId); + PgpMain.setEditPassPhrase(passPhrase); + editKey(secretKeyId); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance( + KeyListSecretActivity.this, messenger, secretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PgpMain.PgpGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + private void createKey() { + PgpMain.setEditPassPhrase(""); + Intent intent = new Intent(EditKeyActivity.ACTION_CREATE_KEY); + startActivityForResult(intent, 0); + } + + private void editKey(long keyId) { + Intent intent = new Intent(EditKeyActivity.ACTION_EDIT_KEY); + intent.putExtra(EditKeyActivity.EXTRA_KEY_ID, keyId); + startActivityForResult(intent, 0); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java new file mode 100644 index 000000000..f4ba2b4d9 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.ui.widget.KeyListAdapter; +import org.sufficientlysecure.keychain.R; + +import com.google.zxing.integration.android.IntentIntegrator; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.ContextMenu; +import android.view.View; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.ExpandableListView; +import android.widget.Toast; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; + +public class KeyListSecretFragment extends KeyListFragment implements + LoaderManager.LoaderCallbacks { + + private KeyListSecretActivity mKeyListSecretActivity; + + private KeyListAdapter mAdapter; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mKeyListSecretActivity = (KeyListSecretActivity) getActivity(); + + mAdapter = new KeyListAdapter(mKeyListSecretActivity, null, Id.type.secret_key); + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + // id is -1 as the child cursors are numbered 0,...,n + getLoaderManager().initLoader(-1, null, this); + } + + /** + * Context Menu on Long Click + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + menu.add(0, Id.menu.edit, 0, R.string.menu_editKey); + } + + @Override + public boolean onContextItemSelected(android.view.MenuItem item) { + ExpandableListContextMenuInfo expInfo = (ExpandableListContextMenuInfo) item.getMenuInfo(); + + // expInfo.id would also return row id of childs, but we always want to get the row id of + // the group item, thus we are using the following way + int groupPosition = ExpandableListView.getPackedPositionGroup(expInfo.packedPosition); + long keyRingRowId = getExpandableListAdapter().getGroupId(groupPosition); + + // get master key id using row id + long masterKeyId = ProviderHelper + .getSecretMasterKeyId(mKeyListSecretActivity, keyRingRowId); + + switch (item.getItemId()) { + case Id.menu.edit: + mKeyListSecretActivity.checkPassPhraseAndEdit(masterKeyId); + + return true; + + default: + return super.onContextItemSelected(item); + + } + } + + + // These are the rows that we will retrieve. + static final String[] PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, + UserIds.USER_ID }; + + static final String SORT_ORDER = UserIds.USER_ID + " ASC"; + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildSecretKeyRingsUri(); + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.setGroupCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.setGroupCursor(null); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java new file mode 100644 index 000000000..df8b7b374 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; +import java.util.List; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.KeyServer.KeyInfo; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +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 SherlockFragmentActivity { + + // possible intent actions for this activity + public static final String ACTION_LOOK_UP_KEY_ID = Constants.INTENT_PREFIX + "LOOK_UP_KEY_ID"; + public static final String ACTION_LOOK_UP_KEY_ID_AND_RETURN = Constants.INTENT_PREFIX + + "LOOK_UP_KEY_ID_AND_RETURN"; + + public static final String EXTRA_KEY_ID = "keyId"; + + public static final String RESULT_EXTRA_TEXT = "text"; + + 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 + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, KeyListPublicActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + break; + + } + return false; + } + + @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, Preferences.getPreferences(this) + .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(); + String action = intent.getAction(); + if (ACTION_LOOK_UP_KEY_ID.equals(action) || ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(action)) { + long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); + if (keyId != 0) { + String query = "0x" + PgpHelper.keyToHex(keyId); + mQuery.setText(query); + search(query); + } + } + } + + private void search(String query) { + mQueryType = Id.keyserver.search; + mQueryString = query; + mAdapter.setKeys(new ArrayList()); + + start(); + } + + private void get(long keyId) { + mQueryType = Id.keyserver.get; + mQueryId = keyId; + + start(); + } + + private void start() { + Log.d(Constants.TAG, "start search with service"); + + // Send all information needed to service to query keys in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_QUERY_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + + String server = (String) mKeyServer.getSelectedItem(); + data.putString(KeychainIntentService.QUERY_KEY_SERVER, server); + + data.putInt(KeychainIntentService.QUERY_KEY_TYPE, mQueryType); + + if (mQueryType == Id.keyserver.search) { + data.putString(KeychainIntentService.QUERY_KEY_STRING, mQueryString); + } else if (mQueryType == Id.keyserver.get) { + data.putLong(KeychainIntentService.QUERY_KEY_ID, mQueryId); + } + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after querying is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, + R.string.progress_querying, ProgressDialog.STYLE_SPINNER) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + if (mQueryType == Id.keyserver.search) { + mSearchResult = returnData + .getParcelableArrayList(KeychainIntentService.RESULT_QUERY_KEY_SEARCH_RESULT); + } else if (mQueryType == Id.keyserver.get) { + mKeyData = returnData.getString(KeychainIntentService.RESULT_QUERY_KEY_KEY_DATA); + } + + // TODO: IMPROVE CODE!!! some global variables can be avoided!!! + if (mQueryType == Id.keyserver.search) { + if (mSearchResult != null) { + Toast.makeText(KeyServerQueryActivity.this, + getString(R.string.keysFound, mSearchResult.size()), + Toast.LENGTH_SHORT).show(); + mAdapter.setKeys(mSearchResult); + } + } else if (mQueryType == Id.keyserver.get) { + Intent orgIntent = getIntent(); + if (ACTION_LOOK_UP_KEY_ID_AND_RETURN.equals(orgIntent.getAction())) { + if (mKeyData != null) { + Intent intent = new Intent(); + intent.putExtra(RESULT_EXTRA_TEXT, mKeyData); + setResult(RESULT_OK, intent); + } else { + setResult(RESULT_CANCELED); + } + finish(); + } else { + if (mKeyData != null) { + Intent intent = new Intent(KeyServerQueryActivity.this, + ImportKeysActivity.class); + intent.setAction(ImportKeysActivity.ACTION_IMPORT); + intent.putExtra(ImportKeysActivity.EXTRA_TEXT, mKeyData); + startActivity(intent); + } + } + } + + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(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 ArrayList(); + } + + 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(PgpHelper.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.MATCH_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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java new file mode 100644 index 000000000..657044f2b --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * Copyright (C) 2011 Senecaso + * + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.MenuItem; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +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 KeyServerUploadActivity extends SherlockFragmentActivity { + + // Not used in sourcode, but listed in AndroidManifest! + public static final String ACTION_EXPORT_KEY_TO_SERVER = Constants.INTENT_PREFIX + + "EXPORT_KEY_TO_SERVER"; + + public static final String EXTRA_KEYRING_ROW_ID = "keyId"; + + private Button export; + private Spinner keyServer; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, KeyListPublicActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + break; + + } + return false; + } + + @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, Preferences.getPreferences(this) + .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) { + uploadKey(); + } + }); + } + + private void uploadKey() { + // Send all information needed to service to upload key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_UPLOAD_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + + int keyRingId = getIntent().getIntExtra(EXTRA_KEYRING_ROW_ID, -1); + data.putInt(KeychainIntentService.UPLOAD_KEY_KEYRING_ROW_ID, keyRingId); + + String server = (String) keyServer.getSelectedItem(); + data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, server); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after uploading is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, R.string.progress_exporting, + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + Toast.makeText(KeyServerUploadActivity.this, R.string.keySendSuccess, + Toast.LENGTH_SHORT).show(); + finish(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java new file mode 100644 index 000000000..447801e55 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; + +public class MainActivity extends SherlockActivity { + + public void manageKeysOnClick(View view) { + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(new Intent(this, KeyListPublicActivity.class), 0); + } + + public void myKeysOnClick(View view) { + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(new Intent(this, KeyListSecretActivity.class), 0); + } + + public void encryptOnClick(View view) { + Intent intent = new Intent(MainActivity.this, EncryptActivity.class); + intent.setAction(EncryptActivity.ACTION_ENCRYPT); + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); + } + + public void decryptOnClick(View view) { + Intent intent = new Intent(MainActivity.this, DecryptActivity.class); + intent.setAction(DecryptActivity.ACTION_DECRYPT); + // used instead of startActivity set actionbar based on callingPackage + startActivityForResult(intent, 0); + } + + public void scanQrcodeOnClick(View view) { + Intent intent = new Intent(this, ImportKeysActivity.class); + startActivityForResult(intent, 0); + } + + public void helpOnClick(View view) { + startActivity(new Intent(this, HelpActivity.class)); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + menu.add(0, Id.menu.option.preferences, 0, R.string.menu_preferences) + .setIcon(R.drawable.ic_menu_settings) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case Id.menu.option.preferences: + startActivity(new Intent(this, PreferencesActivity.class)); + return true; + + default: + break; + + } + return false; + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java new file mode 100644 index 000000000..3c070c6db --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java @@ -0,0 +1,240 @@ +/* + * 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.sufficientlysecure.keychain.ui; + +import org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockPreferenceActivity; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.Preference; +import android.preference.PreferenceScreen; + +public class PreferencesActivity extends SherlockPreferenceActivity { + 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); + super.onCreate(savedInstanceState); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + + addPreferencesFromResource(R.xml.preferences); + + 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())); + 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, + PreferencesKeyServerActivity.class); + intent.putExtra(PreferencesKeyServerActivity.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(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS); + mPreferences.setKeyServers(servers); + mKeyServerPreference.setSummary(getResources().getString(R.string.nKeyServers, + servers.length)); + break; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + break; + } + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + break; + + } + return false; + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java new file mode 100644 index 000000000..b86fe0a2e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java @@ -0,0 +1,162 @@ +/* + * 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.sufficientlysecure.keychain.ui; + +import java.util.Vector; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.ui.widget.Editor; +import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor; +import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.TextView; + +public class PreferencesKeyServerActivity extends SherlockActivity implements OnClickListener, + EditorListener { + + public static final String EXTRA_KEY_SERVERS = "keyServers"; + + private LayoutInflater mInflater; + private ViewGroup mEditors; + private View mAdd; + private TextView mTitle; + private TextView mSummary; + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, PreferencesActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + + return true; + + case Id.menu.option.okay: + okClicked(); + + return true; + + case Id.menu.option.cancel: + cancelClicked(); + + return true; + + default: + break; + + } + return false; + } + + /** + * ActionBar menu is created based on class variables to change it at runtime + * + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + menu.add(1, Id.menu.option.cancel, 0, android.R.string.cancel).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + menu.add(1, Id.menu.option.okay, 1, android.R.string.ok).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + + return true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.key_server_preference); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + + 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(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); + } + } + } + + 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(EXTRA_KEY_SERVERS, servers.toArray(dummy)); + setResult(RESULT_OK, data); + finish(); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java new file mode 100644 index 000000000..fc1cfba17 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyActivity.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; + +public class SelectPublicKeyActivity extends SherlockFragmentActivity { + + // Not used in sourcode, but listed in AndroidManifest! + public static final String ACTION_SELECT_PUBLIC_KEYS = Constants.INTENT_PREFIX + + "SELECT_PUBLIC_KEYS"; + + public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "masterKeyIds"; + + public static final String RESULT_EXTRA_MASTER_KEY_IDS = "masterKeyIds"; + public static final String RESULT_EXTRA_USER_IDS = "userIds"; + + SelectPublicKeyFragment mSelectFragment; + + long selectedMasterKeyIds[]; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.select_public_key_activity); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); + + mSelectFragment = (SelectPublicKeyFragment) getSupportFragmentManager().findFragmentById( + R.id.select_public_key_fragment); + + // TODO: reimplement! + // 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) { + // TODO: reimplement! + + // 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)); + // } + + // preselected master keys + selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); + } + + /** + * returns preselected key ids, this is used in the fragment + * + * @return + */ + public long[] getSelectedMasterKeyIds() { + return selectedMasterKeyIds; + } + + private void cancelClicked() { + setResult(RESULT_CANCELED, null); + finish(); + } + + private void okClicked() { + Intent data = new Intent(); + data.putExtra(RESULT_EXTRA_MASTER_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds()); + data.putExtra(RESULT_EXTRA_USER_IDS, mSelectFragment.getSelectedUserIds()); + setResult(RESULT_OK, data); + finish(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // TODO: reimplement! + // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( + // android.R.drawable.ic_menu_search); + menu.add(1, Id.menu.option.cancel, 0, R.string.btn_doNotSave).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + menu.add(1, Id.menu.option.okay, 1, R.string.btn_okay).setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + return true; + } + + /** + * Menu Options + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case Id.menu.option.okay: + okClicked(); + return true; + + case Id.menu.option.cancel: + cancelClicked(); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java new file mode 100644 index 000000000..8b3e75d05 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import java.util.Date; +import java.util.Vector; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.ui.widget.SelectKeyCursorAdapter; +import org.sufficientlysecure.keychain.R; + +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.widget.ListView; + +public class SelectPublicKeyFragment extends ListFragmentWorkaround implements + LoaderManager.LoaderCallbacks { + + private SelectPublicKeyActivity mActivity; + private SelectKeyCursorAdapter mAdapter; + private ListView mListView; + + private long mSelectedMasterKeyIds[]; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mActivity = (SelectPublicKeyActivity) getSherlockActivity(); + mListView = getListView(); + + // get selected master key ids, which are given to activity by intent + mSelectedMasterKeyIds = mActivity.getSelectedMasterKeyIds(); + + mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.listEmpty)); + + mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.public_key); + + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + /** + * Selects items based on master key ids in list view + * + * @param masterKeyIds + */ + private void preselectMasterKeyIds(long[] masterKeyIds) { + if (masterKeyIds != null) { + for (int i = 0; i < mListView.getCount(); ++i) { + long keyId = mAdapter.getMasterKeyId(i); + for (int j = 0; j < masterKeyIds.length; ++j) { + if (keyId == masterKeyIds[j]) { + mListView.setItemChecked(i, true); + break; + } + } + } + } + } + + /** + * Returns all selected master key ids + * + * @return + */ + public long[] getSelectedMasterKeyIds() { + // mListView.getCheckedItemIds() would give the row ids of the KeyRings not the master key + // ids! + Vector vector = new Vector(); + for (int i = 0; i < mListView.getCount(); ++i) { + if (mListView.isItemChecked(i)) { + vector.add(mAdapter.getMasterKeyId(i)); + } + } + + // convert to long array + long[] selectedMasterKeyIds = new long[vector.size()]; + for (int i = 0; i < vector.size(); ++i) { + selectedMasterKeyIds[i] = vector.get(i); + } + + return selectedMasterKeyIds; + } + + /** + * Returns all selected user ids + * + * @return + */ + public String[] getSelectedUserIds() { + Vector userIds = new Vector(); + for (int i = 0; i < mListView.getCount(); ++i) { + if (mListView.isItemChecked(i)) { + userIds.add((String) mAdapter.getUserId(i)); + } + } + + // make empty array to not return null + String userIdArray[] = new String[0]; + return userIds.toArray(userIdArray); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + + // These are the rows that we will retrieve. + long now = new Date().getTime() / 1000; + String[] projection = new String[] { + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + UserIds.USER_ID, + "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " + + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." + + Keys.CAN_ENCRYPT + " = '1') AS " + + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, + "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " + + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." + + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_ENCRYPT + + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " + + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY + + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; + + String inMasterKeyList = null; + if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) { + inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN ("; + for (int i = 0; i < mSelectedMasterKeyIds.length; ++i) { + if (i != 0) { + inMasterKeyList += ", "; + } + inMasterKeyList += DatabaseUtils.sqlEscapeString("" + mSelectedMasterKeyIds[i]); + } + inMasterKeyList += ")"; + } + + // 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.USER_ID + " ASC"; + if (inMasterKeyList != null) { + // sort by selected master keys + orderBy = inMasterKeyList + " DESC, " + orderBy; + } + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + + // preselect given master keys + preselectMasterKeyIds(mSelectedMasterKeyIds); + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java new file mode 100644 index 000000000..6f43cbabc --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; + +import android.content.Intent; +import android.os.Bundle; + +public class SelectSecretKeyActivity extends SherlockFragmentActivity { + + // Not used in sourcode, but listed in AndroidManifest! + public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX + + "SELECT_SECRET_KEY"; + + public static final String RESULT_EXTRA_MASTER_KEY_ID = "masterKeyId"; + public static final String RESULT_EXTRA_USER_ID = "userId"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.select_secret_key_activity); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + + setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); + + // TODO: reimplement! + // 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()); + } + + /** + * This is executed by SelectSecretKeyFragment after clicking on an item + * + * @param masterKeyId + * @param userId + */ + public void afterListSelection(long masterKeyId, String userId) { + Intent data = new Intent(); + data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); + data.putExtra(RESULT_EXTRA_USER_ID, (String) userId); + setResult(RESULT_OK, data); + finish(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + handleIntent(intent); + } + + private void handleIntent(Intent intent) { + // TODO: reimplement! + + // 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)); + // } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // TODO: reimplement! + // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( + // android.R.drawable.ic_menu_search); + return true; + } + + /** + * Menu Options + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + // app icon in Action Bar clicked; go home + Intent intent = new Intent(this, MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java new file mode 100644 index 000000000..d7d9fb0af --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui; + +import java.util.Date; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.ui.widget.SelectKeyCursorAdapter; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockListFragment; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.app.LoaderManager; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; + +public class SelectSecretKeyFragment extends SherlockListFragment implements + LoaderManager.LoaderCallbacks { + + private SelectSecretKeyActivity mActivity; + private SelectKeyCursorAdapter mAdapter; + private ListView mListView; + + /** + * Define Adapter and Loader on create of Activity + */ + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mActivity = (SelectSecretKeyActivity) getSherlockActivity(); + mListView = getListView(); + + mListView.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + long masterKeyId = mAdapter.getMasterKeyId(position); + String userId = mAdapter.getUserId(position); + + // return data to activity, which results in finishing it + mActivity.afterListSelection(masterKeyId, userId); + } + }); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.listEmpty)); + + mAdapter = new SelectKeyCursorAdapter(mActivity, mListView, null, Id.type.secret_key); + + setListAdapter(mAdapter); + + // Start out with a progress indicator. + setListShown(false); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + Uri baseUri = KeyRings.buildPublicKeyRingsUri(); + + // These are the rows that we will retrieve. + long now = new Date().getTime() / 1000; + String[] projection = new String[] { + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + UserIds.USER_ID, + "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = " + + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys." + + Keys.CAN_SIGN + " = '1') AS " + + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE, + "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS + + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = " + + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys." + + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN + + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " + + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY + + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; + + // 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(")"); + // } + + String orderBy = UserIds.USER_ID + " ASC"; + + // Now create and return a CursorLoader that will take care of + // creating a Cursor for the data being displayed. + return new CursorLoader(getActivity(), baseUri, projection, null, null, orderBy); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + + // The list should now be shown. + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader loader) { + // This is called when the last Cursor provided to onLoadFinished() + // above is about to be closed. We need to make sure we are no + // longer using it. + mAdapter.swapCursor(null); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java new file mode 100644 index 000000000..29960748b --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui; + +import java.util.ArrayList; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.google.zxing.integration.android.IntentIntegrator; + +import android.content.Intent; +import android.os.Bundle; + +public class ShareActivity extends SherlockFragmentActivity { + public static final String ACTION_SHARE_KEYRING = Constants.INTENT_PREFIX + "SHARE_KEYRING"; + public static final String ACTION_SHARE_KEYRING_WITH_QR_CODE = Constants.INTENT_PREFIX + + "SHARE_KEYRING_WITH_QR_CODE"; + + public static final String EXTRA_MASTER_KEY_ID = "masterKeyId"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + handleActions(getIntent()); + } + + protected void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + + if (extras == null) { + extras = new Bundle(); + } + + long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID); + + // get public keyring as ascii armored string + ArrayList keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this, + new long[] { masterKeyId }); + + // close this activity + finish(); + + if (ACTION_SHARE_KEYRING.equals(action)) { + // let user choose application + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, keyringArmored.get(0)); + sendIntent.setType("text/plain"); + startActivity(Intent.createChooser(sendIntent, + getResources().getText(R.string.shareKeyringWith))); + } else if (ACTION_SHARE_KEYRING_WITH_QR_CODE.equals(action)) { + // use barcode scanner integration library + new IntentIntegrator(this).shareText(keyringArmored.get(0)); + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java new file mode 100644 index 000000000..c106614d3 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2013 Dominik Schürmann + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.ui; + +import java.io.IOException; +import java.io.InputStream; + +import net.nightwhistler.htmlspanner.HtmlSpanner; +import net.nightwhistler.htmlspanner.JellyBeanSpanFixTextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuInflater; +import com.actionbarsherlock.view.MenuItem; + +import android.annotation.TargetApi; +import android.content.Intent; +import android.nfc.NdefMessage; +import android.nfc.NdefRecord; +import android.nfc.NfcAdapter; +import android.nfc.NfcAdapter.CreateNdefMessageCallback; +import android.nfc.NfcEvent; +import android.os.Build; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.Log; +import android.widget.Toast; +import android.nfc.NfcAdapter.OnNdefPushCompleteCallback; +import android.os.Handler; +import android.os.Message; +import android.provider.Settings; +import android.text.method.LinkMovementMethod; + +@TargetApi(Build.VERSION_CODES.JELLY_BEAN) +public class ShareNfcBeamActivity extends SherlockFragmentActivity implements + CreateNdefMessageCallback, OnNdefPushCompleteCallback { + public static final String ACTION_SHARE_KEYRING_WITH_NFC = Constants.INTENT_PREFIX + + "SHARE_KEYRING_WITH_NFC"; + + public static final String EXTRA_MASTER_KEY_ID = "masterKeyId"; + + NfcAdapter mNfcAdapter; + + byte[] mSharedKeyringBytes; + + private static final int MESSAGE_SENT = 1; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { + Toast.makeText(this, + getString(R.string.error) + ": " + getString(R.string.error_jellyBeanNeeded), + Toast.LENGTH_LONG).show(); + finish(); + } else { + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(this); + if (mNfcAdapter == null) { + Toast.makeText(this, + getString(R.string.error) + ": " + getString(R.string.error_nfcNeeded), + Toast.LENGTH_LONG).show(); + finish(); + } else { + // handle actions after verifying that nfc works... + handleActions(getIntent()); + } + } + } + + protected void handleActions(Intent intent) { + String action = intent.getAction(); + Bundle extras = intent.getExtras(); + + if (extras == null) { + extras = new Bundle(); + } + + if (ACTION_SHARE_KEYRING_WITH_NFC.equals(action)) { + long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID); + + // get public keyring as byte array + mSharedKeyringBytes = ProviderHelper.getPublicKeyRingsAsByteArray(this, + new long[] { masterKeyId }); + + // Register callback to set NDEF message + mNfcAdapter.setNdefPushMessageCallback(this, this); + // Register callback to listen for message-sent success + mNfcAdapter.setOnNdefPushCompleteCallback(this, this); + } + } + + /** + * Parses the NDEF Message from the intent and prints to the TextView + */ + void handleActionNdefDiscovered(Intent intent) { + Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); + // only one message sent during the beam + NdefMessage msg = (NdefMessage) rawMsgs[0]; + // record 0 contains the MIME type, record 1 is the AAR, if present + byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload(); + + Intent importIntent = new Intent(this, ImportKeysActivity.class); + importIntent.setAction(ImportKeysActivity.ACTION_IMPORT); + importIntent.putExtra(ImportKeysActivity.EXTRA_KEYRING_BYTES, receivedKeyringBytes); + + finish(); + + startActivity(importIntent); + } + + private void buildView() { + // load html from html file from /res/raw + InputStream inputStreamText = getResources().openRawResource(R.raw.nfc_beam_share); + + setContentView(R.layout.share_nfc_beam); + + JellyBeanSpanFixTextView text = (JellyBeanSpanFixTextView) findViewById(R.id.nfc_beam_text); + + // load html into textview + HtmlSpanner htmlSpanner = new HtmlSpanner(); + htmlSpanner.setStripExtraWhiteSpace(true); + try { + text.setText(htmlSpanner.fromHtml(inputStreamText)); + } catch (IOException e) { + Log.e(Constants.TAG, "Error while reading raw resources as stream", e); + } + + // make links work + text.setMovementMethod(LinkMovementMethod.getInstance()); + + // no flickering when clicking textview for Android < 4 + text.setTextColor(getResources().getColor(android.R.color.black)); + + // set actionbar without home button if called from another app + OtherHelper.setActionBarBackButton(this); + } + + /** + * Implementation for the CreateNdefMessageCallback interface + */ + @Override + public NdefMessage createNdefMessage(NfcEvent event) { + /** + * When a device receives a push with an AAR in it, the application specified in the AAR is + * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to + * guarantee that this activity starts when receiving a beamed message. For now, this code + * uses the tag dispatch system. + */ + NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, + mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); + return msg; + } + + /** + * Implementation for the OnNdefPushCompleteCallback interface + */ + @Override + public void onNdefPushComplete(NfcEvent arg0) { + // A handler is needed to send messages to the activity when this + // callback occurs, because it happens from a binder thread + mHandler.obtainMessage(MESSAGE_SENT).sendToTarget(); + } + + /** This handler receives a message from onNdefPushComplete */ + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_SENT: + Toast.makeText(getApplicationContext(), R.string.nfcSuccessfull, Toast.LENGTH_LONG) + .show(); + break; + } + } + }; + + @Override + public void onResume() { + super.onResume(); + // Check to see that the Activity started due to an Android Beam + if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { + handleActionNdefDiscovered(getIntent()); + } else { + // build view only when sending nfc, not when receiving, as it gets directly into Import + // activity on receiving + buildView(); + } + } + + @Override + public void onNewIntent(Intent intent) { + // onResume gets called after this to handle the intent + setIntent(intent); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getSupportMenuInflater(); + inflater.inflate(R.menu.nfc_beam, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + // app icon in Action Bar clicked; go to KeyListPublicActivity + Intent intent = new Intent(this, KeyListPublicActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + return true; + + case R.id.menu_settings: + Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); + startActivity(intentSettings); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java new file mode 100644 index 000000000..21d55aca0 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2011 Senecaso + * + * 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.sufficientlysecure.keychain.ui; + +import java.util.Iterator; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSignature; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; + +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 SherlockFragmentActivity { + + public static final String EXTRA_KEY_ID = "keyId"; + + private long mPubKeyId = 0; + private long mMasterKeyId = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // check we havent already signed it + setContentView(R.layout.sign_key_layout); + + final ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(false); + actionBar.setHomeButtonEnabled(false); + + final Spinner keyServer = (Spinner) findViewById(R.id.keyServer); + ArrayAdapter adapter = new ArrayAdapter(this, + android.R.layout.simple_spinner_item, Preferences.getPreferences(this) + .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 (mPubKeyId != 0) { + initiateSigning(); + } + } + }); + + mPubKeyId = getIntent().getLongExtra(EXTRA_KEY_ID, 0); + if (mPubKeyId == 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, SelectSecretKeyActivity.class); + startActivityForResult(intent, Id.request.secret_keys); + } + } + + private void showPassphraseDialog(final long secretKeyId) { + // Message is received after passphrase is cached + Handler returnHandler = new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + startSigning(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + + try { + PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this, + messenger, secretKeyId); + + passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog"); + } catch (PgpMain.PgpGeneralException e) { + Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!"); + // send message to handler to start encryption directly + returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY); + } + } + + /** + * handles the UI bits of the signing process on the UI thread + */ + private void initiateSigning() { + PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId); + if (pubring != null) { + // if we have already signed this key, dont bother doing it again + boolean alreadySigned = false; + + @SuppressWarnings("unchecked") + Iterator itr = pubring.getPublicKey(mPubKeyId).getSignatures(); + while (itr.hasNext()) { + PGPSignature sig = itr.next(); + if (sig.getKeyID() == mMasterKeyId) { + alreadySigned = true; + break; + } + } + + if (!alreadySigned) { + /* + * get the user's passphrase for this key (if required) + */ + String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId); + if (passphrase == null) { + showPassphraseDialog(mMasterKeyId); + return; // bail out; need to wait until the user has entered the passphrase + // before trying again + } else { + startSigning(); + } + } else { + Toast.makeText(this, "Key has already been signed", Toast.LENGTH_SHORT).show(); + + setResult(RESULT_CANCELED); + finish(); + } + } + } + + /** + * kicks off the actual signing process on a background thread + */ + private void startSigning() { + // Send all information needed to service to sign key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_SIGN_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + + data.putLong(KeychainIntentService.SIGN_KEY_MASTER_KEY_ID, mMasterKeyId); + data.putLong(KeychainIntentService.SIGN_KEY_PUB_KEY_ID, mPubKeyId); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after signing is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, R.string.progress_signing, + ProgressDialog.STYLE_SPINNER) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + Toast.makeText(SignKeyActivity.this, R.string.keySignSuccess, + Toast.LENGTH_SHORT).show(); + + // check if we need to send the key to the server or not + CheckBox sendKey = (CheckBox) findViewById(R.id.sendKey); + if (sendKey.isChecked()) { + /* + * upload the newly signed key to the key server + */ + uploadKey(); + } else { + finish(); + } + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + private void uploadKey() { + // Send all information needed to service to upload key in other thread + Intent intent = new Intent(this, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_UPLOAD_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + + data.putLong(KeychainIntentService.UPLOAD_KEY_KEYRING_ROW_ID, mPubKeyId); + + Spinner keyServer = (Spinner) findViewById(R.id.keyServer); + String server = (String) keyServer.getSelectedItem(); + data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, server); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after uploading is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, R.string.progress_exporting, + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + Toast.makeText(SignKeyActivity.this, R.string.keySendSuccess, + Toast.LENGTH_SHORT).show(); + + finish(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(this); + + // start service with intent + startService(intent); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case Id.request.secret_keys: { + if (resultCode == RESULT_OK) { + mMasterKeyId = data.getLongExtra(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); + } + } + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java new file mode 100644 index 000000000..defc3bc6c --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.dialog; + +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.R; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.widget.Toast; + +public class DeleteFileDialogFragment extends DialogFragment { + private static final String ARG_DELETE_FILE = "delete_file"; + + /** + * Creates new instance of this delete file dialog fragment + */ + public static DeleteFileDialogFragment newInstance(String deleteFile) { + DeleteFileDialogFragment frag = new DeleteFileDialogFragment(); + Bundle args = new Bundle(); + + args.putString(ARG_DELETE_FILE, deleteFile); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); + + final String deleteFile = getArguments().getString(ARG_DELETE_FILE); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.warning); + alert.setMessage(this.getString(R.string.fileDeleteConfirmation, deleteFile)); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(activity, KeychainIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_DELETE_FILE_SECURELY); + data.putString(KeychainIntentService.DELETE_FILE, deleteFile); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance( + R.string.progress_deletingSecurely, ProgressDialog.STYLE_HORIZONTAL); + + // Message is received after deleting is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + Toast.makeText(activity, R.string.fileDeleteSuccessful, + Toast.LENGTH_SHORT).show(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + deletingDialog.show(activity.getSupportFragmentManager(), "deletingDialog"); + + // start service with intent + activity.startService(intent); + } + }); + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + alert.setCancelable(true); + + return alert.create(); + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java new file mode 100644 index 000000000..e61003a9b --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.dialog; + +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; + +public class DeleteKeyDialogFragment extends DialogFragment { + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_DELETE_KEY_RING_ROW_ID = "delete_file"; + private static final String ARG_KEY_TYPE = "key_type"; + + public static final int MESSAGE_OKAY = 1; + + private Messenger mMessenger; + + /** + * Creates new instance of this delete file dialog fragment + */ + public static DeleteKeyDialogFragment newInstance(Messenger messenger, long deleteKeyRingRowId, + int keyType) { + DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment(); + Bundle args = new Bundle(); + + args.putParcelable(ARG_MESSENGER, messenger); + args.putLong(ARG_DELETE_KEY_RING_ROW_ID, deleteKeyRingRowId); + args.putInt(ARG_KEY_TYPE, keyType); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + final long deleteKeyRingRowId = getArguments().getLong(ARG_DELETE_KEY_RING_ROW_ID); + final int keyType = getArguments().getInt(ARG_KEY_TYPE); + + // TODO: better way to do this? + String userId = activity.getString(R.string.unknownUserId); + + if (keyType == Id.type.public_key) { + PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity, + deleteKeyRingRowId); + userId = PgpHelper.getMainUserIdSafe(activity, PgpHelper.getMasterKey(keyRing)); + } else { + PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity, + deleteKeyRingRowId); + userId = PgpHelper.getMainUserIdSafe(activity, PgpHelper.getMasterKey(keyRing)); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.warning); + builder.setMessage(getString( + keyType == 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() { + + @Override + public void onClick(DialogInterface dialog, int id) { + if (keyType == Id.type.public_key) { + ProviderHelper.deletePublicKeyRing(activity, deleteKeyRingRowId); + } else { + ProviderHelper.deleteSecretKeyRing(activity, deleteKeyRingRowId); + } + + dismiss(); + + sendMessageToHandler(MESSAGE_OKAY); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + return builder.create(); + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what) { + Message msg = Message.obtain(); + msg.what = what; + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java new file mode 100644 index 000000000..2f9cdb3b1 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.dialog; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageButton; + +public class FileDialogFragment extends DialogFragment { + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_TITLE = "title"; + private static final String ARG_MESSAGE = "message"; + private static final String ARG_DEFAULT_FILE = "default_file"; + private static final String ARG_CHECKBOX_TEXT = "checkbox_text"; + private static final String ARG_REQUEST_CODE = "request_code"; + + public static final int MESSAGE_OKAY = 1; + + public static final String MESSAGE_DATA_FILENAME = "filename"; + public static final String MESSAGE_DATA_CHECKED = "checked"; + + private Messenger mMessenger; + + /** + * Creates new instance of this file dialog fragment + */ + public static FileDialogFragment newInstance(Messenger messenger, String title, String message, + String defaultFile, String checkboxText, int requestCode) { + FileDialogFragment frag = new FileDialogFragment(); + Bundle args = new Bundle(); + args.putParcelable(ARG_MESSENGER, messenger); + + args.putString(ARG_TITLE, title); + args.putString(ARG_MESSAGE, message); + args.putString(ARG_DEFAULT_FILE, defaultFile); + args.putString(ARG_CHECKBOX_TEXT, checkboxText); + args.putInt(ARG_REQUEST_CODE, requestCode); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + String title = getArguments().getString(ARG_TITLE); + String message = getArguments().getString(ARG_MESSAGE); + String defaultFile = getArguments().getString(ARG_DEFAULT_FILE); + String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT); + final int requestCode = getArguments().getInt(ARG_REQUEST_CODE); + + final EditText mFilename; + final ImageButton mBrowse; + final CheckBox mCheckBox; + + 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); + + 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) { + // only .asc or .gpg files + FileHelper.openFile(activity, mFilename.getText().toString(), "text/plain", + 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); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + boolean checked = false; + if (mCheckBox.isEnabled()) { + checked = mCheckBox.isChecked(); + } + + // return resulting data back to activity + Bundle data = new Bundle(); + data.putString(MESSAGE_DATA_FILENAME, mFilename.getText().toString()); + data.putBoolean(MESSAGE_DATA_CHECKED, checked); + + sendMessageToHandler(MESSAGE_OKAY, data); + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + return alert.create(); + } + + /** + * Updates filename in dialog, normally called in onActivityResult in activity using the + * FileDialog + * + * @param messageId + * @param progress + * @param max + */ + public void setFilename(String filename) { + AlertDialog dialog = (AlertDialog) getDialog(); + EditText filenameEditText = (EditText) dialog.findViewById(R.id.input); + + if (filenameEditText != null) { + filenameEditText.setText(filename); + } + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what, Bundle data) { + Message msg = Message.obtain(); + msg.what = what; + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java new file mode 100644 index 000000000..17f900552 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/LookupUnknownKeyDialogFragment.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.dialog; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.ui.KeyServerQueryActivity; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; + + +public class LookupUnknownKeyDialogFragment extends DialogFragment { + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_UNKNOWN_KEY_ID = "unknown_key_id"; + + public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_CANCEL = 2; + + private Messenger mMessenger; + + /** + * Creates new instance of this dialog fragment + * + * @param messenger + * @param unknownKeyId + * @return + */ + public static LookupUnknownKeyDialogFragment newInstance(Messenger messenger, long unknownKeyId) { + LookupUnknownKeyDialogFragment frag = new LookupUnknownKeyDialogFragment(); + Bundle args = new Bundle(); + args.putLong(ARG_UNKNOWN_KEY_ID, unknownKeyId); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + final long unknownKeyId = getArguments().getLong(ARG_UNKNOWN_KEY_ID); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.title_unknownSignatureKey); + alert.setMessage(getString(R.string.lookupUnknownKey, + PgpHelper.getSmallFingerPrint(unknownKeyId))); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + sendMessageToHandler(MESSAGE_OKAY); + + Intent intent = new Intent(activity, KeyServerQueryActivity.class); + intent.setAction(KeyServerQueryActivity.ACTION_LOOK_UP_KEY_ID); + intent.putExtra(KeyServerQueryActivity.EXTRA_KEY_ID, unknownKeyId); + startActivityForResult(intent, Id.request.look_up_key_id); + } + }); + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + sendMessageToHandler(MESSAGE_CANCEL); + } + }); + alert.setCancelable(true); + alert.setOnCancelListener(new OnCancelListener() { + + @Override + public void onCancel(DialogInterface dialog) { + sendMessageToHandler(MESSAGE_CANCEL); + } + }); + + return alert.create(); + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what) { + Message msg = Message.obtain(); + msg.what = what; + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java new file mode 100644 index 000000000..504de9d69 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.dialog; + +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPPrivateKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; +import org.sufficientlysecure.keychain.helper.PgpMain.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.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.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; + + +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; +import android.widget.Toast; + +public class PassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_SECRET_KEY_ID = "secret_key_id"; + + public static final int MESSAGE_OKAY = 1; + + private Messenger mMessenger; + private EditText mPassphraseEditText; + + /** + * Creates new instance of this dialog fragment + * + * @param secretKeyId + * secret key id you want to use + * @param messenger + * to communicate back after caching the passphrase + * @return + * @throws PgpGeneralException + */ + public static PassphraseDialogFragment newInstance(Context context, Messenger messenger, + long secretKeyId) throws PgpGeneralException { + // check if secret key has a passphrase + if (!(secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none)) { + if (!hasPassphrase(context, secretKeyId)) { + throw new PgpMain.PgpGeneralException("No passphrase! No passphrase dialog needed!"); + } + } + + PassphraseDialogFragment frag = new PassphraseDialogFragment(); + Bundle args = new Bundle(); + args.putLong(ARG_SECRET_KEY_ID, secretKeyId); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Checks if key has a passphrase + * + * @param secretKeyId + * @return true if it has a passphrase + */ + private static boolean hasPassphrase(Context context, long secretKeyId) { + // check if the key has no passphrase + try { + PGPSecretKey secretKey = PgpHelper.getMasterKey(ProviderHelper + .getPGPSecretKeyRingByKeyId(context, secretKeyId)); + // PGPSecretKey secretKey = + // PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); + + Log.d(Constants.TAG, "Check if key has no passphrase..."); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + "SC").build("".toCharArray()); + PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); + if (testKey != null) { + Log.d(Constants.TAG, "Key has no passphrase! Caches empty passphrase!"); + + // cache empty passphrase + PassphraseCacheService.addCachedPassphrase(context, secretKey.getKeyID(), ""); + + return false; + } + } catch (PGPException e) { + // silently catch + } + + return true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + long secretKeyId = getArguments().getLong(ARG_SECRET_KEY_ID); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setTitle(R.string.title_authentication); + + final PGPSecretKey secretKey; + + if (secretKeyId == Id.key.symmetric || secretKeyId == Id.key.none) { + secretKey = null; + alert.setMessage(R.string.passPhraseForSymmetricEncryption); + } else { + // TODO: by master key id??? + secretKey = PgpHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByMasterKeyId( + activity, secretKeyId)); + // secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId)); + + if (secretKey == null) { + alert.setTitle(R.string.title_keyNotFound); + alert.setMessage(getString(R.string.keyNotFound, secretKeyId)); + alert.setPositiveButton(android.R.string.ok, new OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dismiss(); + } + }); + alert.setCancelable(false); + return alert.create(); + } + String userId = PgpHelper.getMainUserIdSafe(activity, secretKey); + + Log.d(Constants.TAG, "User id: '" + userId + "'"); + alert.setMessage(getString(R.string.passPhraseFor, userId)); + } + + LayoutInflater inflater = activity.getLayoutInflater(); + View view = inflater.inflate(R.layout.passphrase, null); + alert.setView(view); + + mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + String passPhrase = mPassphraseEditText.getText().toString(); + long keyId; + if (secretKey != null) { + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() + .setProvider(PgpMain.BOUNCY_CASTLE_PROVIDER_NAME).build( + passPhrase.toCharArray()); + PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor); + 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; + } + + // cache the new passphrase + Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); + PassphraseCacheService.addCachedPassphrase(activity, keyId, passPhrase); + + sendMessageToHandler(MESSAGE_OKAY); + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + + return alert.create(); + } + + @Override + public void onActivityCreated(Bundle arg0) { + super.onActivityCreated(arg0); + + // request focus and open soft keyboard + mPassphraseEditText.requestFocus(); + getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + mPassphraseEditText.setOnEditorActionListener(this); + } + + /** + * Associate the "done" button on the soft keyboard with the okay button in the view + */ + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (EditorInfo.IME_ACTION_DONE == actionId) { + AlertDialog dialog = ((AlertDialog) getDialog()); + Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + + bt.performClick(); + return true; + } + return false; + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what) { + Message msg = Message.obtain(); + msg.what = what; + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java new file mode 100644 index 000000000..69b31419e --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.dialog; + +import android.app.Activity; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.DialogInterface.OnKeyListener; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.view.KeyEvent; + +public class ProgressDialogFragment extends DialogFragment { + private static final String ARG_MESSAGE_ID = "message_id"; + private static final String ARG_STYLE = "style"; + + /** + * Creates new instance of this fragment + * + * @param id + * @return + */ + public static ProgressDialogFragment newInstance(int messageId, int style) { + ProgressDialogFragment frag = new ProgressDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_MESSAGE_ID, messageId); + args.putInt(ARG_STYLE, style); + + frag.setArguments(args); + return frag; + } + + /** + * Updates progress of dialog + * + * @param messageId + * @param progress + * @param max + */ + public void setProgress(int messageId, int progress, int max) { + setProgress(getString(messageId), progress, max); + } + + /** + * Updates progress of dialog + * + * @param messageId + * @param progress + * @param max + */ + public void setProgress(int progress, int max) { + ProgressDialog dialog = (ProgressDialog) getDialog(); + + dialog.setProgress(progress); + dialog.setMax(max); + } + + /** + * Updates progress of dialog + * + * @param messageId + * @param progress + * @param max + */ + public void setProgress(String message, int progress, int max) { + ProgressDialog dialog = (ProgressDialog) getDialog(); + + dialog.setMessage(message); + dialog.setProgress(progress); + dialog.setMax(max); + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Activity activity = getActivity(); + + ProgressDialog dialog = new ProgressDialog(activity); + dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + dialog.setCancelable(false); + dialog.setCanceledOnTouchOutside(false); + + int messageId = getArguments().getInt(ARG_MESSAGE_ID); + int style = getArguments().getInt(ARG_STYLE); + + dialog.setMessage(getString(messageId)); + dialog.setProgressStyle(style); + + // Disable the back button + OnKeyListener keyListener = new OnKeyListener() { + + @Override + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + + if (keyCode == KeyEvent.KEYCODE_BACK) { + return true; + } + return false; + } + + }; + dialog.setOnKeyListener(keyListener); + + return dialog; + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java new file mode 100644 index 000000000..f001e7a7d --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.dialog; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.support.v4.app.DialogFragment; + +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import android.widget.TextView.OnEditorActionListener; + +public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { + private static final String ARG_MESSENGER = "messenger"; + private static final String ARG_TITLE = "title"; + + public static final int MESSAGE_OKAY = 1; + + public static final String MESSAGE_NEW_PASSPHRASE = "new_passphrase"; + + private Messenger mMessenger; + private EditText mPassphraseEditText; + private EditText mPassphraseAgainEditText; + + /** + * Creates new instance of this dialog fragment + * + * @param title + * title of dialog + * @param messenger + * to communicate back after setting the passphrase + * @return + */ + public static SetPassphraseDialogFragment newInstance(Messenger messenger, int title) { + SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_TITLE, title); + args.putParcelable(ARG_MESSENGER, messenger); + + frag.setArguments(args); + + return frag; + } + + /** + * Creates dialog + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final Activity activity = getActivity(); + + int title = getArguments().getInt(ARG_TITLE); + mMessenger = getArguments().getParcelable(ARG_MESSENGER); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + + alert.setTitle(title); + alert.setMessage(R.string.enterPassPhraseTwice); + + LayoutInflater inflater = activity.getLayoutInflater(); + View view = inflater.inflate(R.layout.passphrase_repeat, null); + alert.setView(view); + + mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase); + mPassphraseAgainEditText = (EditText) view.findViewById(R.id.passphrase_passphrase_again); + + alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + + String passPhrase1 = mPassphraseEditText.getText().toString(); + String passPhrase2 = mPassphraseAgainEditText.getText().toString(); + if (!passPhrase1.equals(passPhrase2)) { + Toast.makeText( + activity, + getString(R.string.errorMessage, + getString(R.string.passPhrasesDoNotMatch)), Toast.LENGTH_SHORT) + .show(); + return; + } + + if (passPhrase1.equals("")) { + Toast.makeText( + activity, + getString(R.string.errorMessage, + getString(R.string.passPhraseMustNotBeEmpty)), + Toast.LENGTH_SHORT).show(); + return; + } + + // return resulting data back to activity + Bundle data = new Bundle(); + data.putString(MESSAGE_NEW_PASSPHRASE, passPhrase1); + + sendMessageToHandler(MESSAGE_OKAY, data); + } + }); + + alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int id) { + dismiss(); + } + }); + + return alert.create(); + } + + @Override + public void onActivityCreated(Bundle arg0) { + super.onActivityCreated(arg0); + + // request focus and open soft keyboard + mPassphraseEditText.requestFocus(); + getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); + + mPassphraseAgainEditText.setOnEditorActionListener(this); + } + + /** + * Associate the "done" button on the soft keyboard with the okay button in the view + */ + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (EditorInfo.IME_ACTION_DONE == actionId) { + AlertDialog dialog = ((AlertDialog) getDialog()); + Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); + + bt.performClick(); + return true; + } + return false; + } + + /** + * Send message back to handler which is initialized in a activity + * + * @param what + * Message integer you want to send + */ + private void sendMessageToHandler(Integer what, Bundle data) { + Message msg = Message.obtain(); + msg.what = what; + if (data != null) { + msg.setData(data); + } + + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); + } catch (NullPointerException e) { + Log.w(Constants.TAG, "Messenger is null!", e); + } + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java new file mode 100644 index 000000000..158a271bc --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java @@ -0,0 +1,186 @@ +/* + * Copyright 2011 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.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; + +/** + * Custom layout that arranges children in a grid-like manner, optimizing for even horizontal and + * vertical whitespace. + */ +public class DashboardLayout extends ViewGroup { + private static final int UNEVEN_GRID_PENALTY_MULTIPLIER = 10; + + private int mMaxChildWidth = 0; + private int mMaxChildHeight = 0; + + public DashboardLayout(Context context) { + super(context, null); + } + + public DashboardLayout(Context context, AttributeSet attrs) { + super(context, attrs, 0); + } + + public DashboardLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + mMaxChildWidth = 0; + mMaxChildHeight = 0; + + // Measure once to find the maximum child size. + + int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST); + int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec( + MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.AT_MOST); + + final int count = getChildCount(); + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + + mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth()); + mMaxChildHeight = Math.max(mMaxChildHeight, child.getMeasuredHeight()); + } + + // Measure again for each child to be exactly the same size. + + childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth, MeasureSpec.EXACTLY); + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight, MeasureSpec.EXACTLY); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + + child.measure(childWidthMeasureSpec, childHeightMeasureSpec); + } + + setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec), + resolveSize(mMaxChildHeight, heightMeasureSpec)); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int width = r - l; + int height = b - t; + + final int count = getChildCount(); + + // Calculate the number of visible children. + int visibleCount = 0; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + ++visibleCount; + } + + if (visibleCount == 0) { + return; + } + + // Calculate what number of rows and columns will optimize for even horizontal and + // vertical whitespace between items. Start with a 1 x N grid, then try 2 x N, and so on. + int bestSpaceDifference = Integer.MAX_VALUE; + int spaceDifference; + + // Horizontal and vertical space between items + int hSpace = 0; + int vSpace = 0; + + int cols = 1; + int rows; + + while (true) { + rows = (visibleCount - 1) / cols + 1; + + hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); + vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); + + spaceDifference = Math.abs(vSpace - hSpace); + if (rows * cols != visibleCount) { + spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; + } else if (rows * mMaxChildHeight > height || cols * mMaxChildWidth > width) { + spaceDifference *= UNEVEN_GRID_PENALTY_MULTIPLIER; + } + + if (spaceDifference < bestSpaceDifference) { + // Found a better whitespace squareness/ratio + bestSpaceDifference = spaceDifference; + + // If we found a better whitespace squareness and there's only 1 row, this is + // the best we can do. + if (rows == 1) { + break; + } + } else { + // This is a worse whitespace ratio, use the previous value of cols and exit. + --cols; + rows = (visibleCount - 1) / cols + 1; + hSpace = ((width - mMaxChildWidth * cols) / (cols + 1)); + vSpace = ((height - mMaxChildHeight * rows) / (rows + 1)); + break; + } + + ++cols; + } + + // Lay out children based on calculated best-fit number of rows and cols. + + // If we chose a layout that has negative horizontal or vertical space, force it to zero. + hSpace = Math.max(0, hSpace); + vSpace = Math.max(0, vSpace); + + // Re-use width/height variables to be child width/height. + width = (width - hSpace * (cols + 1)) / cols; + height = (height - vSpace * (rows + 1)) / rows; + + int left, top; + int col, row; + int visibleIndex = 0; + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() == GONE) { + continue; + } + + row = visibleIndex / cols; + col = visibleIndex % cols; + + left = hSpace * (col + 1) + width * col; + top = vSpace * (row + 1) + height * row; + + child.layout(left, top, (hSpace == 0 && col == cols - 1) ? r : (left + width), + (vSpace == 0 && row == rows - 1) ? b : (top + height)); + ++visibleIndex; + } + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/Editor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/Editor.java new file mode 100644 index 000000000..1cf510d3a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/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.sufficientlysecure.keychain.ui.widget; + +public interface Editor { + public interface EditorListener { + public void onDeleted(Editor editor); + } + + public void setEditorListener(EditorListener listener); +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ExpandableListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ExpandableListFragment.java new file mode 100644 index 000000000..54022342f --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ExpandableListFragment.java @@ -0,0 +1,530 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.Fragment; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnCreateContextMenuListener; +import android.view.ViewGroup; +import android.view.animation.AnimationUtils; +import android.widget.AdapterView; +import android.widget.ExpandableListAdapter; +import android.widget.ExpandableListView; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ListAdapter; +import android.widget.ProgressBar; +import android.widget.TextView; + +/** + * @author Khoa Tran + * + * @see android.support.v4.app.ListFragment + * @see android.app.ExpandableListActivity + * + * ExpandableListFragment for Android < 3.0 + * + * from + * http://stackoverflow.com/questions/6051050/expandablelistfragment-with-loadermanager-for- + * compatibility-package + * + */ +public class ExpandableListFragment extends Fragment implements OnCreateContextMenuListener, + ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener, + ExpandableListView.OnGroupExpandListener { + + static final int INTERNAL_EMPTY_ID = 0x00ff0001; + static final int INTERNAL_PROGRESS_CONTAINER_ID = 0x00ff0002; + static final int INTERNAL_LIST_CONTAINER_ID = 0x00ff0003; + + final private Handler mHandler = new Handler(); + + final private Runnable mRequestFocus = new Runnable() { + public void run() { + mExpandableList.focusableViewAvailable(mExpandableList); + } + }; + + final private AdapterView.OnItemClickListener mOnClickListener = new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView parent, View v, int position, long id) { + onListItemClick((ExpandableListView) parent, v, position, id); + } + }; + + ExpandableListAdapter mAdapter; + ExpandableListView mExpandableList; + boolean mFinishedStart = false; + View mEmptyView; + TextView mStandardEmptyView; + View mProgressContainer; + View mExpandableListContainer; + CharSequence mEmptyText; + boolean mExpandableListShown; + + public ExpandableListFragment() { + } + + /** + * Provide default implementation to return a simple list view. Subclasses can override to + * replace with their own layout. If doing so, the returned view hierarchy must have a + * ListView whose id is {@link android.R.id#list android.R.id.list} and can optionally have a + * sibling view id {@link android.R.id#empty android.R.id.empty} that is to be shown when the + * list is empty. + * + *

+ * If you are overriding this method with your own custom content, consider including the + * standard layout {@link android.R.layout#list_content} in your layout file, so that you + * continue to retain all of the standard behavior of ListFragment. In particular, this is + * currently the only way to have the built-in indeterminant progress state be shown. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final Context context = getActivity(); + + FrameLayout root = new FrameLayout(context); + + // ------------------------------------------------------------------ + + LinearLayout pframe = new LinearLayout(context); + pframe.setId(INTERNAL_PROGRESS_CONTAINER_ID); + pframe.setOrientation(LinearLayout.VERTICAL); + pframe.setVisibility(View.GONE); + pframe.setGravity(Gravity.CENTER); + + ProgressBar progress = new ProgressBar(context, null, android.R.attr.progressBarStyleLarge); + pframe.addView(progress, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + + root.addView(pframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + // ------------------------------------------------------------------ + + FrameLayout lframe = new FrameLayout(context); + lframe.setId(INTERNAL_LIST_CONTAINER_ID); + + TextView tv = new TextView(getActivity()); + tv.setId(INTERNAL_EMPTY_ID); + tv.setGravity(Gravity.CENTER); + lframe.addView(tv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + ExpandableListView lv = new ExpandableListView(getActivity()); + lv.setId(android.R.id.list); + lv.setDrawSelectorOnTop(false); + lframe.addView(lv, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + root.addView(lframe, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + // ------------------------------------------------------------------ + + root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + return root; + } + + /** + * Attach to list view once the view hierarchy has been created. + */ + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + ensureList(); + } + + /** + * Detach from list view. + */ + @Override + public void onDestroyView() { + mHandler.removeCallbacks(mRequestFocus); + mExpandableList = null; + mExpandableListShown = false; + mEmptyView = mProgressContainer = mExpandableListContainer = null; + mStandardEmptyView = null; + super.onDestroyView(); + } + + /** + * This method will be called when an item in the list is selected. Subclasses should override. + * Subclasses can call getListView().getItemAtPosition(position) if they need to access the data + * associated with the selected item. + * + * @param l + * The ListView where the click happened + * @param v + * The view that was clicked within the ListView + * @param position + * The position of the view in the list + * @param id + * The row id of the item that was clicked + */ + public void onListItemClick(ExpandableListView l, View v, int position, long id) { + } + + /** + * Provide the cursor for the list view. + */ + public void setListAdapter(ExpandableListAdapter adapter) { + boolean hadAdapter = mAdapter != null; + mAdapter = adapter; + if (mExpandableList != null) { + mExpandableList.setAdapter(adapter); + if (!mExpandableListShown && !hadAdapter) { + // The list was hidden, and previously didn't have an + // adapter. It is now time to show it. + setListShown(true, getView().getWindowToken() != null); + } + } + } + + /** + * Set the currently selected list item to the specified position with the adapter's data + * + * @param position + */ + public void setSelection(int position) { + ensureList(); + mExpandableList.setSelection(position); + } + + /** + * Get the position of the currently selected list item. + */ + public int getSelectedItemPosition() { + ensureList(); + return mExpandableList.getSelectedItemPosition(); + } + + /** + * Get the cursor row ID of the currently selected list item. + */ + public long getSelectedItemId() { + ensureList(); + return mExpandableList.getSelectedItemId(); + } + + /** + * Get the activity's list view widget. + */ + public ExpandableListView getListView() { + ensureList(); + return mExpandableList; + } + + /** + * The default content for a ListFragment has a TextView that can be shown when the list is + * empty. If you would like to have it shown, call this method to supply the text it should use. + */ + public void setEmptyText(CharSequence text) { + ensureList(); + if (mStandardEmptyView == null) { + throw new IllegalStateException("Can't be used with a custom content view"); + } + mStandardEmptyView.setText(text); + if (mEmptyText == null) { + mExpandableList.setEmptyView(mStandardEmptyView); + } + mEmptyText = text; + } + + /** + * Control whether the list is being displayed. You can make it not displayed if you are waiting + * for the initial data to show in it. During this time an indeterminant progress indicator will + * be shown instead. + * + *

+ * Applications do not normally need to use this themselves. The default behavior of + * ListFragment is to start with the list not being shown, only showing it once an adapter is + * given with {@link #setListAdapter(ListAdapter)}. If the list at that point had not been + * shown, when it does get shown it will be do without the user ever seeing the hidden state. + * + * @param shown + * If true, the list view is shown; if false, the progress indicator. The initial + * value is true. + */ + public void setListShown(boolean shown) { + setListShown(shown, true); + } + + /** + * Like {@link #setListShown(boolean)}, but no animation is used when transitioning from the + * previous state. + */ + public void setListShownNoAnimation(boolean shown) { + setListShown(shown, false); + } + + /** + * Control whether the list is being displayed. You can make it not displayed if you are waiting + * for the initial data to show in it. During this time an indeterminant progress indicator will + * be shown instead. + * + * @param shown + * If true, the list view is shown; if false, the progress indicator. The initial + * value is true. + * @param animate + * If true, an animation will be used to transition to the new state. + */ + private void setListShown(boolean shown, boolean animate) { + ensureList(); + if (mProgressContainer == null) { + throw new IllegalStateException("Can't be used with a custom content view"); + } + if (mExpandableListShown == shown) { + return; + } + mExpandableListShown = shown; + if (shown) { + if (animate) { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_out)); + mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_in)); + } else { + mProgressContainer.clearAnimation(); + mExpandableListContainer.clearAnimation(); + } + mProgressContainer.setVisibility(View.GONE); + mExpandableListContainer.setVisibility(View.VISIBLE); + } else { + if (animate) { + mProgressContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_in)); + mExpandableListContainer.startAnimation(AnimationUtils.loadAnimation(getActivity(), + android.R.anim.fade_out)); + } else { + mProgressContainer.clearAnimation(); + mExpandableListContainer.clearAnimation(); + } + mProgressContainer.setVisibility(View.VISIBLE); + mExpandableListContainer.setVisibility(View.GONE); + } + } + + /** + * Get the ListAdapter associated with this activity's ListView. + */ + public ExpandableListAdapter getListAdapter() { + return mAdapter; + } + + private void ensureList() { + if (mExpandableList != null) { + return; + } + View root = getView(); + if (root == null) { + throw new IllegalStateException("Content view not yet created"); + } + if (root instanceof ExpandableListView) { + mExpandableList = (ExpandableListView) root; + } else { + mStandardEmptyView = (TextView) root.findViewById(INTERNAL_EMPTY_ID); + if (mStandardEmptyView == null) { + mEmptyView = root.findViewById(android.R.id.empty); + } else { + mStandardEmptyView.setVisibility(View.GONE); + } + mProgressContainer = root.findViewById(INTERNAL_PROGRESS_CONTAINER_ID); + mExpandableListContainer = root.findViewById(INTERNAL_LIST_CONTAINER_ID); + View rawExpandableListView = root.findViewById(android.R.id.list); + if (!(rawExpandableListView instanceof ExpandableListView)) { + if (rawExpandableListView == null) { + throw new RuntimeException( + "Your content must have a ListView whose id attribute is " + + "'android.R.id.list'"); + } + throw new RuntimeException( + "Content has view with id attribute 'android.R.id.list' " + + "that is not a ListView class"); + } + mExpandableList = (ExpandableListView) rawExpandableListView; + if (mEmptyView != null) { + mExpandableList.setEmptyView(mEmptyView); + } else if (mEmptyText != null) { + mStandardEmptyView.setText(mEmptyText); + mExpandableList.setEmptyView(mStandardEmptyView); + } + } + mExpandableListShown = true; + mExpandableList.setOnItemClickListener(mOnClickListener); + if (mAdapter != null) { + ExpandableListAdapter adapter = mAdapter; + mAdapter = null; + setListAdapter(adapter); + } else { + // We are starting without an adapter, so assume we won't + // have our data right away and start with the progress indicator. + if (mProgressContainer != null) { + setListShown(false, false); + } + } + mHandler.post(mRequestFocus); + } + + /** + * Override this to populate the context menu when an item is long pressed. menuInfo will + * contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo} whose + * packedPosition is a packed position that should be used with + * {@link ExpandableListView#getPackedPositionType(long)} and the other similar methods. + *

+ * {@inheritDoc} + */ + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + } + + /** + * Override this for receiving callbacks when a child has been clicked. + *

+ * {@inheritDoc} + */ + public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, + int childPosition, long id) { + return false; + } + + /** + * Override this for receiving callbacks when a group has been collapsed. + */ + public void onGroupCollapse(int groupPosition) { + } + + /** + * Override this for receiving callbacks when a group has been expanded. + */ + public void onGroupExpand(int groupPosition) { + } + + // /** + // * Ensures the expandable list view has been created before Activity restores all + // * of the view states. + // * + // *@see Activity#onRestoreInstanceState(Bundle) + // */ + // @Override + // protected void onRestoreInstanceState(Bundle state) { + // ensureList(); + // super.onRestoreInstanceState(state); + // } + + /** + * Updates the screen state (current list and other views) when the content changes. + * + * @see Activity#onContentChanged() + */ + + public void onContentChanged() { + // super.onContentChanged(); + View emptyView = getView().findViewById(android.R.id.empty); + mExpandableList = (ExpandableListView) getView().findViewById(android.R.id.list); + if (mExpandableList == null) { + throw new RuntimeException( + "Your content must have a ExpandableListView whose id attribute is " + + "'android.R.id.list'"); + } + if (emptyView != null) { + mExpandableList.setEmptyView(emptyView); + } + mExpandableList.setOnChildClickListener(this); + mExpandableList.setOnGroupExpandListener(this); + mExpandableList.setOnGroupCollapseListener(this); + + if (mFinishedStart) { + setListAdapter(mAdapter); + } + mFinishedStart = true; + } + + /** + * Get the activity's expandable list view widget. This can be used to get the selection, set + * the selection, and many other useful functions. + * + * @see ExpandableListView + */ + public ExpandableListView getExpandableListView() { + ensureList(); + return mExpandableList; + } + + /** + * Get the ExpandableListAdapter associated with this activity's ExpandableListView. + */ + public ExpandableListAdapter getExpandableListAdapter() { + return mAdapter; + } + + /** + * Gets the ID of the currently selected group or child. + * + * @return The ID of the currently selected group or child. + */ + public long getSelectedId() { + return mExpandableList.getSelectedId(); + } + + /** + * Gets the position (in packed position representation) of the currently selected group or + * child. Use {@link ExpandableListView#getPackedPositionType}, + * {@link ExpandableListView#getPackedPositionGroup}, and + * {@link ExpandableListView#getPackedPositionChild} to unpack the returned packed position. + * + * @return A packed position representation containing the currently selected group or child's + * position and type. + */ + public long getSelectedPosition() { + return mExpandableList.getSelectedPosition(); + } + + /** + * Sets the selection to the specified child. If the child is in a collapsed group, the group + * will only be expanded and child subsequently selected if shouldExpandGroup is set to true, + * otherwise the method will return false. + * + * @param groupPosition + * The position of the group that contains the child. + * @param childPosition + * The position of the child within the group. + * @param shouldExpandGroup + * Whether the child's group should be expanded if it is collapsed. + * @return Whether the selection was successfully set on the child. + */ + public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) { + return mExpandableList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup); + } + + /** + * Sets the selection to the specified group. + * + * @param groupPosition + * The position of the group that should be selected. + */ + public void setSelectedGroup(int groupPosition) { + mExpandableList.setSelectedGroup(groupPosition); + } +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java new file mode 100644 index 000000000..198140e0c --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/ImportKeysListLoader.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2012-2013 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.widget; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPObjectFactory; +import org.spongycastle.openpgp.PGPPublicKeyRing; +import org.spongycastle.openpgp.PGPPublicKeyRingCollection; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.spongycastle.openpgp.PGPSecretKeyRingCollection; +import org.spongycastle.openpgp.PGPUtil; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.PgpConversionHelper; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.PositionAwareInputStream; +import org.sufficientlysecure.keychain.R; + +import android.content.Context; +import android.support.v4.content.AsyncTaskLoader; + +/** + * A custom Loader to search for bad adware apps, based on + * https://github.com/brosmike/AirPush-Detector. Daniel Bjorge licensed it under Apachev2 after + * asking him by mail. + */ +public class ImportKeysListLoader extends AsyncTaskLoader>> { + public static final String MAP_ATTR_USER_ID = "user_id"; + public static final String MAP_ATTR_FINGERPINT = "fingerprint"; + + ArrayList> data = new ArrayList>(); + + Context mContext; + List mItems; + + byte[] mKeyringBytes; + String mImportFilename; + + public ImportKeysListLoader(Context context, byte[] keyringBytes, String importFilename) { + super(context); + this.mContext = context; + this.mKeyringBytes = keyringBytes; + this.mImportFilename = importFilename; + } + + @Override + public List> loadInBackground() { + InputData inputData = null; + if (mKeyringBytes != null) { + inputData = new InputData(new ByteArrayInputStream(mKeyringBytes), mKeyringBytes.length); + } else { + try { + inputData = new InputData(new FileInputStream(mImportFilename), + mImportFilename.length()); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "Failed to init FileInputStream!", e); + } + } + + generateListOfKeyrings(inputData); + + return data; + } + + @Override + protected void onReset() { + super.onReset(); + + // Ensure the loader is stopped + onStopLoading(); + } + + @Override + protected void onStartLoading() { + forceLoad(); + } + + @Override + protected void onStopLoading() { + cancelLoad(); + } + + @Override + public void deliverResult(List> data) { + super.deliverResult(data); + } + + /** + * Similar to PGPMain.importKeyRings + * + * @param keyringBytes + * @return + */ + private void generateListOfKeyrings(InputData inputData) { + PositionAwareInputStream progressIn = new PositionAwareInputStream( + inputData.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); + try { + + // read all available blocks... (asc files can contain many blocks with BEGIN END) + while (bufferedInput.available() > 0) { + InputStream in = PGPUtil.getDecoderStream(bufferedInput); + PGPObjectFactory objectFactory = new PGPObjectFactory(in); + + // go through all objects in this block + Object obj; + while ((obj = objectFactory.nextObject()) != null) { + Log.d(Constants.TAG, "Found class: " + obj.getClass()); + + if (obj instanceof PGPKeyRing) { + PGPKeyRing newKeyring = (PGPKeyRing) obj; + addToData(newKeyring); + } else { + Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); + } + } + } + } catch (Exception e) { + Log.e(Constants.TAG, "Exception on parsing key file!", e); + } + } + + private void addToData(PGPKeyRing keyring) { + String userId = PgpHelper.getMainUserId(keyring.getPublicKey()); + + if (keyring instanceof PGPSecretKeyRing) { + userId = mContext.getString(R.string.secretKeyring) + " " + userId; + } + + String fingerprint = PgpHelper.convertFingerprintToHex(keyring.getPublicKey() + .getFingerprint()); + + Map attrs = new HashMap(); + attrs.put(MAP_ATTR_USER_ID, userId); + attrs.put(MAP_ATTR_FINGERPINT, mContext.getString(R.string.fingerprint) + "\n" + + fingerprint); + data.add(attrs); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/IntegerListPreference.java new file mode 100644 index 000000000..15b4ddc6a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/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.sufficientlysecure.keychain.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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java new file mode 100644 index 000000000..1122fc522 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -0,0 +1,236 @@ +/* + * 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.sufficientlysecure.keychain.ui.widget; + +import org.spongycastle.openpgp.PGPPublicKey; +import org.spongycastle.openpgp.PGPSecretKey; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.util.Choice; +import org.sufficientlysecure.keychain.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, int usage) { + mKey = key; + + mIsMasterKey = isMasterKey; + if (mIsMasterKey) { + mDeleteButton.setVisibility(View.INVISIBLE); + } + + mAlgorithm.setText(PgpHelper.getAlgorithmInfo(key)); + String keyId1Str = PgpHelper.getSmallFingerPrint(key.getKeyID()); + String keyId2Str = PgpHelper.getSmallFingerPrint(key.getKeyID() >> 32); + mKeyId.setText(keyId1Str + " " + keyId2Str); + + Vector choices = new Vector(); + boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); + boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA); + if (!isElGamalKey) { + choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString( + R.string.choice_signOnly))); + } + if (!mIsMasterKey && !isDSAKey) { + choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString( + R.string.choice_encryptOnly))); + } + if (!isElGamalKey && !isDSAKey) { + 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); + + // Set value in choice dropdown to key + int selectId = 0; + if (PgpHelper.isEncryptionKey(key)) { + if (PgpHelper.isSigningKey(key)) { + selectId = Id.choice.usage.sign_and_encrypt; + } else { + selectId = Id.choice.usage.encrypt_only; + } + } else { + // set usage if it is predefined + if (usage != -1) { + selectId = usage; + } 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(PgpHelper.getCreationDate(key)); + mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime())); + cal = new GregorianCalendar(); + Date date = PgpHelper.getExpiryDate(key); + if (date == null) { + setExpiryDate(null); + } else { + cal.setTime(PgpHelper.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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java new file mode 100644 index 000000000..8e8d33886 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyListAdapter.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2012-2013 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.ui.widget; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.R; + +import android.content.Context; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.MergeCursor; +import android.net.Uri; +import android.provider.BaseColumns; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorTreeAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +public class KeyListAdapter extends CursorTreeAdapter { + private Context mContext; + private LayoutInflater mInflater; + + protected int mKeyType; + + private static final int CHILD_KEY = 0; + private static final int CHILD_USER_ID = 1; + private static final int CHILD_FINGERPRINT = 2; + + public KeyListAdapter(Context context, Cursor groupCursor, int keyType) { + super(groupCursor, context); + mContext = context; + mInflater = LayoutInflater.from(context); + mKeyType = keyType; + } + + /** + * Inflate new view for group items + */ + @Override + public View newGroupView(Context context, Cursor cursor, boolean isExpanded, ViewGroup parent) { + return mInflater.inflate(R.layout.key_list_group_item, null); + } + + /** + * Binds TextViews from group view to results from database group cursor. + */ + @Override + protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { + int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID); + + TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId); + mainUserId.setText(R.string.unknownUserId); + TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest); + mainUserIdRest.setText(""); + + String userId = cursor.getString(userIdIndex); + if (userId != null) { + String[] userIdSplit = OtherHelper.splitUserId(userId); + + if (userIdSplit[1] != null) { + mainUserIdRest.setText(userIdSplit[1]); + } + mainUserId.setText(userIdSplit[0]); + } + + if (mainUserId.getText().length() == 0) { + mainUserId.setText(R.string.unknownUserId); + } + + if (mainUserIdRest.getText().length() == 0) { + mainUserIdRest.setVisibility(View.GONE); + } + } + + /** + * Inflate new view for child items + */ + @Override + public View newChildView(Context context, Cursor cursor, boolean isLastChild, ViewGroup parent) { + return mInflater.inflate(R.layout.key_list_child_item, null); + } + + /** + * Bind TextViews from view of childs based on query results + */ + @Override + protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { + LinearLayout keyLayout = (LinearLayout) view.findViewById(R.id.keyLayout); + LinearLayout userIdLayout = (LinearLayout) view.findViewById(R.id.userIdLayout); + + // first entry is fingerprint + if (cursor.getPosition() == 0) { + // show only userId layout + keyLayout.setVisibility(View.GONE); + userIdLayout.setVisibility(View.VISIBLE); + + String fingerprint = PgpHelper.getFingerPrint(context, + cursor.getLong(cursor.getColumnIndex(Keys.KEY_ID))); + fingerprint = fingerprint.replace(" ", "\n"); + + TextView userId = (TextView) view.findViewById(R.id.userId); + if (userId == null) { + Log.d(Constants.TAG, "userId is null!"); + } + userId.setText(context.getString(R.string.fingerprint) + "\n" + fingerprint); + } else { + // differentiate between keys and userIds in MergeCursor + if (cursor.getColumnIndex(Keys.KEY_ID) != -1) { + keyLayout.setVisibility(View.VISIBLE); + userIdLayout.setVisibility(View.GONE); + + String keyIdStr = PgpHelper.getSmallFingerPrint(cursor.getLong(cursor + .getColumnIndex(Keys.KEY_ID))); + String algorithmStr = PgpHelper.getAlgorithmInfo( + cursor.getInt(cursor.getColumnIndex(Keys.ALGORITHM)), + cursor.getInt(cursor.getColumnIndex(Keys.KEY_SIZE))); + + TextView keyId = (TextView) view.findViewById(R.id.keyId); + keyId.setText(keyIdStr); + + TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); + keyDetails.setText("(" + algorithmStr + ")"); + + ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); + if (cursor.getInt(cursor.getColumnIndex(Keys.IS_MASTER_KEY)) != 1) { + masterKeyIcon.setVisibility(View.INVISIBLE); + } else { + masterKeyIcon.setVisibility(View.VISIBLE); + } + + ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); + if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_ENCRYPT)) != 1) { + encryptIcon.setVisibility(View.GONE); + } else { + encryptIcon.setVisibility(View.VISIBLE); + } + + ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); + if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_SIGN)) != 1) { + signIcon.setVisibility(View.GONE); + } else { + signIcon.setVisibility(View.VISIBLE); + } + } else { + keyLayout.setVisibility(View.GONE); + userIdLayout.setVisibility(View.VISIBLE); + + String userIdStr = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); + + TextView userId = (TextView) view.findViewById(R.id.userId); + userId.setText(userIdStr); + } + } + } + + /** + * Given the group cursor, we start cursors for a fingerprint, keys, and userIds, which are + * merged together and build the child cursor + */ + @Override + protected Cursor getChildrenCursor(Cursor groupCursor) { + final long keyRingRowId = groupCursor.getLong(groupCursor.getColumnIndex(BaseColumns._ID)); + + Cursor fingerprintCursor = getChildCursor(keyRingRowId, CHILD_FINGERPRINT); + Cursor keyCursor = getChildCursor(keyRingRowId, CHILD_KEY); + Cursor userIdCursor = getChildCursor(keyRingRowId, CHILD_USER_ID); + + MergeCursor mergeCursor = new MergeCursor(new Cursor[] { fingerprintCursor, keyCursor, + userIdCursor }); + Log.d(Constants.TAG, "mergeCursor:" + DatabaseUtils.dumpCursorToString(mergeCursor)); + + return mergeCursor; + } + + /** + * This builds a cursor for a specific type of children + * + * @param keyRingRowId + * foreign row id of the keyRing + * @param type + * @return + */ + private Cursor getChildCursor(long keyRingRowId, int type) { + Uri uri = null; + String[] projection = null; + String sortOrder = null; + String selection = null; + + switch (type) { + case CHILD_FINGERPRINT: + projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, + Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; + sortOrder = Keys.RANK + " ASC"; + + // use only master key for fingerprint + selection = Keys.IS_MASTER_KEY + " = 1 "; + + if (mKeyType == Id.type.public_key) { + uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); + } else { + uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); + } + break; + + case CHILD_KEY: + projection = new String[] { Keys._ID, Keys.KEY_ID, Keys.IS_MASTER_KEY, Keys.ALGORITHM, + Keys.KEY_SIZE, Keys.CAN_SIGN, Keys.CAN_ENCRYPT, }; + sortOrder = Keys.RANK + " ASC"; + + if (mKeyType == Id.type.public_key) { + uri = Keys.buildPublicKeysUri(String.valueOf(keyRingRowId)); + } else { + uri = Keys.buildSecretKeysUri(String.valueOf(keyRingRowId)); + } + + break; + + case CHILD_USER_ID: + projection = new String[] { UserIds._ID, UserIds.USER_ID, UserIds.RANK, }; + sortOrder = UserIds.RANK + " ASC"; + + // not the main user id + selection = UserIds.RANK + " > 0 "; + + if (mKeyType == Id.type.public_key) { + uri = UserIds.buildPublicUserIdsUri(String.valueOf(keyRingRowId)); + } else { + uri = UserIds.buildSecretUserIdsUri(String.valueOf(keyRingRowId)); + } + + break; + + default: + return null; + + } + + return mContext.getContentResolver().query(uri, projection, selection, null, sortOrder); + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java new file mode 100644 index 000000000..7c9ba5fe6 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/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.sufficientlysecure.keychain.ui.widget; + +import org.sufficientlysecure.keychain.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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java new file mode 100644 index 000000000..7142e4426 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -0,0 +1,327 @@ +/* + * 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.sufficientlysecure.keychain.ui.widget; + +import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.PgpConversionHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; +import org.sufficientlysecure.keychain.util.Choice; +import org.sufficientlysecure.keychain.R; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +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 java.util.Iterator; +import java.util.Vector; + +public class SectionView extends LinearLayout implements OnClickListener, EditorListener { + private LayoutInflater mInflater; + private View mAdd; + private ViewGroup mEditors; + private TextView mTitle; + private int mType = 0; + + private Choice mNewKeyAlgorithmChoice; + private int mNewKeySize; + + private SherlockFragmentActivity mActivity; + + private ProgressDialogFragment mGeneratingDialog; + + public SectionView(Context context) { + super(context); + mActivity = (SherlockFragmentActivity) context; + } + + public SectionView(Context context, AttributeSet attrs) { + super(context, attrs); + mActivity = (SherlockFragmentActivity) context; + } + + 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); + + boolean wouldBeMasterKey = (mEditors.getChildCount() == 0); + + final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_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.create_key_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, Vector usages) { + if (mType != Id.type.key) { + return; + } + + mEditors.removeAllViews(); + + // go through all keys and set view based on them + for (int i = 0; i < list.size(); i++) { + KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, + false); + view.setEditorListener(this); + boolean isMasterKey = (mEditors.getChildCount() == 0); + view.setValue(list.get(i), isMasterKey, usages.get(i)); + mEditors.addView(view); + } + + this.updateEditorsVisible(); + } + + private void createKey() { + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(mActivity, KeychainIntentService.class); + + intent.putExtra(KeychainIntentService.EXTRA_ACTION, KeychainIntentService.ACTION_GENERATE_KEY); + + // fill values for this action + Bundle data = new Bundle(); + + String passPhrase; + if (mEditors.getChildCount() > 0) { + PGPSecretKey masterKey = ((KeyEditor) mEditors.getChildAt(0)).getValue(); + passPhrase = PassphraseCacheService + .getCachedPassphrase(mActivity, masterKey.getKeyID()); + + data.putByteArray(KeychainIntentService.GENERATE_KEY_MASTER_KEY, + PgpConversionHelper.PGPSecretKeyToBytes(masterKey)); + } else { + passPhrase = ""; + } + data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passPhrase); + data.putInt(KeychainIntentService.GENERATE_KEY_ALGORITHM, mNewKeyAlgorithmChoice.getId()); + data.putInt(KeychainIntentService.GENERATE_KEY_KEY_SIZE, mNewKeySize); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // show progress dialog + mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating, + ProgressDialog.STYLE_SPINNER); + + // Message is received after generating is done in ApgService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity, mGeneratingDialog) { + public void handleMessage(Message message) { + // handle messages by standard ApgHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get new key from data bundle returned from service + Bundle data = message.getData(); + PGPSecretKeyRing newKeyRing = (PGPSecretKeyRing) PgpConversionHelper + .BytesToPGPKeyRing(data.getByteArray(KeychainIntentService.RESULT_NEW_KEY)); + + boolean isMasterKey = (mEditors.getChildCount() == 0); + + // take only the key from this ring + PGPSecretKey newKey = null; + @SuppressWarnings("unchecked") + Iterator it = newKeyRing.getSecretKeys(); + + if (isMasterKey) { + newKey = it.next(); + } else { + // first one is the master key + it.next(); + newKey = it.next(); + } + + // add view with new key + KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, + mEditors, false); + view.setEditorListener(SectionView.this); + view.setValue(newKey, isMasterKey, -1); + mEditors.addView(view); + SectionView.this.updateEditorsVisible(); + } + }; + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + mGeneratingDialog.show(mActivity.getSupportFragmentManager(), "dialog"); + + // start service with intent + mActivity.startService(intent); + } +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java new file mode 100644 index 000000000..da8ac94ee --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SelectKeyCursorAdapter.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * 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.sufficientlysecure.keychain.ui.widget; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.R; + +import android.content.Context; +import android.database.Cursor; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ListView; +import android.widget.TextView; + +public class SelectKeyCursorAdapter extends CursorAdapter { + + protected int mKeyType; + + private LayoutInflater mInflater; + private ListView mListView; + + public final static String PROJECTION_ROW_AVAILABLE = "available"; + public final static String PROJECTION_ROW_VALID = "valid"; + + @SuppressWarnings("deprecation") + public SelectKeyCursorAdapter(Context context, ListView listView, Cursor c, int keyType) { + super(context, c); + + mInflater = LayoutInflater.from(context); + mListView = listView; + mKeyType = keyType; + } + + public String getUserId(int position) { + mCursor.moveToPosition(position); + return mCursor.getString(mCursor.getColumnIndex(UserIds.USER_ID)); + } + + public long getMasterKeyId(int position) { + mCursor.moveToPosition(position); + return mCursor.getLong(mCursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + boolean valid = cursor.getInt(cursor + .getColumnIndex(PROJECTION_ROW_VALID)) > 0; + + 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 = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID)); + if (userId != null) { + String[] userIdSplit = OtherHelper.splitUserId(userId); + + if (userIdSplit[1] != null) { + mainUserIdRest.setText(userIdSplit[1]); + } + mainUserId.setText(userIdSplit[0]); + } + + long masterKeyId = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID)); + keyId.setText(PgpHelper.getSmallFingerPrint(masterKeyId)); + + if (mainUserIdRest.getText().length() == 0) { + mainUserIdRest.setVisibility(View.GONE); + } + + if (valid) { + if (mKeyType == Id.type.public_key) { + status.setText(R.string.canEncrypt); + } else { + status.setText(R.string.canSign); + } + } else { + if (cursor.getInt(cursor + .getColumnIndex(PROJECTION_ROW_AVAILABLE)) > 0) { + // has some CAN_ENCRYPT keys, but col(ROW_VALID) = 0, so must be revoked or + // expired + status.setText(R.string.expired); + } else { + status.setText(R.string.noKey); + } + } + + CheckBox selected = (CheckBox) view.findViewById(R.id.selected); + if (mKeyType == Id.type.public_key) { + selected.setVisibility(View.VISIBLE); + + if (!valid) { + mListView.setItemChecked(cursor.getPosition(), false); + } + + selected.setChecked(mListView.isItemChecked(cursor.getPosition())); + selected.setEnabled(valid); + } else { + selected.setVisibility(View.GONE); + } + + status.setText(status.getText() + " "); + + view.setEnabled(valid); + mainUserId.setEnabled(valid); + mainUserIdRest.setEnabled(valid); + keyId.setEnabled(valid); + status.setEnabled(valid); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.select_key_item, null); + } + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java new file mode 100644 index 000000000..2215d29e7 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -0,0 +1,196 @@ +/* + * 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.sufficientlysecure.keychain.ui.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.sufficientlysecure.keychain.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; + + // see http://www.regular-expressions.info/email.html + // RFC 2822 if we omit the syntax using double quotes and square brackets + // android.util.Patterns.EMAIL_ADDRESS is only available as of Android 2.2+ + private static final Pattern EMAIL_PATTERN = Pattern + .compile( + "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", + 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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Choice.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Choice.java new file mode 100644 index 000000000..9e0042c00 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/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.sufficientlysecure.keychain.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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java new file mode 100644 index 000000000..586a03498 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2011 Senecaso + * + * 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.sufficientlysecure.keychain.util; + +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.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 org.sufficientlysecure.keychain.helper.PgpHelper; +import org.sufficientlysecure.keychain.helper.PgpMain; + +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); + } + + 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"); + } + + @Override + public ArrayList search(String query) throws QueryException, TooManyResponses, + InsufficientQuery { + ArrayList results = new ArrayList(); + + 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 = PgpHelper.keyFromHex(matcher.group(3)); + info.fingerPrint = PgpHelper.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 ArrayList(); + 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" + PgpHelper.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 = PgpMain.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 + public 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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/InputData.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/InputData.java new file mode 100644 index 000000000..f5bcbf88a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/InputData.java @@ -0,0 +1,40 @@ +/* + * 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.sufficientlysecure.keychain.util; + +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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/IterableIterator.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/IterableIterator.java new file mode 100644 index 000000000..caaa07524 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/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.sufficientlysecure.keychain.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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/KeyServer.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/KeyServer.java new file mode 100644 index 000000000..cdc188887 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/KeyServer.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2011 Senecaso + * + * 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.sufficientlysecure.keychain.util; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import android.os.Parcel; +import android.os.Parcelable; + +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, Parcelable { + private static final long serialVersionUID = -7797972113284992662L; + public ArrayList userIds; + public String revoked; + public Date date; + public String fingerPrint; + public long keyId; + public int size; + public String algorithm; + + public KeyInfo() { + userIds = new ArrayList(); + } + + public KeyInfo(Parcel in) { + this(); + + in.readStringList(this.userIds); + this.revoked = in.readString(); + this.date = (Date) in.readSerializable(); + this.fingerPrint = in.readString(); + this.keyId = in.readLong(); + this.size = in.readInt(); + this.algorithm = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeStringList(userIds); + dest.writeString(revoked); + dest.writeSerializable(date); + dest.writeString(fingerPrint); + dest.writeLong(keyId); + dest.writeInt(size); + dest.writeString(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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Log.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Log.java new file mode 100644 index 000000000..cad01d708 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Log.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2012 Dominik Schürmann + * + * 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.sufficientlysecure.keychain.util; + +import org.sufficientlysecure.keychain.Constants; + +/** + * Wraps Android Logging to enable or disable debug output using Constants + * + */ +public final class Log { + + public static void v(String tag, String msg) { + if (Constants.DEBUG) { + android.util.Log.v(tag, msg); + } + } + + public static void v(String tag, String msg, Throwable tr) { + if (Constants.DEBUG) { + android.util.Log.v(tag, msg, tr); + } + } + + public static void d(String tag, String msg) { + if (Constants.DEBUG) { + android.util.Log.d(tag, msg); + } + } + + public static void d(String tag, String msg, Throwable tr) { + if (Constants.DEBUG) { + android.util.Log.d(tag, msg, tr); + } + } + + public static void i(String tag, String msg) { + if (Constants.DEBUG) { + android.util.Log.i(tag, msg); + } + } + + public static void i(String tag, String msg, Throwable tr) { + if (Constants.DEBUG) { + android.util.Log.i(tag, msg, tr); + } + } + + public static void w(String tag, String msg) { + android.util.Log.w(tag, msg); + } + + public static void w(String tag, String msg, Throwable tr) { + android.util.Log.w(tag, msg, tr); + } + + public static void w(String tag, Throwable tr) { + android.util.Log.w(tag, tr); + } + + public static void e(String tag, String msg) { + android.util.Log.e(tag, msg); + } + + public static void e(String tag, String msg, Throwable tr) { + android.util.Log.e(tag, msg, tr); + } + +} diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java new file mode 100644 index 000000000..a783d7820 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java @@ -0,0 +1,81 @@ +/* + * 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.sufficientlysecure.keychain.util; + +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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Primes.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Primes.java new file mode 100644 index 000000000..f503227a3 --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/Primes.java @@ -0,0 +1,188 @@ +/* + * 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.sufficientlysecure.keychain.util; + +import java.math.BigInteger; + +/** + * Primes for ElGamal + */ +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/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/ProgressDialogUpdater.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/ProgressDialogUpdater.java new file mode 100644 index 000000000..26c05ad0a --- /dev/null +++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/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.sufficientlysecure.keychain.util; + +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); +} -- cgit v1.2.3