aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2014-01-12 15:50:09 -0800
committerDominik Schürmann <dominik@dominikschuermann.de>2014-01-12 15:50:09 -0800
commit92aa5b36bba57e4927f146d49c9f124a37b7b5f9 (patch)
tree865d3962e54225fc4f13e0d19852ddc061e7e05f
parent536c4db48f12287eaf1875e2d208c0ad07e31ee6 (diff)
parente3bcf64d9e63e293c51db921572a9b87533d26ad (diff)
downloadopen-keychain-92aa5b36bba57e4927f146d49c9f124a37b7b5f9.tar.gz
open-keychain-92aa5b36bba57e4927f146d49c9f124a37b7b5f9.tar.bz2
open-keychain-92aa5b36bba57e4927f146d49c9f124a37b7b5f9.zip
Merge pull request #179 from dschuermann/key-details
Key details
-rw-r--r--OpenPGP-Keychain/AndroidManifest.xml68
-rw-r--r--OpenPGP-Keychain/assets/fontawesome-webfont.ttfbin0 -> 80776 bytes
-rw-r--r--OpenPGP-Keychain/build.gradle7
-rw-r--r--OpenPGP-Keychain/libs/android-support-v4.jarbin271754 -> 621451 bytes
-rw-r--r--OpenPGP-Keychain/project.properties4
-rw-r--r--OpenPGP-Keychain/res/drawable-finger/btn_circle.xml32
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable.pngbin2631 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable_focused.pngbin3001 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_normal.pngbin1974 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_pressed.pngbin2624 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_selected.pngbin2554 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_minus.pngbin536 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_plus.pngbin1316 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.pngbin4996 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.pngbin5032 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.pngbin4926 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.pngbin4967 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.pngbin8607 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.pngbin8343 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.pngbin5195 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.pngbin4973 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.pngbin7306 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.pngbin7147 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.pngbin5438 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.pngbin5414 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.pngbin0 -> 171 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.pngbin0 -> 2842 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.pngbin1443 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable.pngbin938 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable_focused.pngbin1436 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_normal.pngbin1249 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_pressed.pngbin1613 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_selected.pngbin1645 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_minus.pngbin288 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_plus.pngbin526 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.pngbin0 -> 158 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.pngbin0 -> 2837 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.pngbin1307 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.pngbin0 -> 182 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.pngbin0 -> 1056 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.pngbin1604 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable/btn_circle_disable.pngbin938 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable/btn_circle_disable_focused.pngbin1436 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable/btn_circle_normal.pngbin1249 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable/btn_circle_pressed.pngbin1613 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable/btn_circle_selected.pngbin1645 -> 0 bytes
-rw-r--r--OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml28
-rw-r--r--OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml28
-rw-r--r--OpenPGP-Keychain/res/drawable/dashboard_help.xml28
-rw-r--r--OpenPGP-Keychain/res/drawable/dashboard_import.xml28
-rw-r--r--OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml28
-rw-r--r--OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml28
-rw-r--r--OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml18
-rw-r--r--OpenPGP-Keychain/res/layout/api_apps_list_activity.xml22
-rw-r--r--OpenPGP-Keychain/res/layout/create_key_dialog.xml (renamed from OpenPGP-Keychain/res/layout/create_key.xml)26
-rw-r--r--OpenPGP-Keychain/res/layout/decrypt.xml180
-rw-r--r--OpenPGP-Keychain/res/layout/decrypt_activity.xml177
-rw-r--r--OpenPGP-Keychain/res/layout/drawer_list.xml18
-rw-r--r--OpenPGP-Keychain/res/layout/drawer_list_item.xml28
-rw-r--r--OpenPGP-Keychain/res/layout/edit_key_activity.xml (renamed from OpenPGP-Keychain/res/layout/edit_key.xml)7
-rw-r--r--OpenPGP-Keychain/res/layout/edit_key_key_item.xml44
-rw-r--r--OpenPGP-Keychain/res/layout/edit_key_section.xml36
-rw-r--r--OpenPGP-Keychain/res/layout/edit_key_user_id_item.xml41
-rw-r--r--OpenPGP-Keychain/res/layout/encrypt.xml306
-rw-r--r--OpenPGP-Keychain/res/layout/encrypt_activity.xml307
-rw-r--r--OpenPGP-Keychain/res/layout/file_dialog.xml9
-rw-r--r--OpenPGP-Keychain/res/layout/help_about_fragment.xml (renamed from OpenPGP-Keychain/res/layout/help_fragment_about.xml)0
-rw-r--r--OpenPGP-Keychain/res/layout/help_activity.xml27
-rw-r--r--OpenPGP-Keychain/res/layout/import_keys.xml51
-rw-r--r--OpenPGP-Keychain/res/layout/import_keys_activity.xml63
-rw-r--r--OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml12
-rw-r--r--OpenPGP-Keychain/res/layout/import_keys_file_fragment.xml9
-rw-r--r--OpenPGP-Keychain/res/layout/import_keys_keyserver_fragment.xml10
-rw-r--r--OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml10
-rw-r--r--OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml11
-rw-r--r--OpenPGP-Keychain/res/layout/key_list_child_item.xml102
-rw-r--r--OpenPGP-Keychain/res/layout/key_list_item.xml (renamed from OpenPGP-Keychain/res/layout/key_list_group_item.xml)20
-rw-r--r--OpenPGP-Keychain/res/layout/key_list_public_activity.xml33
-rw-r--r--OpenPGP-Keychain/res/layout/key_list_public_fragment.xml75
-rw-r--r--OpenPGP-Keychain/res/layout/key_list_public_header.xml16
-rw-r--r--OpenPGP-Keychain/res/layout/key_list_secret_activity.xml33
-rw-r--r--OpenPGP-Keychain/res/layout/key_server_editor.xml60
-rw-r--r--OpenPGP-Keychain/res/layout/key_server_export.xml18
-rw-r--r--OpenPGP-Keychain/res/layout/key_server_export_layout.xml34
-rw-r--r--OpenPGP-Keychain/res/layout/key_server_preference.xml26
-rw-r--r--OpenPGP-Keychain/res/layout/key_server_query.xml (renamed from OpenPGP-Keychain/res/layout/key_server_query_layout.xml)26
-rw-r--r--OpenPGP-Keychain/res/layout/key_server_query_result_item.xml88
-rw-r--r--OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml28
-rw-r--r--OpenPGP-Keychain/res/layout/main.xml70
-rw-r--r--OpenPGP-Keychain/res/layout/passphrase.xml40
-rw-r--r--OpenPGP-Keychain/res/layout/passphrase_dialog.xml24
-rw-r--r--OpenPGP-Keychain/res/layout/passphrase_repeat_dialog.xml (renamed from OpenPGP-Keychain/res/layout/passphrase_repeat.xml)22
-rw-r--r--OpenPGP-Keychain/res/layout/select_key_item.xml20
-rw-r--r--OpenPGP-Keychain/res/layout/share_nfc_beam.xml14
-rw-r--r--OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml16
-rw-r--r--OpenPGP-Keychain/res/layout/sign_key_activity.xml54
-rw-r--r--OpenPGP-Keychain/res/layout/sign_key_layout.xml40
-rw-r--r--OpenPGP-Keychain/res/layout/view_key_activity.xml216
-rw-r--r--OpenPGP-Keychain/res/layout/view_key_keys_item.xml63
-rw-r--r--OpenPGP-Keychain/res/layout/view_key_userids_item.xml17
-rw-r--r--OpenPGP-Keychain/res/menu/key_edit.xml17
-rw-r--r--OpenPGP-Keychain/res/menu/key_list_public.xml13
-rw-r--r--OpenPGP-Keychain/res/menu/key_list_public_multi.xml12
-rw-r--r--OpenPGP-Keychain/res/menu/key_list_secret.xml21
-rw-r--r--OpenPGP-Keychain/res/menu/key_list_secret_multi.xml9
-rw-r--r--OpenPGP-Keychain/res/menu/key_view.xml57
-rw-r--r--OpenPGP-Keychain/res/menu/nfc_beam.xml10
-rw-r--r--OpenPGP-Keychain/res/raw/help_about.html12
-rw-r--r--OpenPGP-Keychain/res/values-sw600dp/dimens.xml8
-rw-r--r--OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml9
-rw-r--r--OpenPGP-Keychain/res/values/colors.xml2
-rw-r--r--OpenPGP-Keychain/res/values/dimens.xml7
-rw-r--r--OpenPGP-Keychain/res/values/strings.xml50
-rw-r--r--OpenPGP-Keychain/res/values/styles.xml12
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java43
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java205
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java19
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java41
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java20
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java12
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java19
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java31
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java8
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java35
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java485
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java256
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java51
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java55
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java2
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java101
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java7
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java7
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java7
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java6
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java9
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java314
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java88
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java109
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java343
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java70
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java210
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java2
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java2
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java109
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java31
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java17
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java79
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java226
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java2
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java515
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java273
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java200
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java126
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java5
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java90
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java55
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java52
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java22
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java2
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java2
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java101
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/DashboardLayout.java186
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/FixedListView.java55
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java48
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java7
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java12
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java69
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java7
-rw-r--r--OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java266
-rw-r--r--README.md35
-rw-r--r--libraries/ActionBarSherlock/build.gradle2
-rw-r--r--libraries/ActionBarSherlock/libs/android-support-v4.jarbin271754 -> 621451 bytes
-rw-r--r--libraries/AndroidBootstrap/AndroidManifest.xml13
-rw-r--r--libraries/AndroidBootstrap/build.gradle17
-rw-r--r--libraries/AndroidBootstrap/libs/android-support-v4.jarbin0 -> 621451 bytes
-rw-r--r--libraries/AndroidBootstrap/proguard-project.txt20
-rw-r--r--libraries/AndroidBootstrap/project.properties15
-rw-r--r--libraries/AndroidBootstrap/res/drawable-hdpi/ic_launcher.pngbin0 -> 9397 bytes
-rw-r--r--libraries/AndroidBootstrap/res/drawable-mdpi/ic_launcher.pngbin0 -> 5237 bytes
-rw-r--r--libraries/AndroidBootstrap/res/drawable-xhdpi/ic_launcher.pngbin0 -> 14383 bytes
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_danger.xml20
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_danger_rounded.xml25
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_default.xml20
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_default_rounded.xml23
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_info.xml20
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_info_rounded.xml23
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_inverse.xml20
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_inverse_rounded.xml23
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_primary.xml20
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_primary_rounded.xml23
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_success.xml20
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_success_rounded.xml23
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_warning.xml20
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bbuton_warning_rounded.xml23
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bthumbnail_container_rounded.xml10
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bthumbnail_container_square.xml9
-rw-r--r--libraries/AndroidBootstrap/res/drawable/bthumbnail_placeholder_default.xml9
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background.xml18
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background_danger.xml12
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background_rounded.xml21
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_danger.xml17
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_success.xml14
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_warning.xml14
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background_success.xml12
-rw-r--r--libraries/AndroidBootstrap/res/drawable/edittext_background_warning.xml12
-rw-r--r--libraries/AndroidBootstrap/res/drawable/thumbnail_circle.xml6
-rw-r--r--libraries/AndroidBootstrap/res/drawable/thumbnail_circle_container.xml12
-rw-r--r--libraries/AndroidBootstrap/res/drawable/thumbnail_circle_minimal.xml11
-rw-r--r--libraries/AndroidBootstrap/res/layout/bootstrap_button.xml40
-rw-r--r--libraries/AndroidBootstrap/res/layout/bootstrap_button_fill.xml40
-rw-r--r--libraries/AndroidBootstrap/res/layout/bootstrap_edit_text.xml14
-rw-r--r--libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail.xml33
-rw-r--r--libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail_circle.xml32
-rw-r--r--libraries/AndroidBootstrap/res/layout/font_awesome_text.xml14
-rw-r--r--libraries/AndroidBootstrap/res/layout/row_title.xml16
-rw-r--r--libraries/AndroidBootstrap/res/layout/row_title_and_subtitle.xml26
-rw-r--r--libraries/AndroidBootstrap/res/layout/row_two_columns.xml28
-rw-r--r--libraries/AndroidBootstrap/res/menu/main.xml9
-rw-r--r--libraries/AndroidBootstrap/res/values-sw600dp/dimens.xml8
-rw-r--r--libraries/AndroidBootstrap/res/values-sw720dp-land/dimens.xml9
-rw-r--r--libraries/AndroidBootstrap/res/values-v11/styles.xml11
-rw-r--r--libraries/AndroidBootstrap/res/values-v14/styles.xml12
-rw-r--r--libraries/AndroidBootstrap/res/values/attrs.xml50
-rw-r--r--libraries/AndroidBootstrap/res/values/colors.xml65
-rw-r--r--libraries/AndroidBootstrap/res/values/dimens.xml14
-rw-r--r--libraries/AndroidBootstrap/res/values/strings.xml8
-rw-r--r--libraries/AndroidBootstrap/res/values/styles.xml16
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapButton.java445
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapCircleThumbnail.java215
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapEditText.java188
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapThumbnail.java209
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesome.java390
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesomeText.java274
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/AutoResizeTextView.java303
-rw-r--r--libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/ImageUtils.java77
-rw-r--r--libraries/StickyListHeaders/.gitignore98
-rw-r--r--libraries/StickyListHeaders/LICENSE191
-rw-r--r--libraries/StickyListHeaders/README.md203
-rw-r--r--libraries/StickyListHeaders/library/.gitignore2
-rw-r--r--libraries/StickyListHeaders/library/AndroidManifest.xml11
-rw-r--r--libraries/StickyListHeaders/library/build.gradle14
-rw-r--r--libraries/StickyListHeaders/library/build.xml92
-rw-r--r--libraries/StickyListHeaders/library/proguard-project.txt20
-rw-r--r--libraries/StickyListHeaders/library/project.properties15
-rw-r--r--libraries/StickyListHeaders/library/res/values/attrs.xml32
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java225
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java11
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java31
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java32
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java38
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java925
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java150
-rw-r--r--libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java175
-rw-r--r--libraries/zxing/AndroidManifest.xml11
-rw-r--r--libraries/zxing/build.gradle14
-rw-r--r--libraries/zxing/project.properties15
-rw-r--r--libraries/zxing/src/com/google/zxing/BarcodeFormat.java (renamed from OpenPGP-Keychain/src/com/google/zxing/BarcodeFormat.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/Binarizer.java (renamed from OpenPGP-Keychain/src/com/google/zxing/Binarizer.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/BinaryBitmap.java (renamed from OpenPGP-Keychain/src/com/google/zxing/BinaryBitmap.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/ChecksumException.java (renamed from OpenPGP-Keychain/src/com/google/zxing/ChecksumException.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/DecodeHintType.java (renamed from OpenPGP-Keychain/src/com/google/zxing/DecodeHintType.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/EncodeHintType.java (renamed from OpenPGP-Keychain/src/com/google/zxing/EncodeHintType.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/FormatException.java (renamed from OpenPGP-Keychain/src/com/google/zxing/FormatException.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/LuminanceSource.java (renamed from OpenPGP-Keychain/src/com/google/zxing/LuminanceSource.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/NotFoundException.java (renamed from OpenPGP-Keychain/src/com/google/zxing/NotFoundException.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/Reader.java (renamed from OpenPGP-Keychain/src/com/google/zxing/Reader.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/ReaderException.java (renamed from OpenPGP-Keychain/src/com/google/zxing/ReaderException.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/Result.java (renamed from OpenPGP-Keychain/src/com/google/zxing/Result.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/ResultMetadataType.java (renamed from OpenPGP-Keychain/src/com/google/zxing/ResultMetadataType.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/ResultPoint.java (renamed from OpenPGP-Keychain/src/com/google/zxing/ResultPoint.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/ResultPointCallback.java (renamed from OpenPGP-Keychain/src/com/google/zxing/ResultPointCallback.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/Writer.java (renamed from OpenPGP-Keychain/src/com/google/zxing/Writer.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/WriterException.java (renamed from OpenPGP-Keychain/src/com/google/zxing/WriterException.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/BitArray.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/BitArray.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/BitMatrix.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/BitMatrix.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/BitSource.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/BitSource.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/CharacterSetECI.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/CharacterSetECI.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/Collections.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/Collections.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/Comparator.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/Comparator.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/DecoderResult.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/DecoderResult.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/DefaultGridSampler.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/DefaultGridSampler.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/DetectorResult.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/DetectorResult.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/ECI.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/ECI.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/GlobalHistogramBinarizer.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/GlobalHistogramBinarizer.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/GridSampler.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/GridSampler.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/HybridBinarizer.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/HybridBinarizer.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/PerspectiveTransform.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/PerspectiveTransform.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/StringUtils.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/StringUtils.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/detector/WhiteRectangleDetector.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/detector/WhiteRectangleDetector.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGF.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGF.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java (renamed from OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/multi/ByQuadrantReader.java (renamed from OpenPGP-Keychain/src/com/google/zxing/multi/ByQuadrantReader.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java (renamed from OpenPGP-Keychain/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/multi/MultipleBarcodeReader.java (renamed from OpenPGP-Keychain/src/com/google/zxing/multi/MultipleBarcodeReader.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java (renamed from OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java (renamed from OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java)0
-rw-r--r--libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java (renamed from OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java)0
-rw-r--r--settings.gradle4
322 files changed, 10072 insertions, 3564 deletions
diff --git a/OpenPGP-Keychain/AndroidManifest.xml b/OpenPGP-Keychain/AndroidManifest.xml
index 8027715af..da655ba02 100644
--- a/OpenPGP-Keychain/AndroidManifest.xml
+++ b/OpenPGP-Keychain/AndroidManifest.xml
@@ -1,24 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.sufficientlysecure.keychain"
android:installLocation="auto"
- android:versionCode="21103"
+ android:versionCode="21106"
android:versionName="2.1.1" >
<!--
@@ -75,21 +59,15 @@
android:label="@string/app_name"
android:theme="@style/Theme.Sherlock.Light" >
<activity
- android:name=".ui.MainActivity"
+ android:name=".ui.KeyListPublicActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/app_name" >
+ android:label="@string/app_name"
+ android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
- </activity>
- <activity
- android:name=".ui.KeyListPublicActivity"
- android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_manage_public_keys"
- android:launchMode="singleTop"
- android:uiOptions="splitActionBarWhenNarrow" >
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.SEARCH" /> -->
@@ -104,8 +82,7 @@
android:name=".ui.KeyListSecretActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_manage_secret_keys"
- android:launchMode="singleTop"
- android:uiOptions="splitActionBarWhenNarrow" >
+ android:launchMode="singleTop" >
<!-- <intent-filter> -->
<!-- <action android:name="android.intent.action.SEARCH" /> -->
@@ -120,9 +97,17 @@
android:name=".ui.EditKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_edit_key"
- android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="stateHidden" />
<activity
+ android:name=".ui.ViewKeyActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:label="@string/title_key_details"
+ android:parentActivityName=".ui.KeyListPublicActivity" >
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".ui.KeyListPublicActivity" />
+ </activity>
+ <activity
android:name=".ui.SelectPublicKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_select_recipients"
@@ -269,8 +254,17 @@
<activity
android:name=".ui.ImportKeysActivity"
android:label="@string/title_import_keys"
+ android:launchMode="singleTop"
android:windowSoftInputMode="stateHidden" >
+ <!-- Handle NFC tags detected from outside our application -->
+ <intent-filter>
+ <action android:name="android.nfc.action.NDEF_DISCOVERED" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <!-- mime type as defined in http://tools.ietf.org/html/rfc3156, section 7 -->
+ <data android:mimeType="application/pgp-keys" />
+ </intent-filter>
<!-- Keychain's own Actions -->
<intent-filter android:label="@string/intent_import_key" >
<action android:name="org.sufficientlysecure.keychain.action.IMPORT_KEY" />
@@ -330,22 +324,6 @@
</intent-filter>
</activity>
<activity
- android:name=".ui.ShareNfcBeamActivity"
- android:label="@string/title_share_by_nfc"
- android:launchMode="singleTop"
- android:uiOptions="splitActionBarWhenNarrow" >
-
- <!-- Handle NFC tags detected from outside our application -->
- <intent-filter>
- <action android:name="android.nfc.action.NDEF_DISCOVERED" />
-
- <category android:name="android.intent.category.DEFAULT" />
- <!-- mime type as defined in http://tools.ietf.org/html/rfc3156, section 7 -->
- <data android:mimeType="application/pgp-keys" />
- </intent-filter>
- </activity>
- <activity android:name=".ui.ShareActivity" />
- <activity
android:name=".ui.HelpActivity"
android:label="@string/title_help" />
diff --git a/OpenPGP-Keychain/assets/fontawesome-webfont.ttf b/OpenPGP-Keychain/assets/fontawesome-webfont.ttf
new file mode 100644
index 000000000..7ec2e1de8
--- /dev/null
+++ b/OpenPGP-Keychain/assets/fontawesome-webfont.ttf
Binary files differ
diff --git a/OpenPGP-Keychain/build.gradle b/OpenPGP-Keychain/build.gradle
index 80c0f05cd..0fffd1fd3 100644
--- a/OpenPGP-Keychain/build.gradle
+++ b/OpenPGP-Keychain/build.gradle
@@ -4,6 +4,7 @@ buildscript {
}
dependencies {
+ // NOTE: Avoid using dynamic versions (+). This breaks offline builds!
classpath 'com.android.tools.build:gradle:0.6.3'
}
}
@@ -19,10 +20,12 @@ repositories {
*/
dependencies {
compile fileTree(dir: 'libs', includes: ['*.jar'], excludes: ['android-support-v4.jar'])
- compile 'com.android.support:support-v4:18.0.+' // already in actionbarsherlock
+ compile 'com.android.support:support-v4:19.0.+' // already in actionbarsherlock
compile project(':libraries:ActionBarSherlock')
compile project(':libraries:HtmlTextView')
- compile project(':libraries:pinned-section-listview:library')
+ compile project(':libraries:StickyListHeaders:library')
+ compile project(':libraries:zxing')
+ compile project(':libraries:AndroidBootstrap')
}
android {
diff --git a/OpenPGP-Keychain/libs/android-support-v4.jar b/OpenPGP-Keychain/libs/android-support-v4.jar
index 99e063b33..9056828a0 100644
--- a/OpenPGP-Keychain/libs/android-support-v4.jar
+++ b/OpenPGP-Keychain/libs/android-support-v4.jar
Binary files differ
diff --git a/OpenPGP-Keychain/project.properties b/OpenPGP-Keychain/project.properties
index 7347abfcd..76caac668 100644
--- a/OpenPGP-Keychain/project.properties
+++ b/OpenPGP-Keychain/project.properties
@@ -11,4 +11,6 @@
target=android-19
android.library.reference.1=../libraries/ActionBarSherlock
android.library.reference.2=../libraries/HtmlTextView
-android.library.reference.3=../libraries/pinned-section-listview/library
+android.library.reference.3=../libraries/StickyListHeaders/library
+android.library.reference.4=../libraries/zxing
+android.library.reference.5=../libraries/AndroidBootstrap
diff --git a/OpenPGP-Keychain/res/drawable-finger/btn_circle.xml b/OpenPGP-Keychain/res/drawable-finger/btn_circle.xml
deleted file mode 100644
index 6c3c7fc1a..000000000
--- a/OpenPGP-Keychain/res/drawable-finger/btn_circle.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_window_focused="false" android:state_enabled="true"
- android:drawable="@drawable/btn_circle_normal" />
- <item android:state_window_focused="false" android:state_enabled="false"
- android:drawable="@drawable/btn_circle_disable" />
- <item android:state_pressed="true"
- android:drawable="@drawable/btn_circle_pressed" />
- <item android:state_focused="true" android:state_enabled="true"
- android:drawable="@drawable/btn_circle_selected" />
- <item android:state_enabled="true"
- android:drawable="@drawable/btn_circle_normal" />
- <item android:state_focused="true"
- android:drawable="@drawable/btn_circle_disable_focused" />
- <item
- android:drawable="@drawable/btn_circle_disable" />
-</selector>
diff --git a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable.png b/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable.png
deleted file mode 100644
index ae063b545..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable_focused.png b/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable_focused.png
deleted file mode 100644
index 7a5d4fe4e..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_disable_focused.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_normal.png b/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_normal.png
deleted file mode 100644
index 5eda66883..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_normal.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_pressed.png
deleted file mode 100644
index 88848bac2..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_selected.png b/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_selected.png
deleted file mode 100644
index 74690705f..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi-finger/btn_circle_selected.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_minus.png b/OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_minus.png
deleted file mode 100644
index 27af3faf4..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_minus.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_plus.png b/OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_plus.png
deleted file mode 100644
index b24168c32..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi-finger/ic_btn_round_plus.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png
deleted file mode 100644
index 0d51bcb68..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_default.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png
deleted file mode 100644
index d4cc0f8ea..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_decrypt_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png
deleted file mode 100644
index 07617bb9d..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_default.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png
deleted file mode 100644
index b8fe6e1d6..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_encrypt_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png
deleted file mode 100644
index 233fddffc..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_default.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png
deleted file mode 100644
index dad8694f8..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_help_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.png
deleted file mode 100644
index 7be4837a0..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_default.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.png
deleted file mode 100644
index a4fe3c903..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_import_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png
deleted file mode 100644
index de83398c2..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_default.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png
deleted file mode 100644
index a86bc1bf9..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_manage_keys_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png
deleted file mode 100644
index f8b54961e..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_default.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png b/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png
deleted file mode 100644
index 6a5c92138..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/dashboard_my_keys_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.png b/OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.png
new file mode 100644
index 000000000..224cc4ff4
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-hdpi/drawer_shadow.9.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png b/OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png
new file mode 100644
index 000000000..ff7b1def9
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-hdpi/ic_drawer.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png b/OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png
deleted file mode 100644
index 3db304fa8..000000000
--- a/OpenPGP-Keychain/res/drawable-hdpi/ic_menu_filebrowser.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable.png b/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable.png
deleted file mode 100644
index 33b74a66c..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable_focused.png b/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable_focused.png
deleted file mode 100644
index 005ad8dca..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_disable_focused.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_normal.png b/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_normal.png
deleted file mode 100644
index fc5af1c9f..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_normal.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_pressed.png b/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_pressed.png
deleted file mode 100644
index 8f40afdfc..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_selected.png b/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_selected.png
deleted file mode 100644
index c74fac227..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi-finger/btn_circle_selected.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_minus.png b/OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_minus.png
deleted file mode 100644
index 96dbb17d2..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_minus.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_plus.png b/OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_plus.png
deleted file mode 100644
index 1ec8a956a..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi-finger/ic_btn_round_plus.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.png b/OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.png
new file mode 100644
index 000000000..3797f99c0
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-mdpi/drawer_shadow.9.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png b/OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png
new file mode 100644
index 000000000..fb681ba26
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-mdpi/ic_drawer.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png b/OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png
deleted file mode 100644
index fda13f1be..000000000
--- a/OpenPGP-Keychain/res/drawable-mdpi/ic_menu_filebrowser.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.png b/OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.png
new file mode 100644
index 000000000..fa3d853e9
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-xhdpi/drawer_shadow.9.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png b/OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png
new file mode 100644
index 000000000..b9bc3d70f
--- /dev/null
+++ b/OpenPGP-Keychain/res/drawable-xhdpi/ic_drawer.png
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png b/OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png
deleted file mode 100644
index d1324014d..000000000
--- a/OpenPGP-Keychain/res/drawable-xhdpi/ic_menu_filebrowser.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable/btn_circle_disable.png b/OpenPGP-Keychain/res/drawable/btn_circle_disable.png
deleted file mode 100644
index 33b74a66c..000000000
--- a/OpenPGP-Keychain/res/drawable/btn_circle_disable.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable/btn_circle_disable_focused.png b/OpenPGP-Keychain/res/drawable/btn_circle_disable_focused.png
deleted file mode 100644
index 005ad8dca..000000000
--- a/OpenPGP-Keychain/res/drawable/btn_circle_disable_focused.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable/btn_circle_normal.png b/OpenPGP-Keychain/res/drawable/btn_circle_normal.png
deleted file mode 100644
index fc5af1c9f..000000000
--- a/OpenPGP-Keychain/res/drawable/btn_circle_normal.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable/btn_circle_pressed.png b/OpenPGP-Keychain/res/drawable/btn_circle_pressed.png
deleted file mode 100644
index 8f40afdfc..000000000
--- a/OpenPGP-Keychain/res/drawable/btn_circle_pressed.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable/btn_circle_selected.png b/OpenPGP-Keychain/res/drawable/btn_circle_selected.png
deleted file mode 100644
index c74fac227..000000000
--- a/OpenPGP-Keychain/res/drawable/btn_circle_selected.png
+++ /dev/null
Binary files differ
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml b/OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml
deleted file mode 100644
index 981e38a0b..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_decrypt.xml
+++ /dev/null
@@ -1,28 +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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dashboard_decrypt_pressed"
- android:state_focused="true"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_decrypt_pressed"
- android:state_focused="false"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_decrypt_pressed"
- android:state_focused="true" />
- <item android:drawable="@drawable/dashboard_decrypt_default"
- android:state_focused="false"
- android:state_pressed="false" />
-</selector>
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml b/OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml
deleted file mode 100644
index af812dc51..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_encrypt.xml
+++ /dev/null
@@ -1,28 +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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dashboard_encrypt_pressed"
- android:state_focused="true"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_encrypt_pressed"
- android:state_focused="false"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_encrypt_pressed"
- android:state_focused="true" />
- <item android:drawable="@drawable/dashboard_encrypt_default"
- android:state_focused="false"
- android:state_pressed="false" />
-</selector>
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_help.xml b/OpenPGP-Keychain/res/drawable/dashboard_help.xml
deleted file mode 100644
index e121ea0d1..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_help.xml
+++ /dev/null
@@ -1,28 +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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dashboard_help_pressed"
- android:state_focused="true"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_help_pressed"
- android:state_focused="false"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_help_pressed"
- android:state_focused="true" />
- <item android:drawable="@drawable/dashboard_help_default"
- android:state_focused="false"
- android:state_pressed="false" />
-</selector>
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_import.xml b/OpenPGP-Keychain/res/drawable/dashboard_import.xml
deleted file mode 100644
index e5857dc6c..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_import.xml
+++ /dev/null
@@ -1,28 +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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dashboard_import_pressed"
- android:state_focused="true"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_import_pressed"
- android:state_focused="false"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_import_pressed"
- android:state_focused="true" />
- <item android:drawable="@drawable/dashboard_import_default"
- android:state_focused="false"
- android:state_pressed="false" />
-</selector>
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml b/OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml
deleted file mode 100644
index ebc519253..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_manage_keys.xml
+++ /dev/null
@@ -1,28 +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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dashboard_manage_keys_pressed"
- android:state_focused="true"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_manage_keys_pressed"
- android:state_focused="false"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_manage_keys_pressed"
- android:state_focused="true" />
- <item android:drawable="@drawable/dashboard_manage_keys_default"
- android:state_focused="false"
- android:state_pressed="false" />
-</selector>
diff --git a/OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml b/OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml
deleted file mode 100644
index d4045db45..000000000
--- a/OpenPGP-Keychain/res/drawable/dashboard_my_keys.xml
+++ /dev/null
@@ -1,28 +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.
- -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/dashboard_my_keys_pressed"
- android:state_focused="true"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_my_keys_pressed"
- android:state_focused="false"
- android:state_pressed="true" />
- <item android:drawable="@drawable/dashboard_my_keys_pressed"
- android:state_focused="true" />
- <item android:drawable="@drawable/dashboard_my_keys_default"
- android:state_focused="false"
- android:state_pressed="false" />
-</selector>
diff --git a/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
index a40444e0f..a88d7afd2 100644
--- a/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/api_app_settings_fragment.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
@@ -41,12 +42,16 @@
android:layout_height="wrap_content"
android:orientation="horizontal" >
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/api_app_settings_select_key_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:text="@string/api_settings_select_key" />
+ android:layout_margin="4dp"
+ android:text="@string/api_settings_select_key"
+ bootstrapbutton:bb_icon_left="fa-key"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
<LinearLayout
android:layout_width="match_parent"
@@ -76,11 +81,16 @@
</LinearLayout>
</LinearLayout>
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/api_app_settings_advanced_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/api_settings_show_advanced" />
+ android:layout_gravity="center_vertical"
+ android:layout_margin="4dp"
+ android:text="@string/api_settings_show_advanced"
+ bootstrapbutton:bb_icon_left="fa-caret-up"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
<LinearLayout
android:id="@+id/api_app_settings_advanced"
diff --git a/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml b/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
index 11f663f7f..71fbcfb12 100644
--- a/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
+++ b/OpenPGP-Keychain/res/layout/api_apps_list_activity.xml
@@ -1,12 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent" >
- <fragment
- android:id="@+id/crypto_consumers_list_fragment"
- android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListFragment"
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent" >
-</LinearLayout> \ No newline at end of file
+ <fragment
+ android:id="@+id/crypto_consumers_list_fragment"
+ android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+
+ <include layout="@layout/drawer_list" />
+
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/create_key.xml b/OpenPGP-Keychain/res/layout/create_key_dialog.xml
index 888f9f898..a2e908433 100644
--- a/OpenPGP-Keychain/res/layout/create_key.xml
+++ b/OpenPGP-Keychain/res/layout/create_key_dialog.xml
@@ -1,26 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
<TableLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
@@ -44,7 +28,7 @@
<Spinner
android:id="@+id/create_key_algorithm"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp" />
</TableRow>
@@ -60,7 +44,7 @@
<Spinner
android:id="@+id/create_key_size"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:padding="4dp" />
diff --git a/OpenPGP-Keychain/res/layout/decrypt.xml b/OpenPGP-Keychain/res/layout/decrypt.xml
deleted file mode 100644
index 0533e6d72..000000000
--- a/OpenPGP-Keychain/res/layout/decrypt.xml
+++ /dev/null
@@ -1,180 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:fillViewport="true"
- android:orientation="vertical" >
-
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:fillViewport="true" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="10dp"
- android:paddingRight="10dp" >
-
- <LinearLayout
- android:id="@+id/signature"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:orientation="horizontal"
- android:padding="4dp"
- android:paddingLeft="10dp"
- android:paddingRight="10dp" >
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" >
-
- <ImageView
- android:id="@+id/ic_signature"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/signed_large" />
-
- <ImageView
- android:id="@+id/ic_signature_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/overlay_error" />
- </RelativeLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="5dip" >
-
- <TextView
- android:id="@+id/mainUserId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:text="Main User Id"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@+id/mainUserIdRest"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:text="Main User Id Rest"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <ImageView
- android:id="@+id/sourcePrevious"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_previous" />
-
- <TextView
- android:id="@+id/sourceLabel"
- style="@style/SectionHeader"
- android:layout_width="0dip"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical"
- android:text="@string/label_message"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <ImageView
- android:id="@+id/sourceNext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_next" />
- </LinearLayout>
-
- <ViewFlipper
- android:id="@+id/source"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1" >
-
- <LinearLayout
- android:id="@+id/sourceMessage"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:padding="4dp" >
-
- <EditText
- android:id="@+id/message"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="top"
- android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
- android:scrollHorizontally="true" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/sourceFile"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:padding="4dp" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <EditText
- android:id="@+id/filename"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:inputType="textNoSuggestions" />
-
- <ImageButton
- android:id="@+id/btn_browse"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_menu_filebrowser" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <CheckBox
- android:id="@+id/deleteAfterDecryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_delete_after_decryption" />
- </LinearLayout>
- </LinearLayout>
- </ViewFlipper>
- </LinearLayout>
- </ScrollView>
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/decrypt_activity.xml b/OpenPGP-Keychain/res/layout/decrypt_activity.xml
new file mode 100644
index 000000000..9d9e1a1e8
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/decrypt_activity.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true"
+ android:orientation="vertical" >
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp" >
+
+ <LinearLayout
+ android:id="@+id/signature"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:orientation="horizontal"
+ android:padding="4dp"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp" >
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <ImageView
+ android:id="@+id/ic_signature"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/signed_large" />
+
+ <ImageView
+ android:id="@+id/ic_signature_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/overlay_error" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="5dip" >
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:text="Main User Id"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:text="Main User Id Rest"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <ImageView
+ android:id="@+id/sourcePrevious"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous" />
+
+ <TextView
+ android:id="@+id/sourceLabel"
+ style="@style/SectionHeader"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_horizontal|center_vertical"
+ android:text="@string/label_message"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <ImageView
+ android:id="@+id/sourceNext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next" />
+ </LinearLayout>
+
+ <ViewFlipper
+ android:id="@+id/source"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" >
+
+ <LinearLayout
+ android:id="@+id/sourceMessage"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <EditText
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="top"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
+ android:scrollHorizontally="true" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sourceFile"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <EditText
+ android:id="@+id/filename"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textNoSuggestions" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/btn_browse"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ bootstrapbutton:bb_icon_left="fa-folder-open"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <CheckBox
+ android:id="@+id/deleteAfterDecryption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_delete_after_decryption" />
+ </LinearLayout>
+ </LinearLayout>
+ </ViewFlipper>
+ </LinearLayout>
+ </ScrollView>
+ </LinearLayout>
+
+ <include layout="@layout/drawer_list" />
+
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/drawer_list.xml b/OpenPGP-Keychain/res/layout/drawer_list.xml
new file mode 100644
index 000000000..18210afc5
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/drawer_list.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ android:layout_gravity="start" tells DrawerLayout to treat
+ this as a sliding drawer on the left side for left-to-right
+ languages and on the right side for right-to-left languages.
+ The drawer is given a fixed width in dp and extends the full height of
+ the container. A solid background is used for contrast
+ with the content view.
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/left_drawer"
+ android:layout_width="240dp"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:background="#fff"
+ android:choiceMode="singleChoice"
+ android:divider="@color/bg_gray"
+ android:dividerHeight="1dp" />
diff --git a/OpenPGP-Keychain/res/layout/drawer_list_item.xml b/OpenPGP-Keychain/res/layout/drawer_list_item.xml
new file mode 100644
index 000000000..14760e79d
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/drawer_list_item.xml
@@ -0,0 +1,28 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:fontawesometext="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <com.beardedhen.androidbootstrap.FontAwesomeText
+ android:id="@+id/drawer_item_icon"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_margin="10dp"
+ android:gravity="center_vertical"
+ android:textSize="24sp"
+ fontawesometext:fa_icon="fa-github" />
+
+ <TextView
+ android:id="@+id/drawer_item_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:paddingBottom="16dp"
+ android:paddingLeft="4dp"
+ android:paddingRight="16dp"
+ android:paddingTop="16dp"
+ android:textAppearance="@android:style/TextAppearance.Medium"
+ android:textColor="#111" />
+
+</LinearLayout>
diff --git a/OpenPGP-Keychain/res/layout/edit_key.xml b/OpenPGP-Keychain/res/layout/edit_key_activity.xml
index 20cc9f2ac..f8597b0df 100644
--- a/OpenPGP-Keychain/res/layout/edit_key.xml
+++ b/OpenPGP-Keychain/res/layout/edit_key_activity.xml
@@ -16,6 +16,7 @@
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true"
@@ -41,12 +42,14 @@
android:layout_height="wrap_content"
android:text="@string/label_no_passphrase" />
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/edit_key_btn_change_pass_phrase"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp"
- android:text="@string/btn_set_passphrase" />
+ android:text="@string/btn_set_passphrase"
+ bootstrapbutton:bb_icon_left="fa-pencil"
+ bootstrapbutton:bb_type="info" />
<LinearLayout
android:id="@+id/edit_key_container"
diff --git a/OpenPGP-Keychain/res/layout/edit_key_key_item.xml b/OpenPGP-Keychain/res/layout/edit_key_key_item.xml
index 521e8ce2c..11bce64ee 100644
--- a/OpenPGP-Keychain/res/layout/edit_key_key_item.xml
+++ b/OpenPGP-Keychain/res/layout/edit_key_key_item.xml
@@ -1,27 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<org.sufficientlysecure.keychain.ui.widget.KeyEditor xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -80,7 +65,7 @@
<TextView
android:id="@+id/creation"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content" />
</TableRow>
@@ -94,10 +79,12 @@
android:paddingRight="10dip"
android:text="@string/label_expiry" />
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/expiry"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" />
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/none"
+ bootstrapbutton:bb_type="default" />
</TableRow>
<TableRow>
@@ -112,22 +99,25 @@
<Spinner
android:id="@+id/usage"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content" />
</TableRow>
</TableLayout>
- <ImageButton
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/delete"
- style="@style/MinusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_margin="10dp"
+ bootstrapbutton:bb_icon_left="fa-minus"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="small"
+ bootstrapbutton:bb_type="danger" />
</LinearLayout>
<View
android:id="@+id/separator"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
diff --git a/OpenPGP-Keychain/res/layout/edit_key_section.xml b/OpenPGP-Keychain/res/layout/edit_key_section.xml
index dd0a5d4bd..9f10ff8c1 100644
--- a/OpenPGP-Keychain/res/layout/edit_key_section.xml
+++ b/OpenPGP-Keychain/res/layout/edit_key_section.xml
@@ -1,31 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<org.sufficientlysecure.keychain.ui.widget.SectionView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/header"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clickable="true"
- android:focusable="true"
android:gravity="center_vertical"
android:orientation="horizontal" >
@@ -40,19 +23,22 @@
android:singleLine="true"
android:text="Section Name" />
- <ImageView
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/plusbutton"
- style="@style/PlusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:duplicateParentState="true" />
+ android:layout_margin="10dp"
+ bootstrapbutton:bb_icon_left="fa-plus"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="small"
+ bootstrapbutton:bb_type="success" />
</LinearLayout>
<LinearLayout
android:id="@+id/editors"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="6dip" />
-</org.sufficientlysecure.keychain.ui.widget.SectionView>
+</org.sufficientlysecure.keychain.ui.widget.SectionView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/edit_key_user_id_item.xml b/OpenPGP-Keychain/res/layout/edit_key_user_id_item.xml
index a3955389f..663949d8e 100644
--- a/OpenPGP-Keychain/res/layout/edit_key_user_id_item.xml
+++ b/OpenPGP-Keychain/res/layout/edit_key_user_id_item.xml
@@ -1,22 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<org.sufficientlysecure.keychain.ui.widget.UserIdEditor xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
@@ -27,7 +12,7 @@
android:text="@string/label_main_user_id" />
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -48,7 +33,7 @@
<EditText
android:id="@+id/name"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textPersonName|textCapWords" />
@@ -66,7 +51,7 @@
<EditText
android:id="@+id/email"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textEmailAddress" />
@@ -84,23 +69,29 @@
<EditText
android:id="@+id/comment"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
</TableRow>
</TableLayout>
- <ImageButton
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/delete"
- style="@style/MinusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
+ android:layout_gravity="center_vertical"
+ android:layout_margin="10dp"
+ android:layout_marginLeft="4dip"
+ android:layout_marginRight="6dip"
+ bootstrapbutton:bb_icon_left="fa-minus"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="small"
+ bootstrapbutton:bb_type="danger" />
</LinearLayout>
<View
android:id="@+id/separator"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
diff --git a/OpenPGP-Keychain/res/layout/encrypt.xml b/OpenPGP-Keychain/res/layout/encrypt.xml
deleted file mode 100644
index c9c6be5df..000000000
--- a/OpenPGP-Keychain/res/layout/encrypt.xml
+++ /dev/null
@@ -1,306 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:fillViewport="true" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="10dp"
- android:paddingRight="10dp" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="4dp" >
-
- <ImageView
- android:id="@+id/modePrevious"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_previous" />
-
- <TextView
- android:id="@+id/modeLabel"
- style="@style/SectionHeader"
- android:layout_width="0dip"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical"
- android:text="@string/label_asymmetric" />
-
- <ImageView
- android:id="@+id/modeNext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_next" />
- </LinearLayout>
-
- <ViewFlipper
- android:id="@+id/mode"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content" >
-
- <LinearLayout
- android:id="@+id/modeAsymmetric"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="4dp" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <CheckBox
- android:id="@+id/sign"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_sign" />
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="16dp" >
-
- <TextView
- android:id="@+id/mainUserId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="Sign User Id"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@+id/mainUserIdRest"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="Sign email"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingBottom="3dip" >
-
- <TextView
- android:id="@+id/label_selectPublicKeys"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:text="@string/label_select_public_keys"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <Button
- android:id="@+id/btn_selectEncryptKeys"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/btn_select_encrypt_keys" />
- </LinearLayout>
- </LinearLayout>
-
- <TableLayout
- android:id="@+id/modeSymmetric"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:padding="4dp"
- android:stretchColumns="1" >
-
- <TableRow>
-
- <TextView
- android:id="@+id/label_passPhrase"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_passphrase"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <EditText
- android:id="@+id/passPhrase"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:inputType="textPassword" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:id="@+id/label_passPhraseAgain"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_passphrase_again"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <EditText
- android:id="@+id/passPhraseAgain"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:inputType="textPassword" />
- </TableRow>
- </TableLayout>
- </ViewFlipper>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="4dp" >
-
- <ImageView
- android:id="@+id/sourcePrevious"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_previous" />
-
- <TextView
- android:id="@+id/sourceLabel"
- style="@style/SectionHeader"
- android:layout_width="0dip"
- android:layout_height="fill_parent"
- android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical"
- android:text="@string/label_message"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <ImageView
- android:id="@+id/sourceNext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_next" />
- </LinearLayout>
-
- <ViewFlipper
- android:id="@+id/source"
- android:layout_width="fill_parent"
- android:layout_height="0dip"
- android:layout_weight="1" >
-
- <LinearLayout
- android:id="@+id/sourceMessage"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:padding="4dp" >
-
- <EditText
- android:id="@+id/message"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="top"
- android:inputType="text|textCapSentences|textMultiLine|textLongMessage" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/sourceFile"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- android:padding="4dp" >
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <EditText
- android:id="@+id/filename"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:inputType="textNoSuggestions" />
-
- <ImageButton
- android:id="@+id/btn_browse"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_menu_filebrowser" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <TextView
- android:id="@+id/label_fileCompression"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:paddingRight="10dip"
- android:text="@string/label_file_compression"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <Spinner
- android:id="@+id/fileCompression"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <CheckBox
- android:id="@+id/deleteAfterEncryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_delete_after_encryption" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <CheckBox
- android:id="@+id/asciiArmour"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_ascii_armor" />
- </LinearLayout>
- </LinearLayout>
- </ViewFlipper>
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/encrypt_activity.xml b/OpenPGP-Keychain/res/layout/encrypt_activity.xml
new file mode 100644
index 000000000..eea484df2
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/encrypt_activity.xml
@@ -0,0 +1,307 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="4dp" >
+
+ <ImageView
+ android:id="@+id/modePrevious"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous" />
+
+ <TextView
+ android:id="@+id/modeLabel"
+ style="@style/SectionHeader"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_horizontal|center_vertical"
+ android:text="@string/label_asymmetric" />
+
+ <ImageView
+ android:id="@+id/modeNext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next" />
+ </LinearLayout>
+
+ <ViewFlipper
+ android:id="@+id/mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:id="@+id/modeAsymmetric"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <CheckBox
+ android:id="@+id/sign"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_sign" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp" >
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="Sign User Id"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text="Sign email"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="3dip" >
+
+ <TextView
+ android:id="@+id/label_selectPublicKeys"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:text="@string/label_select_public_keys"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/btn_selectEncryptKeys"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_margin="4dp"
+ android:text="@string/btn_select_encrypt_keys"
+ bootstrapbutton:bb_icon_left="fa-user"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <TableLayout
+ android:id="@+id/modeSymmetric"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:stretchColumns="1" >
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_passPhrase"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_passphrase"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/passPhrase"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_passPhraseAgain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_passphrase_again"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/passPhraseAgain"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+ </TableRow>
+ </TableLayout>
+ </ViewFlipper>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="4dp" >
+
+ <ImageView
+ android:id="@+id/sourcePrevious"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_previous" />
+
+ <TextView
+ android:id="@+id/sourceLabel"
+ style="@style/SectionHeader"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_horizontal|center_vertical"
+ android:text="@string/label_message"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <ImageView
+ android:id="@+id/sourceNext"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/ic_next" />
+ </LinearLayout>
+
+ <ViewFlipper
+ android:id="@+id/source"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" >
+
+ <LinearLayout
+ android:id="@+id/sourceMessage"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <EditText
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="top"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/sourceFile"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="4dp" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <EditText
+ android:id="@+id/filename"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:inputType="textNoSuggestions" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/btn_browse"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ bootstrapbutton:bb_icon_left="fa-folder-open"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/label_fileCompression"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:paddingRight="10dip"
+ android:text="@string/label_file_compression"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <Spinner
+ android:id="@+id/fileCompression"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <CheckBox
+ android:id="@+id/deleteAfterEncryption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_delete_after_encryption" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <CheckBox
+ android:id="@+id/asciiArmour"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_ascii_armor" />
+ </LinearLayout>
+ </LinearLayout>
+ </ViewFlipper>
+ </LinearLayout>
+ </ScrollView>
+
+ <include layout="@layout/drawer_list" />
+
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/file_dialog.xml b/OpenPGP-Keychain/res/layout/file_dialog.xml
index ce3b1ea60..87816067f 100644
--- a/OpenPGP-Keychain/res/layout/file_dialog.xml
+++ b/OpenPGP-Keychain/res/layout/file_dialog.xml
@@ -16,6 +16,7 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -39,13 +40,17 @@
android:minLines="2"
android:scrollbars="vertical" />
- <ImageButton
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/btn_browse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:layout_margin="4dp"
android:contentDescription="@string/filemanager_title_open"
- android:src="@drawable/ic_menu_filebrowser" />
+ bootstrapbutton:bb_icon_left="fa-folder-open"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
</LinearLayout>
<CheckBox
diff --git a/OpenPGP-Keychain/res/layout/help_fragment_about.xml b/OpenPGP-Keychain/res/layout/help_about_fragment.xml
index 71788e720..71788e720 100644
--- a/OpenPGP-Keychain/res/layout/help_fragment_about.xml
+++ b/OpenPGP-Keychain/res/layout/help_about_fragment.xml
diff --git a/OpenPGP-Keychain/res/layout/help_activity.xml b/OpenPGP-Keychain/res/layout/help_activity.xml
index 89aac6476..77c62e03f 100644
--- a/OpenPGP-Keychain/res/layout/help_activity.xml
+++ b/OpenPGP-Keychain/res/layout/help_activity.xml
@@ -1,13 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <android.support.v4.view.ViewPager
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/pager"
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </LinearLayout>
+
+ <include layout="@layout/drawer_list" />
-</LinearLayout> \ No newline at end of file
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys.xml b/OpenPGP-Keychain/res/layout/import_keys.xml
deleted file mode 100644
index ae391e2fd..000000000
--- a/OpenPGP-Keychain/res/layout/import_keys.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true" >
-
- <FrameLayout
- android:id="@+id/import_navigation_fragment"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:orientation="vertical"
- android:paddingLeft="4dp"
- android:paddingRight="4dp" />
-
- <LinearLayout
- android:id="@+id/import_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:orientation="vertical"
- android:paddingLeft="10dp"
- android:paddingRight="10dp" >
-
- <Button
- android:id="@+id/import_import"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="importOnClick"
- android:text="@string/import_import" />
-
- <Button
- android:id="@+id/import_sign_and_upload"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:onClick="signAndUploadOnClick"
- android:text="@string/import_sign_and_upload" />
- </LinearLayout>
-
- <FrameLayout
- android:id="@+id/import_keys_list_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_above="@+id/import_footer"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/import_navigation_fragment"
- android:orientation="vertical"
- android:paddingLeft="4dp"
- android:paddingRight="4dp" />
-
-</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys_activity.xml b/OpenPGP-Keychain/res/layout/import_keys_activity.xml
new file mode 100644
index 000000000..c2217d2ec
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/import_keys_activity.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true" >
+
+ <FrameLayout
+ android:id="@+id/import_navigation_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:orientation="vertical"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp" />
+
+ <LinearLayout
+ android:id="@+id/import_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:orientation="vertical"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp" >
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/import_import"
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:padding="4dp"
+ android:text="@string/import_import"
+ bootstrapbutton:bb_icon_left="fa-download"
+ bootstrapbutton:bb_type="info" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/import_sign_and_upload"
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:padding="4dp"
+ android:text="@string/import_sign_and_upload"
+ bootstrapbutton:bb_type="info" />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/import_keys_list_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/import_footer"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/import_navigation_fragment"
+ android:orientation="vertical"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp" />
+ </RelativeLayout>
+
+ <include layout="@layout/drawer_list" />
+
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml
index bb3b8a1cb..961c6523a 100644
--- a/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/import_keys_clipboard_fragment.xml
@@ -1,13 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_clipboard_button"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/import_from_clipboard" />
+ android:layout_height="60dp"
+ android:layout_margin="10dp"
+ android:text="@string/import_from_clipboard"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys_file_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_file_fragment.xml
index 896be3989..126bcd8a6 100644
--- a/OpenPGP-Keychain/res/layout/import_keys_file_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/import_keys_file_fragment.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -16,12 +17,16 @@
android:minLines="2"
android:scrollbars="vertical" />
- <ImageButton
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_keys_file_browse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:layout_margin="4dp"
android:contentDescription="@string/filemanager_title_open"
- android:src="@drawable/ic_menu_filebrowser" />
+ bootstrapbutton:bb_icon_left="fa-folder-open"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys_keyserver_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_keyserver_fragment.xml
index 18e87205b..74a2d7853 100644
--- a/OpenPGP-Keychain/res/layout/import_keys_keyserver_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/import_keys_keyserver_fragment.xml
@@ -1,13 +1,17 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_keyserver_button"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/menu_key_server" />
+ android:layout_height="60dp"
+ android:layout_margin="10dp"
+ android:text="@string/menu_key_server"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
<!-- <Spinner -->
<!-- android:id="@+id/import_keys_server_key_server" -->
diff --git a/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml
index b34bb80c3..6d70578af 100644
--- a/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/import_keys_nfc_fragment.xml
@@ -1,13 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_nfc_button"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/menu_import_from_nfc" />
+ android:layout_height="60dp"
+ android:layout_margin="10dp"
+ android:text="@string/menu_import_from_nfc"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml b/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml
index 0a49b014a..f3c09a41d 100644
--- a/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml
+++ b/OpenPGP-Keychain/res/layout/import_keys_qr_code_fragment.xml
@@ -1,14 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
- <Button
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/import_qrcode_button"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/menu_import_from_qr_code" />
+ android:layout_height="60dp"
+ android:layout_margin="10dp"
+ android:text="@string/menu_import_from_qr_code"
+ bootstrapbutton:bb_icon_left="fa-barcode"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
<TextView
android:id="@+id/import_qrcode_text"
diff --git a/OpenPGP-Keychain/res/layout/key_list_child_item.xml b/OpenPGP-Keychain/res/layout/key_list_child_item.xml
deleted file mode 100644
index 81d843529..000000000
--- a/OpenPGP-Keychain/res/layout/key_list_child_item.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="?android:attr/listPreferredItemHeight"
- android:layout_marginRight="?android:attr/scrollbarSize"
- android:orientation="vertical"
- android:singleLine="true" >
-
- <LinearLayout
- android:id="@+id/keyLayout"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingLeft="8dip"
- android:paddingRight="3dip" >
-
- <ImageView
- android:id="@+id/ic_masterKey"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="6dip"
- android:src="@drawable/key_small" />
-
- <TextView
- android:id="@+id/keyId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text="Key ID"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:typeface="monospace" />
-
- <TextView
- android:id="@+id/keyDetails"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="(RSA, 1024bit)"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:layout_gravity="center_vertical"
- android:gravity="right"
- android:orientation="horizontal"
- android:paddingBottom="2dip"
- android:paddingTop="2dip" >
-
- <ImageView
- android:id="@+id/ic_certifyKey"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/certify_small" />
-
- <ImageView
- android:id="@+id/ic_encryptKey"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/encrypted_small" />
-
- <ImageView
- android:id="@+id/ic_signKey"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/signed_small" />
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/userIdLayout"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="36dip"
- android:singleLine="true" >
-
- <TextView
- android:id="@+id/userId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="3dip"
- android:text="User ID"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
-
-</LinearLayout>
diff --git a/OpenPGP-Keychain/res/layout/key_list_group_item.xml b/OpenPGP-Keychain/res/layout/key_list_item.xml
index 240be54b0..2571bb6e7 100644
--- a/OpenPGP-Keychain/res/layout/key_list_group_item.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_item.xml
@@ -1,26 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:layout_marginRight="?android:attr/scrollbarSize"
android:orientation="vertical"
- android:paddingLeft="36dip"
+ android:paddingLeft="8dp"
android:singleLine="true" >
<TextView
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
index a35e23038..f0e843e56 100644
--- a/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_public_activity.xml
@@ -1,25 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:layout_height="match_parent" >
- <fragment
- android:id="@+id/key_list_public_fragment"
- android:name="org.sufficientlysecure.keychain.ui.KeyListPublicFragment"
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_height="match_parent" >
- <TextView
- android:layout_width="match_parent"
- android:layout_height="90dp"
- android:layout_weight="1"
- android:background="@drawable/abs__ab_bottom_solid_light_holo"
- android:paddingBottom="3dp"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:paddingTop="3dp"
- android:text="@string/list_information" />
+ <fragment
+ android:id="@+id/key_list_public_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.KeyListPublicFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+
+ <include layout="@layout/drawer_list" />
-</LinearLayout> \ No newline at end of file
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
new file mode 100644
index 000000000..828809601
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_list_public_fragment.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <se.emilsjolander.stickylistheaders.StickyListHeadersListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:drawSelectorOnTop="true"
+ android:fastScrollEnabled="true"
+ android:paddingBottom="16dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="32dp"
+ android:scrollbarStyle="outsideOverlay" />
+
+ <LinearLayout
+ android:id="@+id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="gone" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/key_list_empty_text1"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/key_list_empty_text2"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/key_list_empty_button_create"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="@string/key_list_empty_button_create"
+ bootstrapbutton:bb_icon_left="fa-plus"
+ bootstrapbutton:bb_type="default" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/key_list_empty_text3"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/key_list_empty_button_import"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="10dp"
+ android:text="@string/key_list_empty_button_import"
+ bootstrapbutton:bb_icon_left="fa-download"
+ bootstrapbutton:bb_type="default" />
+ </LinearLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_public_header.xml b/OpenPGP-Keychain/res/layout/key_list_public_header.xml
new file mode 100644
index 000000000..5768e4153
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_list_public_header.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+
+ <org.sufficientlysecure.keychain.ui.widget.UnderlineTextView
+ android:id="@+id/stickylist_header_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|left"
+ android:padding="8dp"
+ android:textColor="@color/emphasis"
+ android:textSize="17sp"
+ android:textStyle="bold" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml b/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
index d4397c444..13370f2e5 100644
--- a/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
+++ b/OpenPGP-Keychain/res/layout/key_list_secret_activity.xml
@@ -1,25 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drawer_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:layout_height="match_parent" >
- <fragment
- android:id="@+id/key_list_secret_fragment"
- android:name="org.sufficientlysecure.keychain.ui.KeyListSecretFragment"
+ <FrameLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1" />
+ android:layout_height="match_parent" >
- <TextView
- android:layout_width="match_parent"
- android:layout_height="90dp"
- android:layout_weight="1"
- android:background="@drawable/abs__ab_bottom_solid_light_holo"
- android:paddingBottom="3dp"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
- android:paddingTop="3dp"
- android:text="@string/list_information" />
+ <fragment
+ android:id="@+id/key_list_secret_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.KeyListSecretFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ </FrameLayout>
+
+ <include layout="@layout/drawer_list" />
-</LinearLayout> \ No newline at end of file
+</android.support.v4.widget.DrawerLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_editor.xml b/OpenPGP-Keychain/res/layout/key_server_editor.xml
index 51ff8a663..058a43eaf 100644
--- a/OpenPGP-Keychain/res/layout/key_server_editor.xml
+++ b/OpenPGP-Keychain/res/layout/key_server_editor.xml
@@ -1,52 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<org.sufficientlysecure.keychain.ui.widget.KeyServerEditor
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+<org.sufficientlysecure.keychain.ui.widget.KeyServerEditor xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical" >
<LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:orientation="horizontal"
- android:layout_marginLeft="3dip">
+ android:layout_marginLeft="3dip"
+ android:orientation="horizontal" >
<EditText
android:id="@+id/server"
- android:layout_weight="1"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:inputType="textUri"/>
-
- <ImageButton
- android:id="@+id/delete"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/MinusButton"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="3dip"/>
+ android:layout_weight="1"
+ android:inputType="textUri" />
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/delete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_margin="10dp"
+ android:layout_marginRight="3dip"
+ bootstrapbutton:bb_icon_left="fa-minus"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="small"
+ bootstrapbutton:bb_type="danger" />
</LinearLayout>
<View
android:id="@+id/separator"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="1dip"
- android:background="?android:attr/listDivider"/>
+ android:background="?android:attr/listDivider" />
-</org.sufficientlysecure.keychain.ui.widget.KeyServerEditor>
+</org.sufficientlysecure.keychain.ui.widget.KeyServerEditor> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_export.xml b/OpenPGP-Keychain/res/layout/key_server_export.xml
new file mode 100644
index 000000000..30e4e0644
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/key_server_export.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <Spinner
+ android:id="@+id/keyServer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <Button
+ android:id="@+id/btn_export_to_server"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/btn_export_to_server" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_export_layout.xml b/OpenPGP-Keychain/res/layout/key_server_export_layout.xml
deleted file mode 100644
index 257f087ee..000000000
--- a/OpenPGP-Keychain/res/layout/key_server_export_layout.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <Spinner
- android:id="@+id/keyServer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <Button
- android:id="@+id/btn_export_to_server"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_export_to_server" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_preference.xml b/OpenPGP-Keychain/res/layout/key_server_preference.xml
index e9b1d5a95..8b99e5d2f 100644
--- a/OpenPGP-Keychain/res/layout/key_server_preference.xml
+++ b/OpenPGP-Keychain/res/layout/key_server_preference.xml
@@ -1,21 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
@@ -57,15 +42,18 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
</RelativeLayout>
- <ImageView
+ <com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/add"
- style="@style/PlusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
+ android:layout_margin="10dp"
android:layout_marginLeft="4dip"
android:layout_marginRight="6dip"
- android:clickable="true" />
+ bootstrapbutton:bb_icon_left="fa-plus"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="small"
+ bootstrapbutton:bb_type="success" />
</LinearLayout>
<View
diff --git a/OpenPGP-Keychain/res/layout/key_server_query_layout.xml b/OpenPGP-Keychain/res/layout/key_server_query.xml
index 5eca10c45..20f260a95 100644
--- a/OpenPGP-Keychain/res/layout/key_server_query_layout.xml
+++ b/OpenPGP-Keychain/res/layout/key_server_query.xml
@@ -1,32 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:orientation="vertical" >
<Spinner
android:id="@+id/keyServer"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -49,7 +33,7 @@
<ListView
android:id="@+id/list"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1" />
diff --git a/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml b/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml
index 29c8b88f4..6d883d26d 100644
--- a/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml
+++ b/OpenPGP-Keychain/res/layout/key_server_query_result_item.xml
@@ -1,97 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:singleLine="true"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
android:paddingLeft="3dip"
android:paddingRight="?android:attr/scrollbarSize"
- android:layout_height="wrap_content"
- android:layout_width="fill_parent"
- android:orientation="vertical">
+ android:singleLine="true" >
<LinearLayout
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
<LinearLayout
- android:orientation="vertical"
- android:paddingLeft="5dip"
- android:paddingRight="5dip"
android:layout_width="0dip"
android:layout_height="wrap_content"
- android:layout_weight="1">
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingLeft="5dip"
+ android:paddingRight="5dip" >
<TextView
android:id="@+id/mainUserId"
- android:text="Main User ID"
- android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:text="Main User ID"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="@+id/mainUserIdRest"
- android:text="&lt;user@somewhere.com&gt;"
- android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
+ android:layout_height="wrap_content"
+ android:text="&lt;user@somewhere.com>"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
<LinearLayout
- android:orientation="vertical"
- android:minWidth="90dip"
- android:paddingLeft="3dip"
- android:gravity="right"
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:gravity="right"
+ android:minWidth="90dip"
+ android:orientation="vertical"
+ android:paddingLeft="3dip" >
<TextView
android:id="@+id/keyId"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
android:text="BBBBBBBB"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:typeface="monospace"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"/>
+ android:typeface="monospace" />
<TextView
android:id="@+id/algorithm"
- android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
android:id="@+id/status"
- android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textColor="#e00"/>
-
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="#e00" />
</LinearLayout>
-
</LinearLayout>
<LinearLayout
android:id="@+id/list"
- android:orientation="vertical"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="30dip">
-
+ android:layout_marginLeft="30dip"
+ android:orientation="vertical" >
</LinearLayout>
-</LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml b/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml
index 9d3a4a1ab..c69735e67 100644
--- a/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml
+++ b/OpenPGP-Keychain/res/layout/key_server_query_result_user_id.xml
@@ -1,26 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_marginRight="?android:attr/scrollbarSize"
- android:singleLine="true"
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:paddingRight="3dip">
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:paddingRight="3dip"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall" >
-</TextView>
+</TextView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/main.xml b/OpenPGP-Keychain/res/layout/main.xml
deleted file mode 100644
index 71967271b..000000000
--- a/OpenPGP-Keychain/res/layout/main.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <org.sufficientlysecure.keychain.ui.widget.DashboardLayout
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" >
-
- <Button
- android:id="@+id/dashboard_manage_keys"
- style="@style/DashboardButton"
- android:drawableTop="@drawable/dashboard_manage_keys"
- android:onClick="manageKeysOnClick"
- android:text="@string/dashboard_manage_keys" />
-
- <Button
- android:id="@+id/dashboard_my_keys"
- style="@style/DashboardButton"
- android:drawableTop="@drawable/dashboard_my_keys"
- android:onClick="myKeysOnClick"
- android:text="@string/dashboard_my_keys" />
-
- <Button
- android:id="@+id/dashboard_encrypt"
- style="@style/DashboardButton"
- android:drawableTop="@drawable/dashboard_encrypt"
- android:onClick="encryptOnClick"
- android:text="@string/dashboard_encrypt" />
-
- <Button
- android:id="@+id/dashboard_decrypt"
- style="@style/DashboardButton"
- android:drawableTop="@drawable/dashboard_decrypt"
- android:onClick="decryptOnClick"
- android:text="@string/dashboard_decrypt" />
-
- <Button
- android:id="@+id/dashboard_scan_qrcode"
- style="@style/DashboardButton"
- android:drawableTop="@drawable/dashboard_import"
- android:onClick="scanQrcodeOnClick"
- android:text="@string/dashboard_import_keys" />
-
- <Button
- android:id="@+id/dashboard_help"
- style="@style/DashboardButton"
- android:drawableTop="@drawable/dashboard_help"
- android:onClick="helpOnClick"
- android:text="@string/dashboard_help" />
- </org.sufficientlysecure.keychain.ui.widget.DashboardLayout>
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/passphrase.xml b/OpenPGP-Keychain/res/layout/passphrase.xml
deleted file mode 100644
index 778ae6b80..000000000
--- a/OpenPGP-Keychain/res/layout/passphrase.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp" >
-
- <TextView
- android:id="@+id/passphrase_label_passphrase"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:padding="4dp"
- android:text="@string/label_passphrase" />
-
- <EditText
- android:id="@+id/passphrase_passphrase"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:imeOptions="actionDone"
- android:inputType="textPassword"
- android:padding="4dp" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/passphrase_dialog.xml b/OpenPGP-Keychain/res/layout/passphrase_dialog.xml
new file mode 100644
index 000000000..4b331f0f2
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/passphrase_dialog.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp" >
+
+ <TextView
+ android:id="@+id/passphrase_label_passphrase"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="4dp"
+ android:text="@string/label_passphrase" />
+
+ <EditText
+ android:id="@+id/passphrase_passphrase"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:imeOptions="actionDone"
+ android:inputType="textPassword"
+ android:padding="4dp" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/passphrase_repeat.xml b/OpenPGP-Keychain/res/layout/passphrase_repeat_dialog.xml
index 1eef4eb68..2bdd231ee 100644
--- a/OpenPGP-Keychain/res/layout/passphrase_repeat.xml
+++ b/OpenPGP-Keychain/res/layout/passphrase_repeat_dialog.xml
@@ -1,22 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
@@ -34,7 +18,7 @@
<EditText
android:id="@+id/passphrase_passphrase"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:padding="4dp" />
@@ -52,7 +36,7 @@
<EditText
android:id="@+id/passphrase_passphrase_again"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textPassword"
diff --git a/OpenPGP-Keychain/res/layout/select_key_item.xml b/OpenPGP-Keychain/res/layout/select_key_item.xml
index d5b1655ed..bbfe17c44 100644
--- a/OpenPGP-Keychain/res/layout/select_key_item.xml
+++ b/OpenPGP-Keychain/res/layout/select_key_item.xml
@@ -1,22 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingLeft="3dip"
android:paddingRight="?android:attr/scrollbarSize"
@@ -63,7 +47,7 @@
<TextView
android:id="@+id/keyId"
android:layout_width="wrap_content"
- android:layout_height="fill_parent"
+ android:layout_height="match_parent"
android:text="BBBBBBBB"
android:textAppearance="?android:attr/textAppearanceSmall"
android:typeface="monospace" />
diff --git a/OpenPGP-Keychain/res/layout/share_nfc_beam.xml b/OpenPGP-Keychain/res/layout/share_nfc_beam.xml
deleted file mode 100644
index 692f53e4e..000000000
--- a/OpenPGP-Keychain/res/layout/share_nfc_beam.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
-
- <org.sufficientlysecure.htmltextview.HtmlTextView
- android:id="@+id/nfc_beam_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:text="" />
-
-</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml b/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml
index 88b06b698..66102d3e5 100644
--- a/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml
+++ b/OpenPGP-Keychain/res/layout/share_qr_code_dialog.xml
@@ -1,20 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/OpenPGP-Keychain/res/layout/sign_key_activity.xml b/OpenPGP-Keychain/res/layout/sign_key_activity.xml
new file mode 100644
index 000000000..b9f66db7f
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/sign_key_activity.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_send_key"
+ android:textAppearance="?android:attr/textAppearanceMedium" >
+ </TextView>
+
+ <CheckBox
+ android:id="@+id/sendKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:checked="true"
+ android:text="" >
+ </CheckBox>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <Spinner
+ android:id="@+id/keyServer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/sign"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/btn_sign" >
+ </Button>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/sign_key_layout.xml b/OpenPGP-Keychain/res/layout/sign_key_layout.xml
deleted file mode 100644
index 4530831ee..000000000
--- a/OpenPGP-Keychain/res/layout/sign_key_layout.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org> Licensed under the
- Apache License, Version 2.0 (the "License"); you may not use this file except
- in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software distributed
- under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
- OR CONDITIONS OF ANY KIND, either express or implied. See the License for
- the specific language governing permissions and limitations under the License. -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent" android:layout_height="fill_parent"
- android:orientation="vertical">
-
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:orientation="horizontal">
- </LinearLayout>
-
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:orientation="horizontal">
-
- <TextView android:id="@+id/textView1" android:layout_height="wrap_content"
- android:text="@string/label_send_key" android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"></TextView>
- <CheckBox android:text="" android:id="@+id/sendKey"
- android:layout_width="wrap_content" android:layout_height="wrap_content"
- android:checked="true"></CheckBox>
- </LinearLayout>
-
- <LinearLayout android:layout_width="fill_parent"
- android:layout_height="wrap_content" android:orientation="horizontal">
-
- <Spinner android:id="@+id/keyServer" android:layout_width="wrap_content"
- android:layout_height="wrap_content" android:layout_weight="1" />
- </LinearLayout>
-
- <Button android:layout_height="wrap_content"
- android:layout_width="wrap_content" android:text="@string/btn_sign"
- android:id="@+id/sign"></Button>
-
-</LinearLayout>
diff --git a/OpenPGP-Keychain/res/layout/view_key_activity.xml b/OpenPGP-Keychain/res/layout/view_key_activity.xml
new file mode 100644
index 000000000..babec70f6
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/view_key_activity.xml
@@ -0,0 +1,216 @@
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp" >
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:text="@string/section_master_user_id" />
+
+ <TableLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:stretchColumns="1" >
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_name" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_email" />
+
+ <TextView
+ android:id="@+id/email"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_comment" />
+
+ <TextView
+ android:id="@+id/comment"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+ </TableLayout>
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:text="@string/section_master_key" />
+
+ <TableLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:stretchColumns="1" >
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_key_id" />
+
+ <TextView
+ android:id="@+id/key_id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text=""
+ android:typeface="monospace" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_algorithm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_algorithm" />
+
+ <TextView
+ android:id="@+id/algorithm"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_creation" />
+
+ <TextView
+ android:id="@+id/creation"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_expiry" />
+
+ <TextView
+ android:id="@+id/expiry"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_fingerprint" />
+
+ <TextView
+ android:id="@+id/fingerprint"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:typeface="monospace" />
+ </TableRow>
+ </TableLayout>
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:text="@string/section_user_ids" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/user_ids"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:text="@string/section_keys" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/keys"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:text="@string/section_actions" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_encrypt"
+ android:layout_width="match_parent"
+ android:layout_height="60dp"
+ android:padding="4dp"
+ android:text="@string/key_view_action_encrypt"
+ bootstrapbutton:bb_icon_left="fa-lock"
+ bootstrapbutton:bb_type="info" />
+ </LinearLayout>
+
+</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/view_key_keys_item.xml b/OpenPGP-Keychain/res/layout/view_key_keys_item.xml
new file mode 100644
index 000000000..b50253980
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/view_key_keys_item.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:orientation="horizontal"
+ android:paddingLeft="8dip"
+ android:paddingRight="3dip"
+ android:singleLine="true" >
+
+ <ImageView
+ android:id="@+id/ic_masterKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="6dip"
+ android:src="@drawable/key_small" />
+
+ <TextView
+ android:id="@+id/keyId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="Key ID"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:typeface="monospace" />
+
+ <TextView
+ android:id="@+id/keyDetails"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="(RSA, 1024bit)"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical"
+ android:gravity="right"
+ android:orientation="horizontal"
+ android:paddingBottom="2dip"
+ android:paddingTop="2dip" >
+
+ <ImageView
+ android:id="@+id/ic_certifyKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/certify_small" />
+
+ <ImageView
+ android:id="@+id/ic_encryptKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/encrypted_small" />
+
+ <ImageView
+ android:id="@+id/ic_signKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/signed_small" />
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/layout/view_key_userids_item.xml b/OpenPGP-Keychain/res/layout/view_key_userids_item.xml
new file mode 100644
index 000000000..2d022ba13
--- /dev/null
+++ b/OpenPGP-Keychain/res/layout/view_key_userids_item.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:layout_marginRight="?android:attr/scrollbarSize"
+ android:orientation="vertical"
+ android:singleLine="true" >
+
+ <TextView
+ android:id="@+id/userId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="3dip"
+ android:text="User ID"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_edit.xml b/OpenPGP-Keychain/res/menu/key_edit.xml
new file mode 100644
index 000000000..38c52e7f0
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_edit.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_key_edit_export_file"
+ android:showAsAction="never"
+ android:title="@string/menu_export_key"/>
+ <item
+ android:id="@+id/menu_key_edit_delete"
+ android:showAsAction="never"
+ android:title="@string/menu_delete_key"/>
+ <item
+ android:id="@+id/menu_key_edit_cancel"
+ android:showAsAction="never"
+ android:title="@string/menu_key_edit_cancel"/>
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_public.xml b/OpenPGP-Keychain/res/menu/key_list_public.xml
new file mode 100644
index 000000000..7b6261558
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_public.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_key_list_public_import"
+ android:showAsAction="always|withText"
+ android:title="@string/menu_import"/>
+ <item
+ android:id="@+id/menu_key_list_public_export"
+ android:showAsAction="never"
+ android:title="@string/menu_export_keys"/>
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_public_multi.xml b/OpenPGP-Keychain/res/menu/key_list_public_multi.xml
new file mode 100644
index 000000000..92481e9cb
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_public_multi.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_key_list_public_multi_encrypt"
+ android:title="@string/menu_encrypt_to"/>
+ <item
+ android:id="@+id/menu_key_list_public_multi_delete"
+ android:icon="@android:drawable/ic_menu_delete"
+ android:title="@string/menu_delete_key"/>
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_secret.xml b/OpenPGP-Keychain/res/menu/key_list_secret.xml
new file mode 100644
index 000000000..c610eda35
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_secret.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_key_list_secret_create"
+ android:showAsAction="always|withText"
+ android:title="@string/menu_create_key"/>
+ <item
+ android:id="@+id/menu_key_list_secret_create_expert"
+ android:showAsAction="never"
+ android:title="@string/menu_create_key_expert"/>
+ <item
+ android:id="@+id/menu_key_list_secret_import"
+ android:showAsAction="never"
+ android:title="@string/menu_import"/>
+ <item
+ android:id="@+id/menu_key_list_secret_export"
+ android:showAsAction="never"
+ android:title="@string/menu_export_keys"/>
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_list_secret_multi.xml b/OpenPGP-Keychain/res/menu/key_list_secret_multi.xml
new file mode 100644
index 000000000..a10d1e2fa
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_list_secret_multi.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_key_list_public_multi_delete"
+ android:icon="@android:drawable/ic_menu_delete"
+ android:title="@string/menu_delete_key"/>
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/key_view.xml b/OpenPGP-Keychain/res/menu/key_view.xml
new file mode 100644
index 000000000..1078cd937
--- /dev/null
+++ b/OpenPGP-Keychain/res/menu/key_view.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/menu_key_view_share"
+ android:icon="@android:drawable/ic_menu_share"
+ android:showAsAction="always"
+ android:title="@string/menu_share">
+ <menu>
+ <item
+ android:id="@+id/menu_key_view_share_default"
+ android:showAsAction="never"
+ android:title="@string/menu_share"/>
+ <item
+ android:id="@+id/menu_key_view_share_qr_code"
+ android:showAsAction="never"
+ android:title="@string/menu_share_qr_code"/>
+ <item
+ android:id="@+id/menu_key_view_share_nfc"
+ android:showAsAction="never"
+ android:title="@string/menu_share_nfc"/>
+ <item
+ android:id="@+id/menu_key_view_share_clipboard"
+ android:showAsAction="never"
+ android:title="@string/menu_copy_to_clipboard"/>
+ </menu>
+ </item>
+ <item
+ android:id="@+id/menu_key_keyserver"
+ android:icon="@android:drawable/ic_menu_upload"
+ android:showAsAction="always"
+ android:title="@string/menu_share">
+ <menu>
+ <item
+ android:id="@+id/menu_key_view_update"
+ android:showAsAction="never"
+ android:title="@string/menu_update_key"/>
+ <item
+ android:id="@+id/menu_key_view_export_keyserver"
+ android:showAsAction="never"
+ android:title="@string/menu_export_key_to_server"/>
+ </menu>
+ </item>
+ <item
+ android:id="@+id/menu_key_view_sign"
+ android:showAsAction="never"
+ android:title="@string/menu_sign_key"/>
+ <item
+ android:id="@+id/menu_key_view_export_file"
+ android:showAsAction="never"
+ android:title="@string/menu_export_key"/>
+ <item
+ android:id="@+id/menu_key_view_delete"
+ android:showAsAction="never"
+ android:title="@string/menu_delete_key"/>
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/menu/nfc_beam.xml b/OpenPGP-Keychain/res/menu/nfc_beam.xml
deleted file mode 100644
index e1c088b86..000000000
--- a/OpenPGP-Keychain/res/menu/nfc_beam.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-
- <item
- android:id="@+id/menu_settings"
- android:icon="@drawable/ic_menu_settings"
- android:showAsAction="always|withText"
- android:title="@string/menu_beam_preferences"/>
-
-</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/raw/help_about.html b/OpenPGP-Keychain/res/raw/help_about.html
index 0ed0d3b05..54af42b16 100644
--- a/OpenPGP-Keychain/res/raw/help_about.html
+++ b/OpenPGP-Keychain/res/raw/help_about.html
@@ -12,13 +12,15 @@ And don't add newlines before or after p tags because of transifex -->
<h2>Developers OpenPGP Keychain</h2>
<ul>
<li>Dominik Schürmann (Lead developer)</li>
-<li>Ash Hughes</li>
+<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
+<li>Bahtiar 'kalkin' Gadimov (UI)</li>
+
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>Thialfihar (Lead developer)</li>
-<li>Senecaso (QRCode, sign key, upload key)</li>
+<li>'Thialfihar' (Lead developer)</li>
+<li>'Senecaso' (QRCode, sign key, upload key)</li>
<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
@@ -26,7 +28,9 @@ And don't add newlines before or after p tags because of transifex -->
<h2>Libraries</h2>
<ul>
<li><a href="http://actionbarsherlock.com">ActionBarSherlock</a> (Apache License v2)</li>
-<li><a href="http://code.google.com/p/zxing/">ZXing QRCode Integration</a> (Apache License v2)</li>
+<li><a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache License v2)</li>
+<li><a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (MIT License)</li>
+<li><a href="http://code.google.com/p/zxing/">ZXing</a> (Apache License v2)</li>
<li><a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
<li><a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
diff --git a/OpenPGP-Keychain/res/values-sw600dp/dimens.xml b/OpenPGP-Keychain/res/values-sw600dp/dimens.xml
new file mode 100644
index 000000000..1ba777d65
--- /dev/null
+++ b/OpenPGP-Keychain/res/values-sw600dp/dimens.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as
+ screen margins) for sw600dp devices (e.g. 7" tablets) here.
+ -->
+
+</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml b/OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 000000000..eee741a51
--- /dev/null
+++ b/OpenPGP-Keychain/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,9 @@
+<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as
+ screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+ -->
+ <dimen name="activity_horizontal_margin">128dp</dimen>
+
+</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/colors.xml b/OpenPGP-Keychain/res/values/colors.xml
index d1dc6c1f4..780137181 100644
--- a/OpenPGP-Keychain/res/values/colors.xml
+++ b/OpenPGP-Keychain/res/values/colors.xml
@@ -3,5 +3,5 @@
<color name="emphasis">#31b6e7</color>
<color name="bg_gray">#cecbce</color>
-
+
</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/dimens.xml b/OpenPGP-Keychain/res/values/dimens.xml
new file mode 100644
index 000000000..a6dd14032
--- /dev/null
+++ b/OpenPGP-Keychain/res/values/dimens.xml
@@ -0,0 +1,7 @@
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/strings.xml b/OpenPGP-Keychain/res/values/strings.xml
index 807daaeb4..87f7c5a8f 100644
--- a/OpenPGP-Keychain/res/values/strings.xml
+++ b/OpenPGP-Keychain/res/values/strings.xml
@@ -19,8 +19,8 @@
<resources>
<!-- title -->
- <string name="title_manage_public_keys">Manage Public Keys</string>
- <string name="title_manage_secret_keys">Manage Secret Keys</string>
+ <string name="title_manage_public_keys">Contacts</string>
+ <string name="title_manage_secret_keys">Secret Keys</string>
<string name="title_select_recipients">Select Public Key</string>
<string name="title_select_secret_key">Select Secret Key</string>
<string name="title_encrypt">Encrypt</string>
@@ -44,6 +44,7 @@
<string name="title_send_key">Export to Key Server</string>
<string name="title_unknown_signature_key">Unknown Signature Key</string>
<string name="title_sign_key">Sign Key</string>
+ <string name="title_key_details">Key Details</string>
<string name="title_help">Help</string>
<string name="title_share_by_nfc">Share key with NFC</string>
@@ -53,6 +54,9 @@
<string name="section_general">General</string>
<string name="section_defaults">Defaults</string>
<string name="section_advanced">Advanced</string>
+ <string name="section_master_key">Master Key</string>
+ <string name="section_master_user_id">Master User ID</string>
+ <string name="section_actions">Actions</string>
<!-- button -->
<string name="btn_sign_to_clipboard">Sign (Clipboard)</string>
@@ -78,9 +82,11 @@
<!-- menu -->
<string name="menu_preferences">Settings</string>
+ <string name="menu_help">Help</string>
<string name="menu_api_app_settings">Registered Apps</string>
<string name="menu_import_from_file">Import from file</string>
<string name="menu_import_from_qr_code">Import from QR Code</string>
+ <string name="menu_import">Import</string>
<string name="menu_import_from_nfc">Import from NFC</string>
<string name="menu_export_keys">Export all keys</string>
<string name="menu_export_key">Export to file</string>
@@ -95,8 +101,11 @@
<string name="menu_share">Share with…</string>
<string name="menu_share_qr_code">Share with QR Code</string>
<string name="menu_share_nfc">Share with NFC</string>
+ <string name="menu_copy_to_clipboard">Copy to clipboard</string>
<string name="menu_sign_key">Sign key</string>
<string name="menu_beam_preferences">Beam settings</string>
+ <string name="menu_key_edit_cancel">Cancel</string>
+ <string name="menu_encrypt_to">Encrypt to…</string>
<!-- label -->
<string name="label_sign">Sign</string>
@@ -129,6 +138,7 @@
<string name="label_comment">Comment</string>
<string name="label_email">Email</string>
<string name="label_send_key">Send Key to Server?</string>
+ <string name="label_fingerprint">Fingerprint</string>
<string name="no_keys_selected">Select</string>
<string name="one_key_selected">1 Selected</string>
<string name="n_keys_selected">Selected</string>
@@ -142,6 +152,8 @@
<string name="n_key_servers">%s key server(s)</string>
<string name="fingerprint">Fingerprint:</string>
<string name="secret_key">Secret Key:</string>
+ <string name="notValid">not valid</string>
+ <string name="secretKeyring">Secret Keyring</string>
<!-- choice -->
<string name="choice_none">None</string>
@@ -190,6 +202,7 @@
<string name="specify_file_to_export_to">Please specify which file to export to.\nWARNING! File will be overwritten if it exists.</string>
<string name="specify_file_to_export_secret_keys_to">Please specify which file to export to.\nWARNING! You are about to export SECRET keys.\nWARNING! File will be overwritten if it exists.</string>
<string name="key_deletion_confirmation">Do you really want to delete the key \'%s\'?\nYou can\'t undo this!</string>
+ <string name="key_deletion_confirmation_multi">Do you really want to delete all selected keys?\nYou can\'t undo this!</string>
<string name="secret_key_deletion_confirmation">Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!</string>
<string name="keys_added_and_updated">Successfully added %1$s key(s) and updated %2$s key(s).</string>
<string name="keys_added">Successfully added %s key(s).</string>
@@ -206,7 +219,6 @@
<string name="lookup_unknown_key">Unknown key %s, do you want to try finding it on a keyserver?</string>
<string name="key_send_success">Successfully sent key to server</string>
<string name="key_sign_success">Successfully signed key</string>
- <string name="list_information">Long press one entry in this list to show more options!</string>
<string name="list_empty">This list is empty!</string>
<string name="nfc_successfull">Successfully sent key with NFC Beam!</string>
@@ -292,7 +304,7 @@
<string name="compression_very_slow">very slow</string>
<!-- Dashboard -->
- <string name="dashboard_manage_keys">Manage Public Keys</string>
+ <string name="dashboard_manage_keys">Contacts</string>
<string name="dashboard_my_keys">My Secret Keys</string>
<string name="dashboard_encrypt">Encrypt</string>
<string name="dashboard_decrypt">Decrypt</string>
@@ -322,8 +334,8 @@
<!-- Remote API -->
<string name="api_no_apps">No registered applications!</string>
- <string name="api_settings_show_advanced">Show advanced settings…</string>
- <string name="api_settings_hide_advanced">Hide advanced settings…</string>
+ <string name="api_settings_show_advanced">Show advanced settings</string>
+ <string name="api_settings_hide_advanced">Hide advanced settings</string>
<string name="api_settings_no_key">No key selected</string>
<string name="api_settings_select_key">Select key</string>
<string name="api_settings_save">Save</string>
@@ -343,5 +355,31 @@
<!-- Share -->
<string name="share_qr_code_dialog_start">Go through all QR Codes using \'Next\', and scan them one by one.</string>
<string name="share_qr_code_dialog_progress">QR Code %1$d of %2$d</string>
+ <string name="share_nfc_dialog">Share with NFC</string>
+
+ <!-- Key list -->
+ <plurals name="key_list_selected_keys">
+ <item quantity="one">1 key selected.</item>
+ <item quantity="other">%d keys selected.</item>
+ </plurals>
+
+ <string name="key_list_empty_text1">No keys available yet…</string>
+ <string name="key_list_empty_text2">You can start by</string>
+ <string name="key_list_empty_text3">or</string>
+ <string name="key_list_empty_button_create">creating your own key pair</string>
+ <string name="key_list_empty_button_import">importing keys.</string>
+
+ <!-- Key view -->
+ <string name="key_view_action_encrypt">Encrypt to this contact</string>
+
+ <!-- Navigation Drawer -->
+ <string name="nav_contacts">Contacts</string>
+ <string name="nav_encrypt">Encrypt</string>
+ <string name="nav_decrypt">Decrypt</string>
+ <string name="nav_import">Import Keys</string>
+ <string name="nav_secret_keys">My Keys</string>
+ <string name="nav_apps">Registered Apps</string>
+ <string name="drawer_open">Open navigation drawer</string>
+ <string name="drawer_close">Close navigation drawer</string>
</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain/res/values/styles.xml b/OpenPGP-Keychain/res/values/styles.xml
index ef3ff63f6..15214fa62 100644
--- a/OpenPGP-Keychain/res/values/styles.xml
+++ b/OpenPGP-Keychain/res/values/styles.xml
@@ -18,16 +18,6 @@
<resources>
- <style name="MinusButton">
- <item name="android:background">@drawable/btn_circle</item>
- <item name="android:src">@drawable/ic_btn_round_minus</item>
- </style>
-
- <style name="PlusButton">
- <item name="android:background">@drawable/btn_circle</item>
- <item name="android:src">@drawable/ic_btn_round_plus</item>
- </style>
-
<style name="DashboardButton">
<item name="android:layout_gravity">center_vertical</item>
<item name="android:layout_width">wrap_content</item>
@@ -44,7 +34,7 @@
<item name="android:drawablePadding">4dp</item>
<item name="android:layout_marginTop">8dp</item>
<item name="android:paddingLeft">4dp</item>
- <item name="android:textAllCaps">true</item>
+<!-- <item name="android:textAllCaps">true</item> -->
<item name="android:textColor">@color/emphasis</item>
<item name="android:textSize">14sp</item>
</style>
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
index e9b0b67d4..fb7851774 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/Id.java
@@ -27,18 +27,7 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags;
*/
public final class Id {
- public static final String TAG = "APG";
-
public static final class menu {
- public static final int export = 0x21070001;
- public static final int delete = 0x21070002;
- public static final int edit = 0x21070003;
- public static final int update = 0x21070004;
- public static final int exportToServer = 0x21070005;
- public static final int share = 0x21070006;
- public static final int 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;
@@ -82,20 +71,6 @@ public final class Id {
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;
@@ -109,18 +84,6 @@ public final class Id {
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;
@@ -136,7 +99,6 @@ public final class Id {
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;
@@ -152,11 +114,6 @@ public final class Id {
public static final int export_keys = 0x21070002;
}
- // public static final class database {
- // public static final int type_public = 0;
- // public static final int type_secret = 1;
- // }
-
public static final class type {
public static final int public_key = 0x21070001;
public static final int secret_key = 0x21070002;
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java
new file mode 100644
index 000000000..261e26be6
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+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 android.app.Activity;
+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.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+
+public class ExportHelper {
+ protected FileDialogFragment mFileDialog;
+ protected String mExportFilename;
+
+ SherlockFragmentActivity activity;
+
+ public ExportHelper(SherlockFragmentActivity activity) {
+ super();
+ this.activity = activity;
+ }
+
+ public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(deleteHandler);
+
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
+ new long[] { keyRingRowId }, keyType);
+
+ deleteKeyDialog.show(activity.getSupportFragmentManager(), "deleteKeyDialog");
+ }
+
+ /**
+ * Show dialog where to export keys
+ *
+ * @param keyRingMasterKeyId
+ * if -1 export all keys
+ */
+ public void showExportKeysDialog(final Uri dataUri, final int keyType,
+ final String exportFilename) {
+ mExportFilename = exportFilename;
+
+ // 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);
+
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ // TODO?
+ long keyRingMasterKeyId = ProviderHelper.getSecretMasterKeyId(activity,
+ keyRingRowId);
+
+ exportKeys(keyRingMasterKeyId, keyType);
+ }
+ }
+ };
+
+ // 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 (dataUri != null) {
+ // single key export
+ title = activity.getString(R.string.title_export_key);
+ } else {
+ title = activity.getString(R.string.title_export_keys);
+ }
+
+ String message = null;
+ if (keyType == Id.type.public_key) {
+ message = activity.getString(R.string.specify_file_to_export_to);
+ } else {
+ message = activity.getString(R.string.specify_file_to_export_secret_keys_to);
+ }
+
+ mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
+ exportFilename, null, Id.request.filename);
+
+ mFileDialog.show(activity.getSupportFragmentManager(), "fileDialog");
+ }
+ });
+ }
+
+ /**
+ * Export keys
+ *
+ * @param keyRingMasterKeyId
+ * if -1 export all keys
+ */
+ public void exportKeys(long keyRingMasterKeyId, int keyType) {
+ Log.d(Constants.TAG, "exportKeys started");
+
+ // Send all information needed to service to export key in other thread
+ Intent intent = new Intent(activity, KeychainIntentService.class);
+
+ intent.setAction(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, keyType);
+
+ 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(activity,
+ 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 = activity.getString(R.string.key_exported);
+ } else if (exported > 0) {
+ toastMessage = activity.getString(R.string.keys_exported, exported);
+ } else {
+ toastMessage = activity.getString(R.string.no_keys_exported);
+ }
+ Toast.makeText(activity, 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(activity);
+
+ // start service with intent
+ activity.startService(intent);
+ }
+
+ public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == Id.request.filename) {
+ if (resultCode == Activity.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 true;
+ }
+
+ return false;
+ }
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
index 8fa2df1f5..9f3cd8e88 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/helper/OtherHelper.java
@@ -79,23 +79,4 @@ public class OtherHelper {
}
}
- /**
- * 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/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index e2d89bfab..edb30496a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -22,6 +22,8 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -40,6 +42,17 @@ import android.content.Context;
public class PgpKeyHelper {
+ /**
+ * Returns the last 9 chars of a fingerprint
+ *
+ * @param fingerprint
+ * String containing short or long fingerprint
+ * @return
+ */
+ public static String shortifyFingerprint(String fingerprint) {
+ return fingerprint.substring(41);
+ }
+
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
@@ -513,4 +526,32 @@ public class PgpKeyHelper {
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
}
+ /**
+ * Splits userId string into naming part, email part, and comment part
+ *
+ * @param userId
+ * @return array with naming (0), email (1), comment (2)
+ */
+ public static String[] splitUserId(String userId) {
+ String[] result = new String[] { "", "", "" };
+
+ Pattern withComment = Pattern.compile("^(.*) \\((.*)\\) <(.*)>$");
+ Matcher matcher = withComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(3);
+ result[2] = matcher.group(2);
+ return result;
+ }
+
+ Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
+ matcher = withoutComment.matcher(userId);
+ if (matcher.matches()) {
+ result[0] = matcher.group(1);
+ result[1] = matcher.group(2);
+ return result;
+ }
+ return result;
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 82bb473f6..d2381f6f0 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -69,8 +69,8 @@ public class KeychainContract {
public static final String CONTENT_AUTHORITY = Constants.PACKAGE_NAME + ".provider";
- private static final Uri BASE_CONTENT_URI_INTERNAL = Uri.parse("content://"
- + CONTENT_AUTHORITY);
+ private static final Uri BASE_CONTENT_URI_INTERNAL = Uri
+ .parse("content://" + CONTENT_AUTHORITY);
public static final String BASE_KEY_RINGS = "key_rings";
public static final String BASE_DATA = "data";
@@ -185,6 +185,14 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
.appendPath(PATH_KEYS).appendPath(keyRowId).build();
}
+
+ public static Uri buildKeysUri(Uri keyRingUri) {
+ return keyRingUri.buildUpon().appendPath(PATH_KEYS).build();
+ }
+
+ public static Uri buildKeysUri(Uri keyRingUri, String keyRowId) {
+ return keyRingUri.buildUpon().appendPath(PATH_KEYS).appendPath(keyRowId).build();
+ }
}
public static class UserIds implements UserIdsColumns, BaseColumns {
@@ -216,6 +224,14 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
.appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
}
+
+ public static Uri buildUserIdsUri(Uri keyRingUri) {
+ return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).build();
+ }
+
+ public static Uri buildUserIdsUri(Uri keyRingUri, String userIdRowId) {
+ return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
+ }
}
public static class ApiApps implements ApiAppsColumns, BaseColumns {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index f12048277..1683c7c0e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -60,7 +60,7 @@ public class ProviderHelper {
* @param queryUri
* @return
*/
- private static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
+ public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri,
new String[] { KeyRings._ID, KeyRings.KEY_RING_DATA }, null, null, null);
@@ -493,7 +493,7 @@ public class ProviderHelper {
*/
public static long getPublicMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri, keyRingRowId);
+ return getMasterKeyId(context, queryUri);
}
/**
@@ -551,7 +551,7 @@ public class ProviderHelper {
*/
public static long getSecretMasterKeyId(Context context, long keyRingRowId) {
Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri, keyRingRowId);
+ return getMasterKeyId(context, queryUri);
}
/**
@@ -562,7 +562,7 @@ public class ProviderHelper {
* @param keyRingRowId
* @return
*/
- private static long getMasterKeyId(Context context, Uri queryUri, long keyRingRowId) {
+ public static long getMasterKeyId(Context context, Uri queryUri) {
String[] projection = new String[] { KeyRings.MASTER_KEY_ID };
ContentResolver cr = context.getContentResolver();
@@ -592,7 +592,7 @@ public class ProviderHelper {
return getKeyRingsAsArmoredString(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds);
}
- private static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
+ public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
long[] masterKeyIds) {
ArrayList<String> output = new ArrayList<String>();
@@ -661,7 +661,7 @@ public class ProviderHelper {
return getKeyRingsAsByteArray(context, KeyRings.buildSecretKeyRingsUri(), masterKeyIds);
}
- private static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
+ public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
if (masterKeyIds != null && masterKeyIds.length > 0) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
index e592f5d57..bb6e427a4 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
@@ -49,12 +49,13 @@ import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
public class AppSettingsFragment extends Fragment {
// model
@@ -62,12 +63,12 @@ public class AppSettingsFragment extends Fragment {
// view
private LinearLayout mAdvancedSettingsContainer;
- private Button mAdvancedSettingsButton;
+ private BootstrapButton mAdvancedSettingsButton;
private TextView mAppNameView;
private ImageView mAppIconView;
private TextView mKeyUserId;
private TextView mKeyUserIdRest;
- private Button mSelectKeyButton;
+ private BootstrapButton mSelectKeyButton;
private Spinner mEncryptionAlgorithm;
private Spinner mHashAlgorithm;
private Spinner mCompression;
@@ -116,7 +117,8 @@ public class AppSettingsFragment extends Fragment {
}
private void initView(View view) {
- mAdvancedSettingsButton = (Button) view.findViewById(R.id.api_app_settings_advanced_button);
+ mAdvancedSettingsButton = (BootstrapButton) view
+ .findViewById(R.id.api_app_settings_advanced_button);
mAdvancedSettingsContainer = (LinearLayout) view
.findViewById(R.id.api_app_settings_advanced);
@@ -124,7 +126,8 @@ public class AppSettingsFragment extends Fragment {
mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
mKeyUserId = (TextView) view.findViewById(R.id.api_app_settings_user_id);
mKeyUserIdRest = (TextView) view.findViewById(R.id.api_app_settings_user_id_rest);
- mSelectKeyButton = (Button) view.findViewById(R.id.api_app_settings_select_key_button);
+ mSelectKeyButton = (BootstrapButton) view
+ .findViewById(R.id.api_app_settings_select_key_button);
mEncryptionAlgorithm = (Spinner) view
.findViewById(R.id.api_app_settings_encryption_algorithm);
mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
@@ -204,11 +207,13 @@ public class AppSettingsFragment extends Fragment {
if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
mAdvancedSettingsContainer.setVisibility(View.GONE);
- mAdvancedSettingsButton.setText(R.string.api_settings_show_advanced);
+ mAdvancedSettingsButton.setText(getString(R.string.api_settings_show_advanced));
+ mAdvancedSettingsButton.setLeftIcon("fa-caret-up");
} else {
mAdvancedSettingsContainer.startAnimation(visibleAnimation);
mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
- mAdvancedSettingsButton.setText(R.string.api_settings_hide_advanced);
+ mAdvancedSettingsButton.setText(getString(R.string.api_settings_hide_advanced));
+ mAdvancedSettingsButton.setLeftIcon("fa-caret-down");
}
}
});
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
index 4530ac2fc..3c553fff5 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
@@ -18,44 +18,19 @@
package org.sufficientlysecure.keychain.service.remote;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.MainActivity;
+import org.sufficientlysecure.keychain.ui.DrawerActivity;
-import com.actionbarsherlock.app.ActionBar;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.MenuItem;
-
-import android.content.Intent;
import android.os.Bundle;
-public class RegisteredAppsListActivity extends SherlockFragmentActivity {
- private ActionBar mActionBar;
+public class RegisteredAppsListActivity extends DrawerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mActionBar = getSupportActionBar();
-
setContentView(R.layout.api_apps_list_activity);
- mActionBar.setDisplayShowTitleEnabled(true);
- mActionBar.setDisplayHomeAsUpEnabled(true);
+ setupDrawerNavigation(savedInstanceState);
}
- /**
- * 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/service/remote/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
index 1b504a374..4c9d553ad 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
@@ -41,9 +41,6 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements
// This is the Adapter being used to display the list's data.
RegisteredAppsAdapter mAdapter;
- // If non-null, this is the current filter the user has provided.
- String mCurFilter;
-
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
@@ -75,8 +72,7 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements
}
// These are the Contacts rows that we will retrieve.
- static final String[] CONSUMERS_SUMMARY_PROJECTION = new String[] { ApiApps._ID,
- ApiApps.PACKAGE_NAME };
+ static final String[] PROJECTION = new String[] { ApiApps._ID, ApiApps.PACKAGE_NAME };
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@@ -87,7 +83,7 @@ public class RegisteredAppsListFragment extends SherlockListFragment implements
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, CONSUMERS_SUMMARY_PROJECTION, null, null,
+ return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null,
ApiApps.PACKAGE_NAME + " COLLATE LOCALIZED ASC");
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 78ad4c9be..6cc0b3b5a 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -60,19 +60,18 @@ 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 com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
@SuppressLint("NewApi")
-public class DecryptActivity extends SherlockFragmentActivity {
+public class DecryptActivity extends DrawerActivity {
/* Intents */
// without permission
@@ -107,7 +106,7 @@ public class DecryptActivity extends SherlockFragmentActivity {
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
- private ImageButton mBrowse = null;
+ private BootstrapButton mBrowse = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@@ -144,13 +143,6 @@ public class DecryptActivity extends SherlockFragmentActivity {
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();
@@ -216,7 +208,7 @@ public class DecryptActivity extends SherlockFragmentActivity {
mMessage.setMinimumHeight(height);
mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (ImageButton) findViewById(R.id.btn_browse);
+ mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*",
@@ -238,13 +230,15 @@ public class DecryptActivity extends SherlockFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.decrypt);
+ setContentView(R.layout.decrypt_activity);
// set actionbar without home button if called from another app
ActionBarHelper.setBackButton(this);
initView();
+ setupDrawerNavigation(savedInstanceState);
+
// Handle intent actions
handleActions(getIntent());
@@ -262,7 +256,8 @@ public class DecryptActivity extends SherlockFragmentActivity {
if (matcher.matches()) {
data = matcher.group(1);
mMessage.setText(data);
- Toast.makeText(this, R.string.using_clipboard_content, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.using_clipboard_content, Toast.LENGTH_SHORT)
+ .show();
}
}
}
@@ -472,8 +467,9 @@ public class DecryptActivity extends SherlockFragmentActivity {
if (!file.exists() || !file.isFile()) {
Toast.makeText(
this,
- getString(R.string.error_message, getString(R.string.error_file_not_found)),
- Toast.LENGTH_SHORT).show();
+ getString(R.string.error_message,
+ getString(R.string.error_file_not_found)), Toast.LENGTH_SHORT)
+ .show();
return;
}
}
@@ -592,7 +588,8 @@ public class DecryptActivity extends SherlockFragmentActivity {
}
mSecretKeyId = Id.key.symmetric;
if (!PgpOperation.hasSymmetricEncryption(this, inStream)) {
- throw new PgpGeneralException(getString(R.string.error_no_known_encryption_found));
+ throw new PgpGeneralException(
+ getString(R.string.error_no_known_encryption_found));
}
mAssumeSymmetricEncryption = true;
}
@@ -790,8 +787,8 @@ public class DecryptActivity extends SherlockFragmentActivity {
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
Toast.makeText(DecryptActivity.this,
- R.string.unknown_signature_key_touch_to_look_up, Toast.LENGTH_LONG)
- .show();
+ R.string.unknown_signature_key_touch_to_look_up,
+ Toast.LENGTH_LONG).show();
} else {
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
new file mode 100644
index 000000000..ee8a01432
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.ActionBarDrawerToggle;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.view.ActionProvider;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.SubMenu;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.FontAwesomeText;
+
+/**
+ * some fundamental ideas from https://github.com/tobykurien/SherlockNavigationDrawer
+ *
+ *
+ */
+public class DrawerActivity extends SherlockFragmentActivity {
+ private DrawerLayout mDrawerLayout;
+ private ListView mDrawerList;
+ private ActionBarDrawerToggle mDrawerToggle;
+
+ private CharSequence mDrawerTitle;
+ private CharSequence mTitle;
+
+ private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class,
+ EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
+ KeyListSecretActivity.class, RegisteredAppsListActivity.class };
+
+ private static final int MENU_ID_PREFERENCE = 222;
+ private static final int MENU_ID_HELP = 223;
+
+ protected void setupDrawerNavigation(Bundle savedInstanceState) {
+ mDrawerTitle = getString(R.string.app_name);
+ mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ mDrawerList = (ListView) findViewById(R.id.left_drawer);
+
+ // set a custom shadow that overlays the main content when the drawer
+ // opens
+ mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+
+ NavItem mItemIconTexts[] = new NavItem[] {
+ new NavItem("fa-user", getString(R.string.nav_contacts)),
+ new NavItem("fa-lock", getString(R.string.nav_encrypt)),
+ new NavItem("fa-unlock", getString(R.string.nav_decrypt)),
+ new NavItem("fa-download", getString(R.string.nav_import)),
+ new NavItem("fa-key", getString(R.string.nav_secret_keys)),
+ new NavItem("fa-android", getString(R.string.nav_apps)) };
+
+ mDrawerList.setAdapter(new NavigationDrawerAdapter(this, R.layout.drawer_list_item,
+ mItemIconTexts));
+
+ mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
+
+ // enable ActionBar app icon to behave as action to toggle nav drawer
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ // ActionBarDrawerToggle ties together the the proper interactions
+ // between the sliding drawer and the action bar app icon
+ mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
+ mDrawerLayout, /* DrawerLayout object */
+ R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
+ R.string.drawer_open, /* "open drawer" description for accessibility */
+ R.string.drawer_close /* "close drawer" description for accessibility */
+ ) {
+ public void onDrawerClosed(View view) {
+ getSupportActionBar().setTitle(mTitle);
+ // creates call to onPrepareOptionsMenu()
+ supportInvalidateOptionsMenu();
+ }
+
+ public void onDrawerOpened(View drawerView) {
+ mTitle = getSupportActionBar().getTitle();
+ getSupportActionBar().setTitle(mDrawerTitle);
+ // creates call to onPrepareOptionsMenu()
+ supportInvalidateOptionsMenu();
+ }
+ };
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+
+ // if (savedInstanceState == null) {
+ // selectItem(0);
+ // }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
+ menu.add(42, MENU_ID_HELP, 101, R.string.menu_help);
+
+ return super.onCreateOptionsMenu(menu);
+ }
+
+ /* Called whenever we call invalidateOptionsMenu() */
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ // If the nav drawer is open, hide action items related to the content
+ // view
+ boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
+ // menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
+ return super.onPrepareOptionsMenu(menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(final MenuItem item) {
+ // The action bar home/up action should open or close the drawer.
+ // ActionBarDrawerToggle will take care of this.
+ if (mDrawerToggle.onOptionsItemSelected(getMenuItem(item))) {
+ return true;
+ }
+
+ switch (item.getItemId()) {
+ case MENU_ID_PREFERENCE: {
+ Intent intent = new Intent(this, PreferencesActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ case MENU_ID_HELP: {
+ Intent intent = new Intent(this, HelpActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+
+ // Handle action buttons
+ // switch (item.getItemId()) {
+ // case R.id.action_websearch:
+ // // create intent to perform web search for this planet
+ // Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ // intent.putExtra(SearchManager.QUERY, getSupportActionBar().getTitle());
+ // // catch event that there's no activity to handle intent
+ // if (intent.resolveActivity(getPackageManager()) != null) {
+ // startActivity(intent);
+ // } else {
+ // Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
+ // }
+ // return true;
+ // default:
+ // return super.onOptionsItemSelected(item);
+ // }
+ }
+
+ private android.view.MenuItem getMenuItem(final MenuItem item) {
+ return new android.view.MenuItem() {
+ @Override
+ public int getItemId() {
+ return item.getItemId();
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public boolean collapseActionView() {
+ return false;
+ }
+
+ @Override
+ public boolean expandActionView() {
+ return false;
+ }
+
+ @Override
+ public ActionProvider getActionProvider() {
+ return null;
+ }
+
+ @Override
+ public View getActionView() {
+ return null;
+ }
+
+ @Override
+ public char getAlphabeticShortcut() {
+ return 0;
+ }
+
+ @Override
+ public int getGroupId() {
+ return 0;
+ }
+
+ @Override
+ public Drawable getIcon() {
+ return null;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return null;
+ }
+
+ @Override
+ public ContextMenuInfo getMenuInfo() {
+ return null;
+ }
+
+ @Override
+ public char getNumericShortcut() {
+ return 0;
+ }
+
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+
+ @Override
+ public SubMenu getSubMenu() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTitleCondensed() {
+ return null;
+ }
+
+ @Override
+ public boolean hasSubMenu() {
+ return false;
+ }
+
+ @Override
+ public boolean isActionViewExpanded() {
+ return false;
+ }
+
+ @Override
+ public boolean isCheckable() {
+ return false;
+ }
+
+ @Override
+ public boolean isChecked() {
+ return false;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return false;
+ }
+
+ @Override
+ public android.view.MenuItem setActionProvider(ActionProvider actionProvider) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setActionView(View view) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setActionView(int resId) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setAlphabeticShortcut(char alphaChar) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setCheckable(boolean checkable) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setChecked(boolean checked) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setEnabled(boolean enabled) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setIcon(Drawable icon) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setIcon(int iconRes) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setIntent(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setNumericShortcut(char numericChar) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setOnMenuItemClickListener(
+ OnMenuItemClickListener menuItemClickListener) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setShortcut(char numericChar, char alphaChar) {
+ return null;
+ }
+
+ @Override
+ public void setShowAsAction(int actionEnum) {
+ }
+
+ @Override
+ public android.view.MenuItem setShowAsActionFlags(int actionEnum) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setTitle(CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setTitle(int title) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setTitleCondensed(CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public android.view.MenuItem setVisible(boolean visible) {
+ return null;
+ }
+ };
+ }
+
+ /* The click listener for ListView in the navigation drawer */
+ private class DrawerItemClickListener implements ListView.OnItemClickListener {
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ selectItem(position);
+ }
+ }
+
+ private void selectItem(int position) {
+ // update selected item and title, then close the drawer
+ mDrawerList.setItemChecked(position, true);
+ // setTitle(mDrawerTitles[position]);
+ mDrawerLayout.closeDrawer(mDrawerList);
+
+ finish();
+ overridePendingTransition(0, 0);
+
+ Intent intent = new Intent(this, mItemsClass[position]);
+ startActivity(intent);
+ // disable animation of activity start
+ overridePendingTransition(0, 0);
+ }
+
+ /**
+ * When using the ActionBarDrawerToggle, you must call it during onPostCreate() and
+ * onConfigurationChanged()...
+ */
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ // Sync the toggle state after onRestoreInstanceState has occurred.
+ mDrawerToggle.syncState();
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ // Pass any configuration change to the drawer toggles
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ private class NavItem {
+ public String icon;
+ public String title;
+
+ public NavItem(String icon, String title) {
+ super();
+ this.icon = icon;
+ this.title = title;
+ }
+ }
+
+ private class NavigationDrawerAdapter extends ArrayAdapter<NavItem> {
+ Context context;
+ int layoutResourceId;
+ NavItem data[] = null;
+
+ public NavigationDrawerAdapter(Context context, int layoutResourceId, NavItem[] data) {
+ super(context, layoutResourceId, data);
+ this.layoutResourceId = layoutResourceId;
+ this.context = context;
+ this.data = data;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+ NavItemHolder holder = null;
+
+ if (row == null) {
+ LayoutInflater inflater = ((Activity) context).getLayoutInflater();
+ row = inflater.inflate(layoutResourceId, parent, false);
+
+ holder = new NavItemHolder();
+ holder.img = (FontAwesomeText) row.findViewById(R.id.drawer_item_icon);
+ holder.txtTitle = (TextView) row.findViewById(R.id.drawer_item_text);
+
+ row.setTag(holder);
+ } else {
+ holder = (NavItemHolder) row.getTag();
+ }
+
+ NavItem item = data[position];
+ holder.txtTitle.setText(item.title);
+ holder.img.setIcon(item.icon);
+
+ return row;
+ }
+
+ }
+
+ static class NavItemHolder {
+ FontAwesomeText img;
+ TextView txtTitle;
+ }
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index 7abee78f3..be2e4115b 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,6 +27,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
@@ -34,6 +35,7 @@ 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.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
@@ -45,6 +47,7 @@ import org.sufficientlysecure.keychain.util.Log;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -53,7 +56,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
@@ -61,6 +63,9 @@ import android.widget.LinearLayout;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class EditKeyActivity extends SherlockFragmentActivity {
@@ -72,13 +77,14 @@ public class EditKeyActivity extends SherlockFragmentActivity {
public static final String EXTRA_USER_IDS = "user_ids";
public static final String EXTRA_NO_PASSPHRASE = "no_passphrase";
public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generate_default_keys";
- public static final String EXTRA_MASTER_KEY_ID = "master_key_id";
- public static final String EXTRA_MASTER_CAN_SIGN = "master_can_sign";
// results when saving key
public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id";
public static final String RESULT_EXTRA_USER_ID = "user_id";
+ // EDIT
+ private Uri mDataUri;
+
private PGPSecretKeyRing mKeyRing = null;
private SectionView mUserIdsView;
@@ -87,7 +93,7 @@ public class EditKeyActivity extends SherlockFragmentActivity {
private String mCurrentPassPhrase = null;
private String mNewPassPhrase = null;
- private Button mChangePassPhrase;
+ private BootstrapButton mChangePassPhrase;
private CheckBox mNoPassphrase;
@@ -96,34 +102,13 @@ public class EditKeyActivity extends SherlockFragmentActivity {
Vector<Integer> mKeysUsages;
boolean masterCanSign = true;
- // will be set to false to build layout later in handler
- private boolean mBuildLayout = true;
+ ExportHelper mExportHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // Inflate a "Done"/"Cancel" custom action bar
- ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // save
- saveClicked();
- }
- }, R.string.btn_do_not_save, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // cancel
- cancelClicked();
- }
- });
-
- setContentView(R.layout.edit_key);
-
- // find views
- mChangePassPhrase = (Button) findViewById(R.id.edit_key_btn_change_pass_phrase);
- mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
+ mExportHelper = new ExportHelper(this);
mUserIds = new Vector<String>();
mKeys = new Vector<PGPSecretKey>();
@@ -137,32 +122,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
} 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();
- }
}
/**
@@ -171,6 +130,20 @@ public class EditKeyActivity extends SherlockFragmentActivity {
* @param intent
*/
private void handleActionCreateKey(Intent intent) {
+ // Inflate a "Done"/"Cancel" custom action bar
+ ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ saveClicked();
+ }
+ }, R.string.btn_do_not_save, new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ cancelClicked();
+ }
+ });
+
Bundle extras = intent.getExtras();
mCurrentPassPhrase = "";
@@ -197,9 +170,6 @@ public class EditKeyActivity extends SherlockFragmentActivity {
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.setAction(KeychainIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS);
@@ -256,12 +226,55 @@ public class EditKeyActivity extends SherlockFragmentActivity {
startService(serviceIntent);
}
}
+ } else {
+ buildLayout();
+ }
+ }
+
+ /**
+ * Handle intent action to edit existing key
+ *
+ * @param intent
+ */
+ private void handleActionEditKey(Intent intent) {
+ // Inflate a "Done"/"Cancel" custom action bar
+ ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_save,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ saveClicked();
+ }
+ });
+
+ mDataUri = intent.getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mDataUri);
+
+ long keyRingRowId = Long.valueOf(mDataUri.getLastPathSegment());
+
+ // get master key id using row id
+ long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId);
+
+ boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(this, keyRingRowId);
+
+ String passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
+ if (passphrase == null) {
+ showPassphraseDialog(masterKeyId, masterCanSign);
+ } else {
+ // PgpMain.setEditPassPhrase(passPhrase);
+ mCurrentPassPhrase = passphrase;
+
+ finallyEdit(masterKeyId, masterCanSign);
+ }
}
}
private void showPassphraseDialog(final long masterKeyId, final boolean masterCanSign) {
// Message is received after passphrase is cached
- final boolean mCanSign = masterCanSign;
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
@@ -291,51 +304,54 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
}
- /**
- * Handle intent action to edit existing key
- *
- * @param intent
- */
- @SuppressWarnings("unchecked")
- private void handleActionEditKey(Intent intent) {
- Bundle extras = intent.getExtras();
-
- if (extras != null) {
- if (extras.containsKey(EXTRA_MASTER_CAN_SIGN)) {
- masterCanSign = extras.getBoolean(EXTRA_MASTER_CAN_SIGN);
- }
- if (extras.containsKey(EXTRA_MASTER_KEY_ID)) {
- long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID);
-
- // build layout in edit()
- mBuildLayout = false;
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ // show menu only on edit
+ if (mDataUri != null) {
+ return super.onPrepareOptionsMenu(menu);
+ } else {
+ return false;
+ }
+ }
- String passPhrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
- if (passPhrase == null) {
- showPassphraseDialog(masterKeyId, masterCanSign);
- } else {
- // PgpMain.setEditPassPhrase(passPhrase);
- mCurrentPassPhrase = passPhrase;
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getSupportMenuInflater().inflate(R.menu.key_edit, menu);
+ return true;
+ }
- finallyEdit(masterKeyId, masterCanSign);
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_key_edit_cancel:
+ cancelClicked();
+ return true;
+ case R.id.menu_key_edit_export_file:
+ mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR
+ + "/secexport.asc");
+ return true;
+ case R.id.menu_key_edit_delete: {
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
}
+ };
- }
+ mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler);
+ return true;
}
+ }
+ return super.onOptionsItemSelected(item);
}
+ @SuppressWarnings("unchecked")
private void finallyEdit(final long masterKeyId, final boolean masterCanSign) {
- // TODO: ???
- if (mCurrentPassPhrase == null) {
- mCurrentPassPhrase = "";
- }
-
- if (mCurrentPassPhrase.equals("")) {
- // check "no passphrase" checkbox and remove button
- mNoPassphrase.setChecked(true);
- mChangePassPhrase.setVisibility(View.GONE);
- }
-
if (masterKeyId != 0) {
PGPSecretKey masterKey = null;
mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId);
@@ -357,7 +373,18 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
}
+ // TODO: ???
+ if (mCurrentPassPhrase == null) {
+ mCurrentPassPhrase = "";
+ }
+
buildLayout();
+
+ if (mCurrentPassPhrase.equals("")) {
+ // check "no passphrase" checkbox and remove button
+ mNoPassphrase.setChecked(true);
+ mChangePassPhrase.setVisibility(View.GONE);
+ }
}
/**
@@ -402,6 +429,12 @@ public class EditKeyActivity extends SherlockFragmentActivity {
* id and key.
*/
private void buildLayout() {
+ setContentView(R.layout.edit_key_activity);
+
+ // find views
+ mChangePassPhrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_pass_phrase);
+ mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase);
+
// Build layout based on given userIds and keys
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -418,6 +451,28 @@ public class EditKeyActivity extends SherlockFragmentActivity {
container.addView(mKeysView);
updatePassPhraseButtonText();
+
+ 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);
+ }
+ }
+ });
}
private long getMasterKeyId() {
@@ -604,7 +659,14 @@ public class EditKeyActivity extends SherlockFragmentActivity {
}
private void updatePassPhraseButtonText() {
- mChangePassPhrase.setText(isPassphraseSet() ? R.string.btn_change_passphrase
- : R.string.btn_set_passphrase);
+ mChangePassPhrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
+ : getString(R.string.btn_set_passphrase));
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 2dfdf254e..c974dfd46 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -55,21 +55,19 @@ 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 com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
-public class EncryptActivity extends SherlockFragmentActivity {
+public class EncryptActivity extends DrawerActivity {
/* Intents */
public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT";
@@ -87,7 +85,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
private long mEncryptionKeyIds[] = null;
private EditText mMessage = null;
- private Button mSelectKeysButton = null;
+ private BootstrapButton mSelectKeysButton = null;
private boolean mEncryptEnabled = false;
private String mEncryptString = "";
@@ -117,7 +115,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
private EditText mFilename = null;
private CheckBox mDeleteAfter = null;
- private ImageButton mBrowse = null;
+ private BootstrapButton mBrowse = null;
private String mInputFilename = null;
private String mOutputFilename = null;
@@ -154,13 +152,6 @@ public class EncryptActivity extends SherlockFragmentActivity {
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();
@@ -181,13 +172,15 @@ public class EncryptActivity extends SherlockFragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.encrypt);
+ setContentView(R.layout.encrypt_activity);
// set actionbar without home button if called from another app
ActionBarHelper.setBackButton(this);
initView();
+ setupDrawerNavigation(savedInstanceState);
+
// Handle intent actions
handleActions(getIntent());
@@ -492,8 +485,9 @@ public class EncryptActivity extends SherlockFragmentActivity {
if (!file.exists() || !file.isFile()) {
Toast.makeText(
this,
- getString(R.string.error_message, getString(R.string.error_file_not_found)),
- Toast.LENGTH_SHORT).show();
+ getString(R.string.error_message,
+ getString(R.string.error_file_not_found)), Toast.LENGTH_SHORT)
+ .show();
return;
}
}
@@ -511,7 +505,8 @@ public class EncryptActivity extends SherlockFragmentActivity {
gotPassPhrase = (passPhrase.length() != 0);
if (!gotPassPhrase) {
- Toast.makeText(this, R.string.passphrase_must_not_be_empty, Toast.LENGTH_SHORT).show();
+ Toast.makeText(this, R.string.passphrase_must_not_be_empty, Toast.LENGTH_SHORT)
+ .show();
return;
}
} else {
@@ -523,8 +518,8 @@ public class EncryptActivity extends SherlockFragmentActivity {
}
if (!encryptIt && mSecretKeyId == 0) {
- Toast.makeText(this, R.string.select_encryption_or_signature_key, Toast.LENGTH_SHORT)
- .show();
+ Toast.makeText(this, R.string.select_encryption_or_signature_key,
+ Toast.LENGTH_SHORT).show();
return;
}
@@ -825,7 +820,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
mModeLabel.setOnClickListener(nextModeClickListener);
mMessage = (EditText) findViewById(R.id.message);
- mSelectKeysButton = (Button) findViewById(R.id.btn_selectEncryptKeys);
+ mSelectKeysButton = (BootstrapButton) findViewById(R.id.btn_selectEncryptKeys);
mSign = (CheckBox) findViewById(R.id.sign);
mMainUserId = (TextView) findViewById(R.id.mainUserId);
mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
@@ -841,7 +836,7 @@ public class EncryptActivity extends SherlockFragmentActivity {
mMessage.setMinimumHeight(height);
mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (ImageButton) findViewById(R.id.btn_browse);
+ mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*",
@@ -853,10 +848,12 @@ public class EncryptActivity extends SherlockFragmentActivity {
Choice[] choices = new Choice[] {
new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
+ getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zip, "ZIP (" + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zlib, "ZLIB (" + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.bzip2, "BZIP2 (" + getString(R.string.compression_very_slow)
- + ")"), };
+ new Choice(Id.choice.compression.zip, "ZIP ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.zlib, "ZLIB ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2 ("
+ + getString(R.string.compression_very_slow) + ")"), };
ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
android.R.layout.simple_spinner_item, choices);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@@ -896,9 +893,9 @@ public class EncryptActivity extends SherlockFragmentActivity {
private void updateView() {
if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- mSelectKeysButton.setText(R.string.no_keys_selected);
+ mSelectKeysButton.setText(getString(R.string.no_keys_selected));
} else if (mEncryptionKeyIds.length == 1) {
- mSelectKeysButton.setText(R.string.one_key_selected);
+ mSelectKeysButton.setText(getString(R.string.one_key_selected));
} else {
mSelectKeysButton.setText("" + mEncryptionKeyIds.length + " "
+ getResources().getString(R.string.n_keys_selected));
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
index 13350b6c6..d604c1c86 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpActivity.java
@@ -17,24 +17,21 @@
package org.sufficientlysecure.keychain.ui;
+import java.util.ArrayList;
+
import org.sufficientlysecure.keychain.R;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentTransaction;
+import android.support.v4.view.ViewPager;
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 {
@@ -45,37 +42,19 @@ public class HelpActivity extends SherlockFragmentActivity {
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);
-
+
setContentView(R.layout.help_activity);
- mViewPager = new ViewPager(this);
- mViewPager.setId(R.id.pager);
+ mViewPager = (ViewPager) findViewById(R.id.pager);
- setContentView(mViewPager);
- ActionBar bar = getSupportActionBar();
- bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
- bar.setDisplayShowTitleEnabled(true);
- bar.setDisplayHomeAsUpEnabled(true);
+ final ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayShowTitleEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ actionBar.setHomeButtonEnabled(false);
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
mTabsAdapter = new TabsAdapter(this, mViewPager);
@@ -87,20 +66,20 @@ public class HelpActivity extends SherlockFragmentActivity {
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)),
+ mTabsAdapter.addTab(actionBar.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)),
+ mTabsAdapter.addTab(actionBar.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)),
+ mTabsAdapter.addTab(actionBar.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)),
+ mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.help_tab_about)),
HelpFragmentAbout.class, null, (selectedTab == 3 ? true : false));
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java
index e7a977707..840ebb650 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/HelpFragmentAbout.java
@@ -48,7 +48,7 @@ public class HelpFragmentAbout extends SherlockFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.help_fragment_about, container, false);
+ View view = inflater.inflate(R.layout.help_about_fragment, container, false);
TextView versionText = (TextView) view.findViewById(R.id.help_about_version);
versionText.setText(getString(R.string.help_about_version) + " " + getVersion());
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 37edc8f49..7d8f4154f 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -22,7 +22,6 @@ import java.util.List;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
@@ -30,26 +29,30 @@ import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.view.View;
+import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
-public class ImportKeysActivity extends SherlockFragmentActivity implements OnNavigationListener {
+public class ImportKeysActivity extends DrawerActivity implements OnNavigationListener {
public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY";
public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_QR_CODE";
@@ -73,14 +76,36 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
OnNavigationListener mOnNavigationListener;
String[] mNavigationStrings;
+ BootstrapButton mImportButton;
+ BootstrapButton mImportSignUploadButton;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.import_keys);
+ setContentView(R.layout.import_keys_activity);
+
+ mImportButton = (BootstrapButton) findViewById(R.id.import_import);
+ mImportButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ importKeys();
+ }
+ });
+ mImportSignUploadButton = (BootstrapButton) findViewById(R.id.import_sign_and_upload);
+ mImportSignUploadButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ signAndUploadOnClick();
+ }
+ });
+
+ getSupportActionBar().setDisplayShowTitleEnabled(false);
+
+ setupDrawerNavigation(savedInstanceState);
// set actionbar without home button if called from another app
- ActionBarHelper.setBackButton(this);
+ // ActionBarHelper.setBackButton(this);
// set drop down navigation
mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
@@ -90,7 +115,6 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
list.setDropDownViewResource(R.layout.sherlock_spinner_dropdown_item);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getSupportActionBar().setListNavigationCallbacks(list, this);
- getSupportActionBar().setDisplayShowTitleEnabled(false);
handleActions(savedInstanceState, getIntent());
}
@@ -216,23 +240,6 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
mListFragment.loadNew(importData, importFilename);
}
- @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);
-
- }
- }
-
// private void importAndSignOld(final long keyId, final String expectedFingerprint) {
// if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
//
@@ -345,7 +352,8 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
int bad = returnData.getInt(KeychainIntentService.RESULT_IMPORT_BAD);
String toastMessage;
if (added > 0 && updated > 0) {
- toastMessage = getString(R.string.keys_added_and_updated, added, updated);
+ toastMessage = getString(R.string.keys_added_and_updated, added,
+ updated);
} else if (added > 0) {
toastMessage = getString(R.string.keys_added, added);
} else if (updated > 0) {
@@ -396,11 +404,11 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
}
}
- public void importOnClick(View view) {
+ public void importOnClick() {
importKeys();
}
- public void signAndUploadOnClick(View view) {
+ public void signAndUploadOnClick() {
// first, import!
// importOnClick(view);
@@ -409,4 +417,43 @@ public class ImportKeysActivity extends SherlockFragmentActivity implements OnNa
.show();
}
+ /**
+ * NFC
+ */
+ @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());
+ }
+ }
+
+ /**
+ * NFC
+ */
+ @Override
+ public void onNewIntent(Intent intent) {
+ // onResume gets called after this to handle the intent
+ setIntent(intent);
+ }
+
+ /**
+ * NFC: Parses the NDEF Message from the intent and prints to the TextView
+ */
+ @SuppressLint("NewApi")
+ 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_KEY);
+ importIntent.putExtra(ImportKeysActivity.EXTRA_KEY_BYTES, receivedKeyringBytes);
+
+ handleActions(null, importIntent);
+ }
+
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
index dcb7dbcc6..31f758395 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
@@ -26,12 +26,13 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysClipboardFragment extends Fragment {
private ImportKeysActivity mImportActivity;
- private Button mButton;
+ private BootstrapButton mButton;
/**
* Creates new instance of this fragment
@@ -52,7 +53,7 @@ public class ImportKeysClipboardFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_clipboard_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_clipboard_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_clipboard_button);
mButton.setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
index fbca9013b..ea76d2898 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java
@@ -31,14 +31,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
-import android.widget.ImageButton;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysFileFragment extends Fragment {
public static final String ARG_PATH = "path";
private ImportKeysActivity mImportActivity;
private EditText mFilename;
- private ImageButton mBrowse;
+ private BootstrapButton mBrowse;
/**
* Creates new instance of this fragment
@@ -61,7 +62,7 @@ public class ImportKeysFileFragment extends Fragment {
View view = inflater.inflate(R.layout.import_keys_file_fragment, container, false);
mFilename = (EditText) view.findViewById(R.id.import_keys_file_input);
- mBrowse = (ImageButton) view.findViewById(R.id.import_keys_file_browse);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.import_keys_file_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
index 2d756dde6..83af8cf48 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
@@ -26,11 +26,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysNFCFragment extends Fragment {
- private Button mButton;
+ private BootstrapButton mButton;
/**
* Creates new instance of this fragment
@@ -51,7 +52,7 @@ public class ImportKeysNFCFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_nfc_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_nfc_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_nfc_button);
mButton.setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index 62b59b4f7..f9ead3a94 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -30,18 +30,18 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
+import com.beardedhen.androidbootstrap.BootstrapButton;
import com.google.zxing.integration.android.IntentIntegratorSupportV4;
import com.google.zxing.integration.android.IntentResult;
public class ImportKeysQrCodeFragment extends Fragment {
private ImportKeysActivity mImportActivity;
- private Button mButton;
+ private BootstrapButton mButton;
private TextView mText;
private ProgressBar mProgress;
@@ -66,7 +66,7 @@ public class ImportKeysQrCodeFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_qr_code_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_qrcode_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_qrcode_button);
mText = (TextView) view.findViewById(R.id.import_qrcode_text);
mProgress = (ProgressBar) view.findViewById(R.id.import_qrcode_progress);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index 106c8ebef..c985f1f60 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -24,12 +24,13 @@ import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewGroup;
import android.view.View.OnClickListener;
-import android.widget.Button;
+import android.view.ViewGroup;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class ImportKeysServerFragment extends Fragment {
- private Button mButton;
+ private BootstrapButton mButton;
/**
* Creates new instance of this fragment
@@ -50,7 +51,7 @@ public class ImportKeysServerFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.import_keys_keyserver_fragment, container, false);
- mButton = (Button) view.findViewById(R.id.import_keyserver_button);
+ mButton = (BootstrapButton) view.findViewById(R.id.import_keyserver_button);
mButton.setOnClickListener(new OnClickListener() {
@Override
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
deleted file mode 100644
index 7b844fe08..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.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_import_from_file)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- menu.add(0, Id.menu.option.export_keys, 6, R.string.menu_export_keys).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_KEY_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_export_key);
- } else {
- title = getString(R.string.title_export_keys);
- }
-
- String message = null;
- if (mKeyType == Id.type.public_key) {
- message = getString(R.string.specify_file_to_export_to);
- } else {
- message = getString(R.string.specify_file_to_export_secret_keys_to);
- }
-
- 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.setAction(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.key_exported);
- } else if (exported > 0) {
- toastMessage = getString(R.string.keys_exported, exported);
- } else {
- toastMessage = getString(R.string.no_keys_exported);
- }
- 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
deleted file mode 100644
index 0d61b1108..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-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.list_empty));
- }
-
- /**
- * 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_export_key);
- menu.add(0, Id.menu.delete, 111, R.string.menu_delete_key);
- }
-
- @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
index 95a3dd3b1..204939610 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicActivity.java
@@ -20,102 +20,79 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
+
+import android.content.Intent;
+import android.os.Bundle;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
-import android.content.Intent;
-import android.os.Bundle;
+public class KeyListPublicActivity extends DrawerActivity {
-public class KeyListPublicActivity extends KeyListActivity {
+ ExportHelper mExportHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mKeyType = Id.type.public_key;
+ mExportHelper = new ExportHelper(this);
setContentView(R.layout.key_list_public_activity);
- mExportFilename = Constants.path.APP_DIR + "/pubexport.asc";
+ // now setup navigation drawer in DrawerActivity...
+ setupDrawerNavigation(savedInstanceState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- menu.add(1, Id.menu.option.key_server, 1, R.string.menu_key_server)
- .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_import_from_qr_code)
- .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_import_from_nfc)
- .setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-
+ getSupportMenuInflater().inflate(R.menu.key_list_public, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case Id.menu.option.key_server: {
- startActivityForResult(new Intent(this, KeyServerQueryActivity.class), 0);
+ case R.id.menu_key_list_public_import:
+ Intent intentImport = new Intent(this, ImportKeysActivity.class);
+ startActivityForResult(intentImport, Id.request.import_from_qr_code);
return true;
- }
- case Id.menu.option.import_from_file: {
- Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_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_KEY_FROM_QR_CODE);
- startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
+ case R.id.menu_key_list_public_export:
+ mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.path.APP_DIR
+ + "/pubexport.asc");
return true;
- }
-
- case Id.menu.option.import_from_nfc: {
- Intent intentImportFromFile = new Intent(this, ImportKeysActivity.class);
- intentImportFromFile.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_NFC);
- startActivityForResult(intentImportFromFile, 0);
-
- return true;
- }
-
- default: {
+ 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;
- // }
- // }
- // }
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, 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
index 0fdcea809..ea088efca 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,171 +17,205 @@
package org.sufficientlysecure.keychain.ui;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
+import java.util.Set;
+
import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter;
-import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.KeyListPublicAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
+import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
+import android.annotation.SuppressLint;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
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.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.AbsListView.MultiChoiceModeListener;
+import android.widget.AdapterView;
+import android.widget.ListView;
-public class KeyListPublicFragment extends KeyListFragment implements
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+/**
+ * Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
+ * StickyListHeaders library which does not extend upon ListView.
+ */
+public class KeyListPublicFragment extends Fragment implements AdapterView.OnItemClickListener,
LoaderManager.LoaderCallbacks<Cursor> {
- private KeyListPublicActivity mKeyListPublicActivity;
+ private KeyListPublicAdapter mAdapter;
+ private StickyListHeadersListView mStickyList;
- private KeyListAdapter mAdapter;
+ // empty layout
+ private BootstrapButton mButtonEmptyCreate;
+ private BootstrapButton mButtonEmptyImport;
/**
- * Define Adapter and Loader on create of Activity
+ * Load custom layout with StickyListView from library
*/
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.key_list_public_fragment, container, false);
+
+ mButtonEmptyCreate = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_create);
+ mButtonEmptyCreate.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getActivity(), EditKeyActivity.class);
+ intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
+ intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
+ intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, ""); // show user id view
+ startActivityForResult(intent, 0);
+ }
+ });
- mAdapter = new KeyListAdapter(mKeyListPublicActivity, null, Id.type.public_key);
- setListAdapter(mAdapter);
+ mButtonEmptyImport = (BootstrapButton) view.findViewById(R.id.key_list_empty_button_import);
+ mButtonEmptyImport.setOnClickListener(new OnClickListener() {
- // Start out with a progress indicator.
- setListShown(false);
+ @Override
+ public void onClick(View v) {
+ Intent intentImportFromFile = new Intent(getActivity(), ImportKeysActivity.class);
+ startActivityForResult(intentImportFromFile, Id.request.import_from_qr_code);
+ }
+ });
- // 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);
+ return view;
}
/**
- * Context Menu on Long Click
+ * Define Adapter and Loader on create of Activity
*/
+ @SuppressLint("NewApi")
@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_update_key);
- menu.add(0, Id.menu.signKey, 2, R.string.menu_sign_key);
- menu.add(0, Id.menu.exportToServer, 3, R.string.menu_export_key_to_server);
- menu.add(0, Id.menu.share, 6, R.string.menu_share);
- menu.add(0, Id.menu.share_qr_code, 7, R.string.menu_share_qr_code);
- menu.add(0, Id.menu.share_nfc, 8, R.string.menu_share_nfc);
-
- }
-
- @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 = PgpKeyHelper.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, (int)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 = PgpKeyHelper.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);
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
- return true;
+ // mKeyListPublicActivity = (KeyListPublicActivity) getActivity();
+ mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
- case Id.menu.share:
- // get master key id using row id
- long masterKeyId3 = ProviderHelper.getPublicMasterKeyId(mKeyListActivity, keyRingRowId);
+ mStickyList.setOnItemClickListener(this);
+ mStickyList.setAreHeadersSticky(true);
+ mStickyList.setDrawingListUnderStickyHeader(false);
+ mStickyList.setFastScrollEnabled(true);
+ try {
+ mStickyList.setFastScrollAlwaysVisible(true);
+ } catch (ApiLevelTooLowException e) {
+ }
- Intent shareIntent = new Intent(mKeyListActivity, ShareActivity.class);
- shareIntent.setAction(ShareActivity.ACTION_SHARE_KEYRING);
- shareIntent.putExtra(ShareActivity.EXTRA_MASTER_KEY_ID, masterKeyId3);
- startActivityForResult(shareIntent, 0);
+ // this view is made visible if no data is available
+ mStickyList.setEmptyView(getActivity().findViewById(R.id.empty));
+
+ /*
+ * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
+ * available for Android >= 3.0
+ */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mStickyList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ mStickyList.getWrappedList().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+ private int count = 0;
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ android.view.MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.key_list_public_multi, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ Set<Integer> positions = mAdapter.getCurrentCheckedPosition();
+
+ // get IDs for checked positions as long array
+ long[] ids = new long[positions.size()];
+ int i = 0;
+ for (int pos : positions) {
+ ids[i] = mAdapter.getItemId(pos);
+ i++;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_key_list_public_multi_encrypt: {
+ encrypt(ids);
+
+ break;
+ }
+ case R.id.menu_key_list_public_multi_delete: {
+ showDeleteKeyDialog(ids);
+
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ count = 0;
+ mAdapter.clearSelection();
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked) {
+ if (checked) {
+ count++;
+ mAdapter.setNewSelection(position, checked);
+ } else {
+ count--;
+ mAdapter.removeSelection(position);
+ }
+
+ String keysSelected = getResources().getQuantityString(
+ R.plurals.key_list_selected_keys, count, count);
+ mode.setTitle(keysSelected);
+ }
+
+ });
+ }
- return true;
+ // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
+ // Start out with a progress indicator.
+ // setListShown(false);
- default:
- return super.onContextItemSelected(item);
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new KeyListPublicAdapter(getActivity(), null, Id.type.public_key, USER_ID_INDEX);
+ mStickyList.setAdapter(mAdapter);
- }
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
}
// 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 int USER_ID_INDEX = 2;
+
static final String SORT_ORDER = UserIds.USER_ID + " ASC";
@Override
@@ -199,14 +233,17 @@ public class KeyListPublicFragment extends KeyListFragment implements
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- mAdapter.setGroupCursor(data);
+ mAdapter.swapCursor(data);
+ mStickyList.setAdapter(mAdapter);
+
+ // NOTE: Not supported by StickyListHeader, thus no indicator is shown while loading
// The list should now be shown.
- if (isResumed()) {
- setListShown(true);
- } else {
- setListShownNoAnimation(true);
- }
+ // if (isResumed()) {
+ // setListShown(true);
+ // } else {
+ // setListShownNoAnimation(true);
+ // }
}
@Override
@@ -214,7 +251,43 @@ public class KeyListPublicFragment extends KeyListFragment implements
// 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);
+ mAdapter.swapCursor(null);
+ }
+
+ /**
+ * On click on item, start key view activity
+ */
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ Intent detailsIntent = new Intent(getActivity(), ViewKeyActivity.class);
+ detailsIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id)));
+ startActivity(detailsIntent);
+ }
+
+ public void encrypt(long[] keyRingRowIds) {
+ // get master key ids from row ids
+ long[] keyRingIds = new long[keyRingRowIds.length];
+ for (int i = 0; i < keyRingRowIds.length; i++) {
+ keyRingIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), keyRingRowIds[i]);
+ }
+
+ Intent intent = new Intent(getActivity(), EncryptActivity.class);
+ intent.setAction(EncryptActivity.ACTION_ENCRYPT);
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingIds);
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+ }
+
+ /**
+ * Show dialog to delete key
+ *
+ * @param keyRingRowIds
+ */
+ public void showDeleteKeyDialog(long[] keyRingRowIds) {
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null,
+ keyRingRowIds, Id.type.public_key);
+
+ deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
}
-}
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
index 822c73872..34a053d25 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretActivity.java
@@ -1,18 +1,18 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
import android.content.Intent;
import android.os.Bundle;
@@ -27,47 +28,53 @@ import android.os.Bundle;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
-public class KeyListSecretActivity extends KeyListActivity {
+public class KeyListSecretActivity extends DrawerActivity {
+
+ ExportHelper mExportHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mKeyType = Id.type.secret_key;
+ mExportHelper = new ExportHelper(this);
setContentView(R.layout.key_list_secret_activity);
- mExportFilename = Constants.path.APP_DIR + "/secexport.asc";
+ // now setup navigation drawer in DrawerActivity...
+ setupDrawerNavigation(savedInstanceState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- menu.add(1, Id.menu.option.create, 1, R.string.menu_create_key).setShowAsAction(
- MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- menu.add(1, Id.menu.option.createExpert, 2, R.string.menu_create_key_expert).setShowAsAction(
- MenuItem.SHOW_AS_ACTION_NEVER);
-
+ getSupportMenuInflater().inflate(R.menu.key_list_secret, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case Id.menu.option.create: {
+ case R.id.menu_key_list_secret_create:
createKey();
- return true;
- }
- case Id.menu.option.createExpert: {
+ return true;
+ case R.id.menu_key_list_secret_create_expert:
createKeyExpert();
+
return true;
- }
+ case R.id.menu_key_list_secret_export:
+ mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.path.APP_DIR
+ + "/secexport.asc");
- default: {
+ return true;
+ case R.id.menu_key_list_secret_import:
+ Intent intentImport = new Intent(this, ImportKeysActivity.class);
+ startActivityForResult(intentImport, Id.request.import_from_qr_code);
+
+ return true;
+ default:
return super.onOptionsItemSelected(item);
}
- }
}
private void createKey() {
@@ -84,12 +91,11 @@ public class KeyListSecretActivity extends KeyListActivity {
startActivityForResult(intent, 0);
}
- void editKey(long masterKeyId, boolean masterCanSign) {
- Intent intent = new Intent(this, EditKeyActivity.class);
- intent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
- intent.putExtra(EditKeyActivity.EXTRA_MASTER_KEY_ID, masterKeyId);
- intent.putExtra(EditKeyActivity.EXTRA_MASTER_CAN_SIGN, masterCanSign);
- startActivityForResult(intent, 0);
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
index 4bbf3d6ef..0e2d22e1e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java
@@ -1,116 +1,168 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
+import java.util.Set;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.adapter.KeyListAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import android.annotation.SuppressLint;
+import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.View;
-import android.widget.ExpandableListView;
-import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.AbsListView.MultiChoiceModeListener;
+import android.widget.AdapterView.OnItemClickListener;
-public class KeyListSecretFragment extends KeyListFragment implements
- LoaderManager.LoaderCallbacks<Cursor> {
+import com.actionbarsherlock.app.SherlockListFragment;
- private KeyListSecretActivity mKeyListSecretActivity;
+public class KeyListSecretFragment extends SherlockListFragment implements
+ LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener {
- private KeyListAdapter mAdapter;
+ private KeyListSecretActivity mKeyListSecretActivity;
+ private KeyListSecretAdapter mAdapter;
/**
* Define Adapter and Loader on create of Activity
*/
+ @SuppressLint("NewApi")
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mKeyListSecretActivity = (KeyListSecretActivity) getActivity();
- mAdapter = new KeyListAdapter(mKeyListSecretActivity, null, Id.type.secret_key);
- setListAdapter(mAdapter);
+ getListView().setOnItemClickListener(this);
+
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText(getString(R.string.list_empty));
+
+ /*
+ * ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
+ * available for Android >= 3.0
+ */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
+ getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {
+
+ private int count = 0;
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ android.view.MenuInflater inflater = getActivity().getMenuInflater();
+ inflater.inflate(R.menu.key_list_secret_multi, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ Set<Integer> positions = mAdapter.getCurrentCheckedPosition();
+
+ // get IDs for checked positions as long array
+ long[] ids = new long[positions.size()];
+ int i = 0;
+ for (int pos : positions) {
+ ids[i] = mAdapter.getItemId(pos);
+ i++;
+ }
+
+ switch (item.getItemId()) {
+ case R.id.menu_key_list_public_multi_delete: {
+ showDeleteKeyDialog(ids);
+
+ break;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ count = 0;
+ mAdapter.clearSelection();
+ }
+
+ @Override
+ public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
+ boolean checked) {
+ if (checked) {
+ count++;
+ mAdapter.setNewSelection(position, checked);
+ } else {
+ count--;
+ mAdapter.removeSelection(position);
+ }
+
+ String keysSelected = getResources().getQuantityString(
+ R.plurals.key_list_selected_keys, count, count);
+ mode.setTitle(keysSelected);
+ }
+
+ });
+ }
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
// Start out with a progress indicator.
setListShown(false);
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new KeyListSecretAdapter(mKeyListSecretActivity, null, 0);
+ setListAdapter(mAdapter);
+
// 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_edit_key);
- }
-
- @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);
-
- boolean masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(mKeyListSecretActivity,
- keyRingRowId);
-
- switch (item.getItemId()) {
- case Id.menu.edit:
- mKeyListSecretActivity.editKey(masterKeyId, masterCanSign);
-
- return true;
-
- default:
- return super.onContextItemSelected(item);
-
- }
+ getLoaderManager().initLoader(0, null, this);
}
// 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 + " COLLATE LOCALIZED ASC";
- static final String SORT_ORDER = UserIds.USER_ID + " ASC";
-
- @Override
public Loader<Cursor> 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.
+ // First, pick the base URI to use depending on whether we are
+ // currently filtering.
Uri baseUri = KeyRings.buildSecretKeyRingsUri();
// Now create and return a CursorLoader that will take care of
@@ -118,11 +170,10 @@ public class KeyListSecretFragment extends KeyListFragment implements
return new CursorLoader(getActivity(), baseUri, PROJECTION, null, null, SORT_ORDER);
}
- @Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
- mAdapter.setGroupCursor(data);
+ mAdapter.swapCursor(data);
// The list should now be shown.
if (isResumed()) {
@@ -132,12 +183,33 @@ public class KeyListSecretFragment extends KeyListFragment implements
}
}
- @Override
public void onLoaderReset(Loader<Cursor> 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);
+ mAdapter.swapCursor(null);
}
+ /**
+ * On click on item, start key view activity
+ */
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ Intent editIntent = new Intent(mKeyListSecretActivity, EditKeyActivity.class);
+ editIntent.setData(KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(id)));
+ editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
+ startActivityForResult(editIntent, 0);
+ }
+
+ /**
+ * Show dialog to delete key
+ *
+ * @param keyRingRowIds
+ */
+ public void showDeleteKeyDialog(long[] keyRingRowIds) {
+ DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(null,
+ keyRingRowIds, Id.type.secret_key);
+
+ deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
+ }
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java
index b4679f9d5..6073e6b80 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerQueryActivity.java
@@ -111,7 +111,7 @@ public class KeyServerQueryActivity extends SherlockFragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.key_server_query_layout);
+ setContentView(R.layout.key_server_query);
mQuery = (EditText)findViewById(R.id.query);
mSearch = (Button)findViewById(R.id.btn_search);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java
index 996637c7a..8a32ea513 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/KeyServerUploadActivity.java
@@ -76,7 +76,7 @@ public class KeyServerUploadActivity extends SherlockFragmentActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.key_server_export_layout);
+ setContentView(R.layout.key_server_export);
export = (Button) findViewById(R.id.btn_export_to_server);
keyServer = (Spinner) findViewById(R.id.keyServer);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
deleted file mode 100644
index 9a270e60b..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/MainActivity.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
-
-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);
- menu.add(0, Id.menu.option.crypto_consumers, 0, R.string.menu_api_app_settings)
- .setIcon(R.drawable.ic_menu_settings)
- .setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
-
- case Id.menu.option.preferences:
- startActivity(new Intent(this, PreferencesActivity.class));
- return true;
-
- case Id.menu.option.crypto_consumers:
- startActivity(new Intent(this, RegisteredAppsListActivity.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
index 6607ab4d5..46bbd05c9 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -20,13 +20,9 @@ import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
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;
@@ -34,6 +30,9 @@ import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockPreferenceActivity;
+
public class PreferencesActivity extends SherlockPreferenceActivity {
private IntegerListPreference mPassPhraseCacheTtl = null;
private IntegerListPreference mEncryptionAlgorithm = null;
@@ -52,8 +51,8 @@ public class PreferencesActivity extends SherlockPreferenceActivity {
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(true);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setHomeButtonEnabled(true);
+ actionBar.setDisplayHomeAsUpEnabled(false);
+ actionBar.setHomeButtonEnabled(false);
addPreferencesFromResource(R.xml.preferences);
@@ -219,22 +218,4 @@ public class PreferencesActivity extends SherlockPreferenceActivity {
}
}
}
-
- @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/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
index b0711ed31..83669a523 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
@@ -137,21 +137,4 @@ public class SelectSecretKeyActivity extends SherlockFragmentActivity {
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/ShareActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java
deleted file mode 100644
index 159b2b63a..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareActivity.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.ui;
-
-import java.util.ArrayList;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
-
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-
-public class ShareActivity extends SherlockFragmentActivity {
- // Actions for internal use only:
- 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 = "master_key_id";
-
- @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<String> keyringArmored = ProviderHelper.getPublicKeyRingsAsArmoredString(this,
- new long[] { masterKeyId });
-
- 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.action_share_key_with)));
- } else if (ACTION_SHARE_KEYRING_WITH_QR_CODE.equals(action)) {
- ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
- .get(0));
- dialog.show(getSupportFragmentManager(), "qrCodeShareDialog");
- }
-
- // close this activity
- // TODO: finish() would also close dialog...
- // integrate this into new KeyViewActivity when ready
- // finish();
- }
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java
deleted file mode 100644
index db6a156c7..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ShareNfcBeamActivity.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- * 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 org.sufficientlysecure.htmltextview.HtmlTextView;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-
-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.NfcAdapter.OnNdefPushCompleteCallback;
-import android.nfc.NfcEvent;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Parcelable;
-import android.provider.Settings;
-import android.widget.Toast;
-
-import com.actionbarsherlock.app.SherlockFragmentActivity;
-import com.actionbarsherlock.view.Menu;
-import com.actionbarsherlock.view.MenuInflater;
-import com.actionbarsherlock.view.MenuItem;
-
-@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 = "master_key_id";
-
- 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_jelly_bean_needed),
- 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_nfc_needed),
- 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_KEY);
- importIntent.putExtra(ImportKeysActivity.EXTRA_KEY_BYTES, receivedKeyringBytes);
-
- finish();
-
- startActivity(importIntent);
- }
-
- private void buildView() {
- setContentView(R.layout.share_nfc_beam);
-
- HtmlTextView aboutTextView = (HtmlTextView) findViewById(R.id.nfc_beam_text);
-
- // load html from raw resource (Parsing handled by HtmlTextView library)
- aboutTextView.setHtmlFromRawResource(this, R.raw.nfc_beam_share);
-
- // no flickering when clicking textview for Android < 4
- aboutTextView.setTextColor(getResources().getColor(android.R.color.black));
-
- // set actionbar without home button if called from another app
- ActionBarHelper.setBackButton(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.nfc_successfull, 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
index c2fe1315b..6abdddee6 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/SignKeyActivity.java
@@ -68,7 +68,7 @@ public class SignKeyActivity extends SherlockFragmentActivity {
super.onCreate(savedInstanceState);
// check we havent already signed it
- setContentView(R.layout.sign_key_layout);
+ setContentView(R.layout.sign_key_activity);
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayShowTitleEnabled(true);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
new file mode 100644
index 000000000..e2f90e87c
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013 Bahtiar 'kalkin' Gadimov
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.helper.ExportHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+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.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.CreateNdefMessageCallback;
+import android.nfc.NfcAdapter.OnNdefPushCompleteCallback;
+import android.nfc.NfcEvent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.text.format.DateFormat;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+@SuppressLint("NewApi")
+public class ViewKeyActivity extends SherlockFragmentActivity implements CreateNdefMessageCallback,
+ OnNdefPushCompleteCallback, LoaderManager.LoaderCallbacks<Cursor> {
+
+ ExportHelper mExportHelper;
+
+ private Uri mDataUri;
+
+ private PGPPublicKey mPublicKey;
+
+ private TextView mName;
+ private TextView mEmail;
+ private TextView mComment;
+ private TextView mAlgorithm;
+ private TextView mKeyId;
+ private TextView mExpiry;
+ private TextView mCreation;
+ private TextView mFingerprint;
+ private BootstrapButton mActionEncrypt;
+
+ private ListView mUserIds;
+ private ListView mKeys;
+
+ // NFC
+ private NfcAdapter mNfcAdapter;
+ private byte[] mSharedKeyringBytes;
+ private static final int NFC_SENT = 1;
+
+ private static final int LOADER_ID_KEYRING = 0;
+ private static final int LOADER_ID_USER_IDS = 1;
+ private static final int LOADER_ID_KEYS = 2;
+ private ViewKeyUserIdsAdapter mUserIdsAdapter;
+ private ViewKeyKeysAdapter mKeysAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mExportHelper = new ExportHelper(this);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setIcon(android.R.color.transparent);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ setContentView(R.layout.view_key_activity);
+
+ mName = (TextView) findViewById(R.id.name);
+ mEmail = (TextView) findViewById(R.id.email);
+ mComment = (TextView) findViewById(R.id.comment);
+ mKeyId = (TextView) findViewById(R.id.key_id);
+ mAlgorithm = (TextView) findViewById(R.id.algorithm);
+ mCreation = (TextView) findViewById(R.id.creation);
+ mExpiry = (TextView) findViewById(R.id.expiry);
+ mFingerprint = (TextView) findViewById(R.id.fingerprint);
+ mActionEncrypt = (BootstrapButton) findViewById(R.id.action_encrypt);
+ mUserIds = (ListView) findViewById(R.id.user_ids);
+ mKeys = (ListView) findViewById(R.id.keys);
+
+ Intent intent = getIntent();
+ mDataUri = intent.getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mDataUri);
+ loadData(mDataUri);
+ initNfc(mDataUri);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getSupportMenuInflater().inflate(R.menu.key_view, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ Intent homeIntent = new Intent(this, KeyListPublicActivity.class);
+ homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ startActivity(homeIntent);
+ return true;
+ case R.id.menu_key_view_update:
+ updateFromKeyserver(mDataUri);
+ return true;
+ case R.id.menu_key_view_sign:
+ signKey(mDataUri);
+ return true;
+ case R.id.menu_key_view_export_keyserver:
+ uploadToKeyserver(mDataUri);
+ return true;
+ case R.id.menu_key_view_export_file:
+ mExportHelper.showExportKeysDialog(mDataUri, Id.type.public_key, Constants.path.APP_DIR
+ + "/pubexport.asc");
+ return true;
+ case R.id.menu_key_view_share_default:
+ shareKey(mDataUri);
+ return true;
+ case R.id.menu_key_view_share_qr_code:
+ shareKeyQrCode(mDataUri);
+ return true;
+ case R.id.menu_key_view_share_nfc:
+ shareNfc();
+ return true;
+ case R.id.menu_key_view_share_clipboard:
+ copyToClipboard(mDataUri);
+ return true;
+ case R.id.menu_key_view_delete: {
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+ };
+
+ mExportHelper.deleteKey(mDataUri, Id.type.public_key, returnHandler);
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void loadData(Uri dataUri) {
+ // TODO: don't use pubkey object, use database!!!
+
+ PGPPublicKeyRing ring = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
+ mPublicKey = ring.getPublicKey();
+
+ mKeyId.setText(PgpKeyHelper.shortifyFingerprint(PgpKeyHelper
+ .convertFingerprintToHex(mPublicKey.getFingerprint())));
+
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(mPublicKey.getFingerprint());
+ fingerprint = fingerprint.replace(" ", "\n");
+ mFingerprint.setText(fingerprint);
+
+ // TODO: get image with getUserAttributes() on key and then PGPUserAttributeSubpacketVector
+
+ Date expiryDate = PgpKeyHelper.getExpiryDate(mPublicKey);
+ if (expiryDate == null) {
+ mExpiry.setText(R.string.none);
+ } else {
+ mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
+ }
+
+ mCreation.setText(DateFormat.getDateFormat(getApplicationContext()).format(
+ PgpKeyHelper.getCreationDate(mPublicKey)));
+ mAlgorithm.setText(PgpKeyHelper.getAlgorithmInfo(mPublicKey));
+
+ mActionEncrypt.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ long[] encryptionKeyIds = new long[] { mPublicKey.getKeyID() };
+ Intent intent = new Intent(ViewKeyActivity.this, EncryptActivity.class);
+ intent.setAction(EncryptActivity.ACTION_ENCRYPT);
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
+ // used instead of startActivity set actionbar based on callingPackage
+ startActivityForResult(intent, 0);
+ }
+ });
+
+ mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0);
+
+ mUserIds.setAdapter(mUserIdsAdapter);
+ // mUserIds.setEmptyView(findViewById(android.R.id.empty));
+ // mUserIds.setClickable(true);
+ // mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ // @Override
+ // public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) {
+ // }
+ // });
+
+ mKeysAdapter = new ViewKeyKeysAdapter(this, null, 0);
+
+ mKeys.setAdapter(mKeysAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
+ getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
+ getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
+ }
+
+ static final String[] KEYRING_PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID,
+ UserIds.USER_ID };
+
+ static final String[] USER_IDS_PROJECTION = new String[] { UserIds._ID, UserIds.USER_ID,
+ UserIds.RANK, };
+ // not the main user id
+ static final String USER_IDS_SELECTION = UserIds.RANK + " > 0 ";
+ static final String USER_IDS_SORT_ORDER = UserIds.USER_ID + " COLLATE LOCALIZED ASC";
+
+ static final String[] KEYS_PROJECTION = new String[] { Keys._ID, Keys.KEY_ID,
+ Keys.IS_MASTER_KEY, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.CAN_CERTIFY, Keys.CAN_SIGN,
+ Keys.CAN_ENCRYPT, };
+ static final String KEYS_SORT_ORDER = Keys.RANK + " ASC";
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ switch (id) {
+ case LOADER_ID_KEYRING: {
+ Uri baseUri = mDataUri;
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(this, baseUri, KEYRING_PROJECTION, null, null, null);
+ }
+ case LOADER_ID_USER_IDS: {
+ Uri baseUri = UserIds.buildUserIdsUri(mDataUri);
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, USER_IDS_SELECTION, null,
+ USER_IDS_SORT_ORDER);
+ }
+ case LOADER_ID_KEYS: {
+ Uri baseUri = Keys.buildKeysUri(mDataUri);
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(this, baseUri, KEYS_PROJECTION, null, null, KEYS_SORT_ORDER);
+ }
+
+ default:
+ return null;
+ }
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ switch (loader.getId()) {
+ case LOADER_ID_KEYRING:
+ if (data.moveToFirst()) {
+ String[] mainUserId = PgpKeyHelper.splitUserId(data.getString(2));
+ setTitle(mainUserId[0]);
+ mName.setText(mainUserId[0]);
+ mEmail.setText(mainUserId[1]);
+ mComment.setText(mainUserId[2]);
+ }
+
+ break;
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(data);
+ break;
+ case LOADER_ID_KEYS:
+ mKeysAdapter.swapCursor(data);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /**
+ * 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.
+ */
+ public void onLoaderReset(Loader<Cursor> loader) {
+ switch (loader.getId()) {
+ case LOADER_ID_KEYRING:
+ // TODO?
+ break;
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(null);
+ break;
+ case LOADER_ID_KEYS:
+ mKeysAdapter.swapCursor(null);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void uploadToKeyserver(Uri dataUri) {
+ long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
+
+ Intent uploadIntent = new Intent(this, KeyServerUploadActivity.class);
+ uploadIntent.setAction(KeyServerUploadActivity.ACTION_EXPORT_KEY_TO_SERVER);
+ uploadIntent.putExtra(KeyServerUploadActivity.EXTRA_KEYRING_ROW_ID, (int) keyRingRowId);
+ startActivityForResult(uploadIntent, Id.request.export_to_server);
+ }
+
+ private void updateFromKeyserver(Uri dataUri) {
+ long updateKeyId = 0;
+ PGPPublicKeyRing updateRing = (PGPPublicKeyRing) ProviderHelper
+ .getPGPKeyRing(this, dataUri);
+
+ if (updateRing != null) {
+ updateKeyId = PgpKeyHelper.getMasterKey(updateRing).getKeyID();
+ }
+ if (updateKeyId == 0) {
+ Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
+ return;
+ }
+
+ Intent signIntent = new Intent(this, SignKeyActivity.class);
+ signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, updateKeyId);
+ startActivity(signIntent);
+
+ Intent queryIntent = new Intent(this, 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);
+ }
+
+ private void signKey(Uri dataUri) {
+ long keyId = 0;
+ PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, dataUri);
+
+ if (signKey != null) {
+ keyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
+ }
+ if (keyId == 0) {
+ Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
+ return;
+ }
+
+ Intent signIntent = new Intent(this, SignKeyActivity.class);
+ signIntent.putExtra(SignKeyActivity.EXTRA_KEY_ID, keyId);
+ startActivity(signIntent);
+ }
+
+ private void shareKey(Uri dataUri) {
+ // get public keyring as ascii armored string
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
+ new long[] { masterKeyId });
+
+ // 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.action_share_key_with)));
+ }
+
+ private void shareKeyQrCode(Uri dataUri) {
+ // get public keyring as ascii armored string
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
+ new long[] { masterKeyId });
+
+ ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(keyringArmored
+ .get(0));
+ dialog.show(getSupportFragmentManager(), "shareQrCodeDialog");
+ }
+
+ private void copyToClipboard(Uri dataUri) {
+ // get public keyring as ascii armored string
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
+ new long[] { masterKeyId });
+
+ ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
+ }
+
+ private void shareNfc() {
+ ShareNfcDialogFragment dialog = ShareNfcDialogFragment.newInstance();
+ dialog.show(getSupportFragmentManager(), "shareNfcDialog");
+ }
+
+ /**
+ * NFC: Initialize NFC sharing if OS and device supports it
+ */
+ private void initNfc(Uri dataUri) {
+ // check if NFC Beam is supported (>= Android 4.1)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ // Check for available NFC Adapter
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ if (mNfcAdapter != null) {
+ // init nfc
+
+ // get public keyring as byte array
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
+ mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri,
+ 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);
+ }
+ }
+ }
+
+ /**
+ * NFC: 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;
+ }
+
+ /**
+ * NFC: 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
+ mNfcHandler.obtainMessage(NFC_SENT).sendToTarget();
+ }
+
+ /**
+ * NFC: This handler receives a message from onNdefPushComplete
+ */
+ private final Handler mNfcHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case NFC_SENT:
+ Toast.makeText(getApplicationContext(), R.string.nfc_successfull, Toast.LENGTH_LONG)
+ .show();
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (!mExportHelper.handleActivityResult(requestCode, resultCode, data)) {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
deleted file mode 100644
index e94934008..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListAdapter.java
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR 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.adapter;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-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.unknown_user_id);
- 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.unknown_user_id);
- }
-
- if (mainUserIdRest.getText().length() == 0) {
- mainUserIdRest.setVisibility(View.GONE);
- } else {
- mainUserIdRest.setVisibility(View.VISIBLE);
- }
- }
-
- /**
- * 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 = PgpKeyHelper.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 = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
- .getColumnIndex(Keys.KEY_ID)));
- String algorithmStr = PgpKeyHelper.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 certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
- if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) {
- certifyIcon.setVisibility(View.GONE);
- } else {
- certifyIcon.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_CERTIFY, 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_CERTIFY, 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);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
new file mode 100644
index 000000000..f1e58a5d3
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListPublicAdapter.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+import org.sufficientlysecure.keychain.util.Log;
+
+import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Implements StickyListHeadersAdapter from library
+ */
+public class KeyListPublicAdapter extends CursorAdapter implements StickyListHeadersAdapter {
+ private LayoutInflater mInflater;
+ private int mSectionColumnIndex;
+
+ @SuppressLint("UseSparseArrays")
+ private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
+
+ public KeyListPublicAdapter(Context context, Cursor c, int flags, int sectionColumnIndex) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ mSectionColumnIndex = sectionColumnIndex;
+ }
+
+ /**
+ * Bind cursor data to the item list view
+ *
+ * NOTE: CursorAdapter already implements the ViewHolder pattern in its getView() method. Thus
+ * no ViewHolder is required here.
+ */
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknown_user_id);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = cursor.getString(userIdIndex);
+ if (userId != null) {
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
+
+ if (userIdSplit[1] != null) {
+ mainUserIdRest.setText(userIdSplit[1]);
+ }
+ mainUserId.setText(userIdSplit[0]);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknown_user_id);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ } else {
+ mainUserIdRest.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.key_list_item, null);
+ }
+
+ /**
+ * Creates a new header view and binds the section headers to it. It uses the ViewHolder
+ * pattern. Most functionality is similar to getView() from Android's CursorAdapter.
+ *
+ * NOTE: The variables mDataValid and mCursor are available due to the super class
+ * CursorAdapter.
+ */
+ @Override
+ public View getHeaderView(int position, View convertView, ViewGroup parent) {
+ HeaderViewHolder holder;
+ if (convertView == null) {
+ holder = new HeaderViewHolder();
+ convertView = mInflater.inflate(R.layout.key_list_public_header, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.stickylist_header_text);
+ convertView.setTag(holder);
+ } else {
+ holder = (HeaderViewHolder) convertView.getTag();
+ }
+
+ if (!mDataValid) {
+ // no data available at this point
+ Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
+ return convertView;
+ }
+
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("couldn't move cursor to position " + position);
+ }
+
+ // set header text as first char in user id
+ String headerText = "" + mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
+ holder.text.setText(headerText);
+ return convertView;
+ }
+
+ /**
+ * Header IDs should be static, position=1 should always return the same Id that is.
+ */
+ @Override
+ public long getHeaderId(int position) {
+ if (!mDataValid) {
+ // no data available at this point
+ Log.d(Constants.TAG, "getHeaderView: No data available at this point!");
+ return -1;
+ }
+
+ if (!mCursor.moveToPosition(position)) {
+ throw new IllegalStateException("couldn't move cursor to position " + position);
+ }
+
+ // return the first character of the name as ID because this is what
+ // headers private HashMap<Integer, Boolean> mSelection = new HashMap<Integer,
+ // Boolean>();are based upon
+ return mCursor.getString(mSectionColumnIndex).subSequence(0, 1).charAt(0);
+ }
+
+ class HeaderViewHolder {
+ TextView text;
+ }
+
+ /** -------------------------- MULTI-SELECTION METHODS -------------- */
+ public void setNewSelection(int position, boolean value) {
+ mSelection.put(position, value);
+ notifyDataSetChanged();
+ }
+
+ public boolean isPositionChecked(int position) {
+ Boolean result = mSelection.get(position);
+ return result == null ? false : result;
+ }
+
+ public Set<Integer> getCurrentCheckedPosition() {
+ return mSelection.keySet();
+ }
+
+ public void removeSelection(int position) {
+ mSelection.remove(position);
+ notifyDataSetChanged();
+ }
+
+ public void clearSelection() {
+ mSelection.clear();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // let the adapter handle setting up the row views
+ View v = super.getView(position, convertView, parent);
+
+ /**
+ * Change color for multi-selection
+ */
+ // default color
+ v.setBackgroundColor(Color.TRANSPARENT);
+ if (mSelection.get(position) != null) {
+ // this is a selected position, change color!
+ v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
+ }
+ return v;
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
new file mode 100644
index 000000000..d06c0287c
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/KeyListSecretAdapter.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import java.util.HashMap;
+import java.util.Set;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class KeyListSecretAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ @SuppressLint("UseSparseArrays")
+ private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();
+
+ public KeyListSecretAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
+
+ TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mainUserId.setText(R.string.unknown_user_id);
+ TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mainUserIdRest.setText("");
+
+ String userId = cursor.getString(userIdIndex);
+ if (userId != null) {
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
+
+ if (userIdSplit[1] != null) {
+ mainUserIdRest.setText(userIdSplit[1]);
+ }
+ mainUserId.setText(userIdSplit[0]);
+ }
+
+ if (mainUserId.getText().length() == 0) {
+ mainUserId.setText(R.string.unknown_user_id);
+ }
+
+ if (mainUserIdRest.getText().length() == 0) {
+ mainUserIdRest.setVisibility(View.GONE);
+ } else {
+ mainUserIdRest.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.key_list_item, null);
+ }
+
+ /** -------------------------- MULTI-SELECTION METHODS -------------- */
+ public void setNewSelection(int position, boolean value) {
+ mSelection.put(position, value);
+ notifyDataSetChanged();
+ }
+
+ public boolean isPositionChecked(int position) {
+ Boolean result = mSelection.get(position);
+ return result == null ? false : result;
+ }
+
+ public Set<Integer> getCurrentCheckedPosition() {
+ return mSelection.keySet();
+ }
+
+ public void removeSelection(int position) {
+ mSelection.remove(position);
+ notifyDataSetChanged();
+ }
+
+ public void clearSelection() {
+ mSelection.clear();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ // let the adapter handle setting up the row views
+ View v = super.getView(position, convertView, parent);
+
+ /**
+ * Change color for multi-selection
+ */
+ // default color
+ v.setBackgroundColor(Color.TRANSPARENT);
+ if (mSelection.get(position) != null) {
+ // this is a selected position, change color!
+ v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis));
+ }
+ return v;
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index ebb7261be..c6eca0296 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -18,11 +18,10 @@
package org.sufficientlysecure.keychain.ui.adapter;
import org.sufficientlysecure.keychain.Id;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
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;
@@ -78,7 +77,7 @@ public class SelectKeyCursorAdapter extends CursorAdapter {
String userId = cursor.getString(cursor.getColumnIndex(UserIds.USER_ID));
if (userId != null) {
- String[] userIdSplit = OtherHelper.splitUserId(userId);
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
if (userIdSplit[1] != null) {
mainUserIdRest.setText(userIdSplit[1]);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
new file mode 100644
index 000000000..51286af66
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+
+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.ImageView;
+import android.widget.TextView;
+
+public class ViewKeyKeysAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public ViewKeyKeysAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(cursor
+ .getColumnIndex(Keys.KEY_ID)));
+ String algorithmStr = PgpKeyHelper.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 certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
+ if (cursor.getInt(cursor.getColumnIndex(Keys.CAN_CERTIFY)) != 1) {
+ certifyIcon.setVisibility(View.GONE);
+ } else {
+ certifyIcon.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);
+ }
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.view_key_keys_item, null);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
new file mode 100644
index 000000000..2e2606fd0
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract.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.TextView;
+
+public class ViewKeyUserIdsAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ int userIdIndex = cursor.getColumnIndex(UserIds.USER_ID);
+
+ String userIdStr = cursor.getString(userIdIndex);
+
+ TextView userId = (TextView) view.findViewById(R.id.userId);
+ userId.setText(userIdStr);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.view_key_userids_item, null);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 638702b57..101167777 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -38,7 +38,7 @@ 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_DELETE_KEY_RING_ROW_IDS = "delete_file";
private static final String ARG_KEY_TYPE = "key_type";
public static final int MESSAGE_OKAY = 1;
@@ -48,13 +48,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
/**
* Creates new instance of this delete file dialog fragment
*/
- public static DeleteKeyDialogFragment newInstance(Messenger messenger, long deleteKeyRingRowId,
+ public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds,
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.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds);
args.putInt(ARG_KEY_TYPE, keyType);
frag.setArguments(args);
@@ -70,36 +70,48 @@ public class DeleteKeyDialogFragment extends DialogFragment {
final FragmentActivity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- final long deleteKeyRingRowId = getArguments().getLong(ARG_DELETE_KEY_RING_ROW_ID);
+ final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS);
final int keyType = getArguments().getInt(ARG_KEY_TYPE);
- // TODO: better way to do this?
- String userId = activity.getString(R.string.unknown_user_id);
+ AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+ builder.setTitle(R.string.warning);
+
+ if (keyRingRowIds.length == 1) {
+ // TODO: better way to do this?
+ String userId = activity.getString(R.string.unknown_user_id);
+
+ if (keyType == Id.type.public_key) {
+ PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity,
+ keyRingRowIds[0]);
+ userId = PgpKeyHelper.getMainUserIdSafe(activity,
+ PgpKeyHelper.getMasterKey(keyRing));
+ } else {
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity,
+ keyRingRowIds[0]);
+ userId = PgpKeyHelper.getMainUserIdSafe(activity,
+ PgpKeyHelper.getMasterKey(keyRing));
+ }
- if (keyType == Id.type.public_key) {
- PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByRowId(activity,
- deleteKeyRingRowId);
- userId = PgpKeyHelper.getMainUserIdSafe(activity, PgpKeyHelper.getMasterKey(keyRing));
+ builder.setMessage(getString(
+ keyType == Id.type.public_key ? R.string.key_deletion_confirmation
+ : R.string.secret_key_deletion_confirmation, userId));
} else {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByRowId(activity,
- deleteKeyRingRowId);
- userId = PgpKeyHelper.getMainUserIdSafe(activity, PgpKeyHelper.getMasterKey(keyRing));
+ builder.setMessage(R.string.key_deletion_confirmation_multi);
}
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(R.string.warning);
- builder.setMessage(getString(
- keyType == Id.type.public_key ? R.string.key_deletion_confirmation
- : R.string.secret_key_deletion_confirmation, 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);
+ for (long keyRowId : keyRingRowIds) {
+ ProviderHelper.deletePublicKeyRing(activity, keyRowId);
+ }
} else {
- ProviderHelper.deleteSecretKeyRing(activity, deleteKeyRingRowId);
+ for (long keyRowId : keyRingRowIds) {
+ ProviderHelper.deleteSecretKeyRing(activity, keyRowId);
+ }
}
dismiss();
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
index 730fcc520..e65da2aa8 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java
@@ -18,9 +18,9 @@
package org.sufficientlysecure.keychain.ui.dialog;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
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;
@@ -36,7 +36,8 @@ import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.EditText;
-import android.widget.ImageButton;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
// TODO: return result from file manager activity to this dialog! not the activity!
// do it like in ImportFileFragment!
@@ -55,6 +56,10 @@ public class FileDialogFragment extends DialogFragment {
private Messenger mMessenger;
+ private EditText mFilename;
+ private BootstrapButton mBrowse;
+ private CheckBox mCheckBox;
+
/**
* Creates new instance of this file dialog fragment
*/
@@ -90,10 +95,6 @@ public class FileDialogFragment extends DialogFragment {
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);
@@ -105,13 +106,13 @@ public class FileDialogFragment extends DialogFragment {
mFilename = (EditText) view.findViewById(R.id.input);
mFilename.setText(defaultFile);
- mBrowse = (ImageButton) view.findViewById(R.id.btn_browse);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.btn_browse);
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// only .asc or .gpg files
- // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc or gpg types!
- FileHelper.openFile(activity, mFilename.getText().toString(), "*/*",
- requestCode);
+ // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc
+ // or gpg types!
+ FileHelper.openFile(activity, mFilename.getText().toString(), "*/*", requestCode);
}
});
@@ -196,4 +197,3 @@ public class FileDialogFragment extends DialogFragment {
}
}
}
-
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index aba7e974e..e88271240 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -141,7 +141,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.passphrase, null);
+ View view = inflater.inflate(R.layout.passphrase_dialog, null);
alert.setView(view);
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index 797b28829..d5c366bed 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -90,7 +90,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
alert.setMessage(R.string.enter_passphrase_twice);
LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.passphrase_repeat, null);
+ View view = inflater.inflate(R.layout.passphrase_repeat_dialog, null);
alert.setView(view);
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
new file mode 100644
index 000000000..03e09cdcb
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/dialog/ShareNfcDialogFragment.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.dialog;
+
+import org.sufficientlysecure.htmltextview.HtmlTextView;
+import org.sufficientlysecure.keychain.R;
+
+import android.annotation.TargetApi;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.nfc.NfcAdapter;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class ShareNfcDialogFragment extends DialogFragment {
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static ShareNfcDialogFragment newInstance() {
+ ShareNfcDialogFragment frag = new ShareNfcDialogFragment();
+
+ return frag;
+ }
+
+ /**
+ * Creates dialog
+ */
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final FragmentActivity activity = getActivity();
+
+ AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+
+ alert.setIcon(android.R.drawable.ic_dialog_info);
+ alert.setTitle(R.string.share_nfc_dialog);
+ alert.setCancelable(true);
+
+ alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ dismiss();
+ }
+ });
+
+ HtmlTextView textView = new HtmlTextView(getActivity());
+ textView.setPadding(8, 8, 8, 8);
+ alert.setView(textView);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ textView.setText(getString(R.string.error) + ": "
+ + getString(R.string.error_jelly_bean_needed));
+ } else {
+ // check if NFC Adapter is available
+ NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
+ if (nfcAdapter == null) {
+ textView.setText(getString(R.string.error) + ": "
+ + getString(R.string.error_nfc_needed));
+ } else {
+ // nfc works...
+ textView.setHtmlFromRawResource(getActivity(), R.raw.nfc_beam_share);
+
+ alert.setNegativeButton(R.string.menu_beam_preferences,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ Intent intentSettings = new Intent(
+ Settings.ACTION_NFCSHARING_SETTINGS);
+ startActivity(intentSettings);
+ }
+ });
+ }
+ }
+
+ // no flickering when clicking textview for Android < 4
+ // aboutTextView.setTextColor(getResources().getColor(android.R.color.black));
+
+ return alert.create();
+ }
+} \ 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
deleted file mode 100644
index 158a271bc..000000000
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/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.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/FixedListView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
new file mode 100644
index 000000000..277f14b6f
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/FixedListView.java
@@ -0,0 +1,55 @@
+package org.sufficientlysecure.keychain.ui.widget;
+
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * Automatically calculate height of listview based on contained items. This enables to put this
+ * ListView into a ScrollView without messing up.
+ *
+ * from
+ * http://stackoverflow.com/questions/2419246/how-do-i-create-a-listview-thats-not-in-a-scrollview-
+ * or-has-the-scrollview-dis
+ */
+public class FixedListView extends ListView {
+
+ public FixedListView(Context context) {
+ super(context);
+ }
+
+ public FixedListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public FixedListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Calculate height of the entire list by providing a very large
+ // height hint. But do not use the highest 2 bits of this integer;
+ // those are reserved for the MeasureSpec mode.
+ int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
+ super.onMeasure(widthMeasureSpec, expandSpec);
+ }
+
+} \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index 5748839bc..0f5d26644 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -16,12 +16,18 @@
package org.sufficientlysecure.keychain.ui.widget;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Vector;
+
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Choice;
-import org.sufficientlysecure.keychain.R;
import android.app.DatePickerDialog;
import android.app.Dialog;
@@ -32,18 +38,12 @@ 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;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private PGPSecretKey mKey;
@@ -51,12 +51,12 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
private boolean mIsMasterKey;
- ImageButton mDeleteButton;
+ BootstrapButton mDeleteButton;
TextView mAlgorithm;
TextView mKeyId;
Spinner mUsage;
TextView mCreationDate;
- Button mExpiryDateButton;
+ BootstrapButton mExpiryDateButton;
GregorianCalendar mExpiryDate;
private int mDatePickerResultCount = 0;
@@ -87,7 +87,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mAlgorithm = (TextView) findViewById(R.id.algorithm);
mKeyId = (TextView) findViewById(R.id.keyId);
mCreationDate = (TextView) findViewById(R.id.creation);
- mExpiryDateButton = (Button) findViewById(R.id.expiry);
+ mExpiryDateButton = (BootstrapButton) findViewById(R.id.expiry);
mUsage = (Spinner) findViewById(R.id.usage);
Choice choices[] = {
new Choice(Id.choice.usage.sign_only, getResources().getString(
@@ -101,7 +101,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mUsage.setAdapter(adapter);
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
setExpiryDate(null);
@@ -118,16 +118,18 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
date.get(Calendar.DAY_OF_MONTH));
mDatePickerResultCount = 0;
dialog.setCancelable(true);
- dialog.setButton(Dialog.BUTTON_NEGATIVE, getContext()
- .getString(R.string.btn_no_date), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- if (mDatePickerResultCount++ == 0) // Note: Ignore results after the first
- // one - android sends multiples.
- {
- setExpiryDate(null);
- }
- }
- });
+ dialog.setButton(Dialog.BUTTON_NEGATIVE,
+ getContext().getString(R.string.btn_no_date),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ if (mDatePickerResultCount++ == 0) // Note: Ignore results after the
+ // first
+ // one - android sends multiples.
+ {
+ setExpiryDate(null);
+ }
+ }
+ });
dialog.show();
}
});
@@ -237,7 +239,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private void setExpiryDate(GregorianCalendar date) {
mExpiryDate = date;
if (date == null) {
- mExpiryDateButton.setText(R.string.none);
+ mExpiryDateButton.setText(getContext().getString(R.string.none));
} else {
mExpiryDateButton.setText(DateFormat.getDateInstance().format(date.getTime()));
}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
index 5fdb2c9c8..01259ccd1 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
@@ -23,14 +23,15 @@ 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;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
public class KeyServerEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
- ImageButton mDeleteButton;
+ BootstrapButton mDeleteButton;
TextView mServer;
public KeyServerEditor(Context context) {
@@ -48,7 +49,7 @@ public class KeyServerEditor extends LinearLayout implements Editor, OnClickList
mServer = (TextView) findViewById(R.id.server);
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
super.onFinishInflate();
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java
index 99190e9c7..b33dbe4c5 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -45,17 +45,16 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragmentActivity;
+import com.beardedhen.androidbootstrap.BootstrapButton;
public class SectionView extends LinearLayout implements OnClickListener, EditorListener {
private LayoutInflater mInflater;
- private View mAdd;
- private ImageView mPlusButton;
+ private BootstrapButton mPlusButton;
private ViewGroup mEditors;
private TextView mTitle;
private int mType = 0;
@@ -103,7 +102,6 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
public void setCanEdit(boolean bCanEdit) {
canEdit = bCanEdit;
- mPlusButton = (ImageView) findViewById(R.id.plusbutton);
if (!canEdit) {
mPlusButton.setVisibility(View.INVISIBLE);
}
@@ -117,8 +115,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
setDrawingCacheEnabled(true);
setAlwaysDrawnWithCacheEnabled(true);
- mAdd = findViewById(R.id.header);
- mAdd.setOnClickListener(this);
+ mPlusButton = (BootstrapButton) findViewById(R.id.plusbutton);
+ mPlusButton.setOnClickListener(this);
mEditors = (ViewGroup) findViewById(R.id.editors);
mTitle = (TextView) findViewById(R.id.title);
@@ -155,7 +153,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
case Id.type.key: {
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
- View view = mInflater.inflate(R.layout.create_key, null);
+ View view = mInflater.inflate(R.layout.create_key_dialog, null);
dialog.setView(view);
dialog.setTitle(R.string.title_create_key);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
new file mode 100644
index 000000000..752d44f89
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UnderlineTextView.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 Eric Frohnhoefer
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ * Copied from StickyListHeaders lib example
+ *
+ * @author Eric Frohnhoefer
+ */
+public class UnderlineTextView extends TextView {
+ private final Paint mPaint = new Paint();
+ private int mUnderlineHeight = 0;
+
+ public UnderlineTextView(Context context) {
+ this(context, null);
+ }
+
+ public UnderlineTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public UnderlineTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ init(context, attrs);
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ Resources r = getResources();
+ mUnderlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2,
+ r.getDisplayMetrics());
+ }
+
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ super.setPadding(left, top, right, bottom + mUnderlineHeight);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ // Draw the underline the same color as the text
+ mPaint.setColor(getTextColors().getDefaultColor());
+ canvas.drawRect(0, getHeight() - mUnderlineHeight, getWidth(), getHeight(), mPaint);
+ }
+}
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
index 6c65e840c..5428b626e 100644
--- a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
@@ -27,14 +27,15 @@ 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;
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
- private ImageButton mDeleteButton;
+ private BootstrapButton mDeleteButton;
private RadioButton mIsMainUserId;
private EditText mName;
private EditText mEmail;
@@ -95,7 +96,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
setDrawingCacheEnabled(true);
setAlwaysDrawnWithCacheEnabled(true);
- mDeleteButton = (ImageButton) findViewById(R.id.delete);
+ mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
mIsMainUserId = (RadioButton) findViewById(R.id.isMainUserId);
mIsMainUserId.setOnClickListener(this);
diff --git a/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java
new file mode 100644
index 000000000..0d4092d9e
--- /dev/null
+++ b/OpenPGP-Keychain/src/org/sufficientlysecure/keychain/util/SectionCursorAdapter.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2011 Gonçalo Ferreira
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR 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.LinkedHashMap;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.support.v4.widget.CursorAdapter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+/**
+ * Originally from https://github.com/monxalo/android-section-adapter
+ *
+ * getCustomGroup() has been modified
+ */
+public abstract class SectionCursorAdapter extends CursorAdapter {
+
+ private static final String TAG = "SectionCursorAdapter";
+ private static final boolean LOGV = false;
+
+ private static final int TYPE_NORMAL = 1;
+ private static final int TYPE_HEADER = 0;
+ private static final int TYPE_COUNT = 2;
+
+ private final int mHeaderRes;
+ private final int mGroupColumn;
+ private final LayoutInflater mLayoutInflater;
+
+ private LinkedHashMap<Integer, String> sectionsIndexer;
+
+ public static class ViewHolder {
+ public TextView textView;
+ }
+
+ public SectionCursorAdapter(Context context, Cursor c, int headerLayout, int groupColumn) {
+ super(context, c, 0);
+
+ sectionsIndexer = new LinkedHashMap<Integer, String>();
+
+ mHeaderRes = headerLayout;
+ mGroupColumn = groupColumn;
+ mLayoutInflater = LayoutInflater.from(context);
+
+ if (c != null) {
+ calculateSectionHeaders();
+ c.registerDataSetObserver(mDataSetObserver);
+ }
+ }
+
+ private DataSetObserver mDataSetObserver = new DataSetObserver() {
+ public void onChanged() {
+ calculateSectionHeaders();
+ };
+
+ public void onInvalidated() {
+ sectionsIndexer.clear();
+ };
+ };
+
+ /**
+ * <p>
+ * This method serve as an intercepter before the sections are calculated so you can transform
+ * some computer data into human readable, e.g. format a unix timestamp, or a status.
+ * </p>
+ *
+ * <p>
+ * By default this method returns the original data for the group column.
+ * </p>
+ *
+ * @param groupData
+ * @return
+ */
+ protected String getCustomGroup(String groupData) {
+ return groupData.substring(0, 1);
+ }
+
+ private void calculateSectionHeaders() {
+ int i = 0;
+
+ String previous = "";
+ int count = 0;
+
+ final Cursor c = getCursor();
+
+ sectionsIndexer.clear();
+
+ if (c == null) {
+ return;
+ }
+
+ c.moveToPosition(-1);
+
+ while (c.moveToNext()) {
+ final String group = getCustomGroup(c.getString(mGroupColumn));
+
+ if (!previous.equals(group)) {
+ sectionsIndexer.put(i + count, group);
+ previous = group;
+
+ if (LOGV)
+ Log.v(TAG, "Group " + group + "at position: " + (i + count));
+
+ count++;
+ }
+
+ i++;
+ }
+ }
+
+ public String getGroupCustomFormat(Object obj) {
+ return null;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ int viewType = getItemViewType(position);
+
+ if (viewType == TYPE_NORMAL) {
+ Cursor c = (Cursor) getItem(position);
+
+ if (c == null) {
+ if (LOGV)
+ Log.d(TAG, "getItem(" + position + ") = null");
+ return mLayoutInflater.inflate(mHeaderRes, parent, false);
+ }
+
+ final int mapCursorPos = getSectionForPosition(position);
+ c.moveToPosition(mapCursorPos);
+
+ return super.getView(mapCursorPos, convertView, parent);
+ } else {
+ ViewHolder holder = null;
+
+ if (convertView == null) {
+ if (LOGV)
+ Log.v(TAG, "Creating new view for section");
+
+ holder = new ViewHolder();
+ convertView = mLayoutInflater.inflate(mHeaderRes, parent, false);
+ holder.textView = (TextView) convertView;
+
+ convertView.setTag(holder);
+ } else {
+ if (LOGV)
+ Log.v(TAG, "Reusing view for section");
+
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ TextView sectionText = holder.textView;
+
+ final String group = sectionsIndexer.get(position);
+ final String customFormat = getGroupCustomFormat(group);
+
+ sectionText.setText(customFormat == null ? group : customFormat);
+
+ return sectionText;
+ }
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return TYPE_COUNT;
+ }
+
+ @Override
+ public int getCount() {
+ return super.getCount() + sectionsIndexer.size();
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return getItemViewType(position) == TYPE_NORMAL;
+ }
+
+ public int getPositionForSection(int section) {
+ if (sectionsIndexer.containsKey(section)) {
+ return section + 1;
+ }
+ return section;
+ }
+
+ public int getSectionForPosition(int position) {
+ int offset = 0;
+ for (Integer key : sectionsIndexer.keySet()) {
+ if (position > key) {
+ offset++;
+ } else {
+ break;
+ }
+ }
+
+ return position - offset;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (getItemViewType(position) == TYPE_NORMAL) {
+ return super.getItem(getSectionForPosition(position));
+ }
+ return super.getItem(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (getItemViewType(position) == TYPE_NORMAL) {
+ return super.getItemId(getSectionForPosition(position));
+ }
+ return super.getItemId(position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == getPositionForSection(position)) {
+ return TYPE_NORMAL;
+ }
+ return TYPE_HEADER;
+ }
+
+ @Override
+ public void changeCursor(Cursor cursor) {
+ final Cursor old = swapCursor(cursor);
+
+ if (old != null) {
+ old.close();
+ }
+ }
+
+ @Override
+ public Cursor swapCursor(Cursor newCursor) {
+ if (getCursor() != null) {
+ getCursor().unregisterDataSetObserver(mDataSetObserver);
+ }
+
+ final Cursor oldCursor = super.swapCursor(newCursor);
+
+ calculateSectionHeaders();
+
+ if (newCursor != null) {
+ newCursor.registerDataSetObserver(mDataSetObserver);
+ }
+
+ return oldCursor;
+ }
+} \ No newline at end of file
diff --git a/README.md b/README.md
index 601d4710c..df2e72850 100644
--- a/README.md
+++ b/README.md
@@ -11,14 +11,14 @@ Translations are managed at Transifex, please contribute there at https://www.tr
## Code Contributions
-Fork OpenPGP Keychain and do a pull request. I will help with occuring problems and merge your changes back into the main project.
+Fork OpenPGP Keychain and create a pull request. I will help with occuring problems and merge your changes back into the main project.
I am happy about every code contribution and appreciate your effort to help us developing OpenPGP Keychain :)
## Build with Gradle
1. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html)
-2. Export ANDROID_HOME pointing to your Android SDK
-3. Download Android Support Repository, and Google Repository using Android SDK Manager
+2. Open the Android SDK Manager (shell command: ``android``). Expand the Extras directory and install "Android Support Repository"
+3. Export ANDROID_HOME pointing to your Android SDK
4. Execute ``./gradlew build``
## Development with Eclipse
@@ -27,9 +27,11 @@ Android Studio is currently not supported or recommended!
1. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/ActionBarSherlock"
2. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/HtmlTextView"
-3. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/pinned-section-listview/library"
-4. File -> Import -> Android -> Existing Android Code Into Workspace, choose "OpenPGP-Keychain"
-5. OpenPGP-Kechain can now be build
+3. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/StickyListHeaders/library"
+4. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/zxing"
+5. File -> Import -> Android -> Existing Android Code Into Workspace, choose "libraries/AndroidBootstrap"
+6. File -> Import -> Android -> Existing Android Code Into Workspace, choose "OpenPGP-Keychain"
+7. OpenPGP-Kechain can now be build
# Keychain API
@@ -107,13 +109,6 @@ TODO
# Libraries
-All JAR-Libraries are provided in this repository under "libs", all Android Library projects are under "libraries".
-
-* ActionBarSherlock to provide an ActionBar for Android < 3.0
-* HtmlTextView for non-crashing TextViews with HTML content
-* forked Spongy Castle Crypto Lib (Android version of Bouncy Castle)
-* barcodescanner-android-integration-supportv4.jar: Barcode Scanner Integration
-
## Build Barcode Scanner Integration
1. Checkout their SVN (see http://code.google.com/p/zxing/source/checkout)
@@ -124,6 +119,10 @@ All JAR-Libraries are provided in this repository under "libs", all Android Libr
On error see: http://code.google.com/p/zxing/issues/detail?id=1207
+## ZXing
+
+ZXing classes were extracted from the ZXing library (http://code.google.com/p/zxing/).
+Only classes related to QR Code generation are utilized.
## Bouncy Castle
@@ -208,13 +207,17 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens
https://github.com/dschuermann/html-textview
Apache License v2
-* ZXing QRCode Integration
+* ZXing
http://code.google.com/p/zxing/
Apache License v2
-* pinned-section-listview
- https://github.com/beworker/pinned-section-listview
+* StickyListHeaders
+ https://github.com/emilsjolander/StickyListHeaders
Apache License v2
+
+* Android-Bootstrap
+ https://github.com/Bearded-Hen/Android-Bootstrap
+ MIT License
## Images
diff --git a/libraries/ActionBarSherlock/build.gradle b/libraries/ActionBarSherlock/build.gradle
index 41bbe1d9b..e25786733 100644
--- a/libraries/ActionBarSherlock/build.gradle
+++ b/libraries/ActionBarSherlock/build.gradle
@@ -1,7 +1,7 @@
apply plugin: 'android-library'
dependencies {
- compile 'com.android.support:support-v4:18.0.+'
+ compile 'com.android.support:support-v4:19.0.+'
}
android {
diff --git a/libraries/ActionBarSherlock/libs/android-support-v4.jar b/libraries/ActionBarSherlock/libs/android-support-v4.jar
index 99e063b33..9056828a0 100644
--- a/libraries/ActionBarSherlock/libs/android-support-v4.jar
+++ b/libraries/ActionBarSherlock/libs/android-support-v4.jar
Binary files differ
diff --git a/libraries/AndroidBootstrap/AndroidManifest.xml b/libraries/AndroidBootstrap/AndroidManifest.xml
new file mode 100644
index 000000000..ee1013229
--- /dev/null
+++ b/libraries/AndroidBootstrap/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.beardedhen.androidbootstrap"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="7"
+ android:targetSdkVersion="17" />
+
+ <application/>
+
+</manifest>
diff --git a/libraries/AndroidBootstrap/build.gradle b/libraries/AndroidBootstrap/build.gradle
new file mode 100644
index 000000000..a63f9e02c
--- /dev/null
+++ b/libraries/AndroidBootstrap/build.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion "19.0.0"
+ defaultConfig {
+ minSdkVersion 7
+ targetSdkVersion 17
+ }
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
diff --git a/libraries/AndroidBootstrap/libs/android-support-v4.jar b/libraries/AndroidBootstrap/libs/android-support-v4.jar
new file mode 100644
index 000000000..9056828a0
--- /dev/null
+++ b/libraries/AndroidBootstrap/libs/android-support-v4.jar
Binary files differ
diff --git a/libraries/AndroidBootstrap/proguard-project.txt b/libraries/AndroidBootstrap/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libraries/AndroidBootstrap/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/libraries/AndroidBootstrap/project.properties b/libraries/AndroidBootstrap/project.properties
new file mode 100644
index 000000000..1b8c5a340
--- /dev/null
+++ b/libraries/AndroidBootstrap/project.properties
@@ -0,0 +1,15 @@
+# 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 edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-18
+android.library=true
diff --git a/libraries/AndroidBootstrap/res/drawable-hdpi/ic_launcher.png b/libraries/AndroidBootstrap/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 000000000..96a442e5b
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/libraries/AndroidBootstrap/res/drawable-mdpi/ic_launcher.png b/libraries/AndroidBootstrap/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 000000000..359047dfa
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/libraries/AndroidBootstrap/res/drawable-xhdpi/ic_launcher.png b/libraries/AndroidBootstrap/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..71c6d760f
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_danger.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_danger.xml
new file mode 100644
index 000000000..4fd748cb4
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_danger.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_danger_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger_edge" />
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_danger_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger_disabled_edge" />
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_danger" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger_edge" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_danger_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_danger_rounded.xml
new file mode 100644
index 000000000..2fe8b2571
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_danger_rounded.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_danger_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_danger_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger_disabled_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_danger" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_default.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_default.xml
new file mode 100644
index 000000000..77318eae0
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_default.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_default_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_default_edge" />
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_default_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_default_disabled_edge" />
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_default" />
+ <stroke android:width="1dp" android:color="@color/bbutton_default_edge" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_default_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_default_rounded.xml
new file mode 100644
index 000000000..923a2b66f
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_default_rounded.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_default_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_default_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_default_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_default_disabled_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_default" />
+ <stroke android:width="1dp" android:color="@color/bbutton_default_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_info.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_info.xml
new file mode 100644
index 000000000..5727e095e
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_info.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_info_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_info_edge" />
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_info_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_info_disabled_edge" />
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_info" />
+ <stroke android:width="1dp" android:color="@color/bbutton_info_edge" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_info_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_info_rounded.xml
new file mode 100644
index 000000000..c171215ef
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_info_rounded.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_info_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_info_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_info_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_info_disabled_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_info" />
+ <stroke android:width="1dp" android:color="@color/bbutton_info_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_inverse.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_inverse.xml
new file mode 100644
index 000000000..bee362b30
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_inverse.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_inverse_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_inverse_edge" />
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_inverse_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_inverse_disabled_edge" />
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_inverse" />
+ <stroke android:width="1dp" android:color="@color/bbutton_inverse_edge" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_inverse_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_inverse_rounded.xml
new file mode 100644
index 000000000..e5ceb1da8
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_inverse_rounded.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_inverse_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_inverse_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_inverse_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_inverse_disabled_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_inverse" />
+ <stroke android:width="1dp" android:color="@color/bbutton_inverse_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_primary.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_primary.xml
new file mode 100644
index 000000000..5e438f37d
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_primary.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_primary_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_primary_edge" />
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_primary_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_primary_disabled_edge" />
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_primary" />
+ <stroke android:width="1dp" android:color="@color/bbutton_primary_edge" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_primary_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_primary_rounded.xml
new file mode 100644
index 000000000..88d08ea5d
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_primary_rounded.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_primary_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_primary_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_primary_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_primary_disabled_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_primary" />
+ <stroke android:width="1dp" android:color="@color/bbutton_primary_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_success.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_success.xml
new file mode 100644
index 000000000..c611ae9ba
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_success.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_success_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success_edge" />
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_success_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success_disabled_edge" />
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_success" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success_edge" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_success_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_success_rounded.xml
new file mode 100644
index 000000000..5536ac0fd
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_success_rounded.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_success_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_success_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success_disabled_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_success" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_warning.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_warning.xml
new file mode 100644
index 000000000..e0596201b
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_warning.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_warning_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning_edge" />
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_warning_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning_disabled_edge" />
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_warning" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning_edge" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bbuton_warning_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bbuton_warning_rounded.xml
new file mode 100644
index 000000000..ef0220d60
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bbuton_warning_rounded.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"><shape>
+ <solid android:color="@color/bbutton_warning_pressed" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_warning_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning_disabled_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+
+ <item><shape>
+ <solid android:color="@color/bbutton_warning" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning_edge" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius"/>
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bthumbnail_container_rounded.xml b/libraries/AndroidBootstrap/res/drawable/bthumbnail_container_rounded.xml
new file mode 100644
index 000000000..01d8c00e2
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bthumbnail_container_rounded.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item><shape>
+ <solid android:color="@color/bthumbnail_background" />
+ <stroke android:width="1dp" android:color="@color/bthumbnail_border" />
+ <corners android:radius="@dimen/bthumbnail_rounded_corner_radius"/>
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bthumbnail_container_square.xml b/libraries/AndroidBootstrap/res/drawable/bthumbnail_container_square.xml
new file mode 100644
index 000000000..2c2729085
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bthumbnail_container_square.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item><shape>
+ <solid android:color="@color/bthumbnail_background" />
+ <stroke android:width="1dp" android:color="@color/bthumbnail_border" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/bthumbnail_placeholder_default.xml b/libraries/AndroidBootstrap/res/drawable/bthumbnail_placeholder_default.xml
new file mode 100644
index 000000000..fa0013790
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/bthumbnail_placeholder_default.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item><shape android:shape="rectangle">
+ <solid android:color="@color/bthumbnail_placeholder" />
+
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background.xml
new file mode 100644
index 000000000..f7f58502a
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_primary" />
+ </shape></item>
+<item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_edittext_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_edittext_border" />
+ </shape></item>
+
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_edittext_border" />
+ </shape></item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background_danger.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background_danger.xml
new file mode 100644
index 000000000..dd38089d0
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background_danger.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_danger" />
+ </shape></item>
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger" />
+ </shape></item>
+
+</selector>
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded.xml
new file mode 100644
index 000000000..d3a318fd8
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_primary" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+ <item android:state_enabled="false"><shape>
+ <solid android:color="@color/bbutton_edittext_disabled" />
+ <stroke android:width="1dp" android:color="@color/bbutton_edittext_border" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_edittext_border" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+
+</selector>
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_danger.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_danger.xml
new file mode 100644
index 000000000..ad2d03a5e
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_danger.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_danger" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_danger" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+
+
+
+</selector>
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_success.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_success.xml
new file mode 100644
index 000000000..7ef80a5f5
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_success.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_success" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+
+</selector>
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_warning.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_warning.xml
new file mode 100644
index 000000000..b90c3f96f
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background_rounded_warning.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_warning" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning" />
+ <corners android:radius="@dimen/bbuton_rounded_corner_radius" />
+ </shape></item>
+
+</selector>
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background_success.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background_success.xml
new file mode 100644
index 000000000..8f6af9700
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background_success.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_success" />
+ </shape></item>
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_success" />
+ </shape></item>
+
+</selector>
diff --git a/libraries/AndroidBootstrap/res/drawable/edittext_background_warning.xml b/libraries/AndroidBootstrap/res/drawable/edittext_background_warning.xml
new file mode 100644
index 000000000..0f95154c9
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/edittext_background_warning.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true"><shape>
+ <solid android:color="@color/white" />
+ <stroke android:width="2dp" android:color="@color/bbutton_warning" />
+ </shape></item>
+<item><shape android:shape="rectangle">
+ <solid android:color="@color/white" />
+ <stroke android:width="1dp" android:color="@color/bbutton_warning" />
+ </shape></item>
+
+</selector>
diff --git a/libraries/AndroidBootstrap/res/drawable/thumbnail_circle.xml b/libraries/AndroidBootstrap/res/drawable/thumbnail_circle.xml
new file mode 100644
index 000000000..c8d3ab2ff
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/thumbnail_circle.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/bthumbnail_placeholder"/>
+ </shape>
diff --git a/libraries/AndroidBootstrap/res/drawable/thumbnail_circle_container.xml b/libraries/AndroidBootstrap/res/drawable/thumbnail_circle_container.xml
new file mode 100644
index 000000000..7f9e90d84
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/thumbnail_circle_container.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <shape
+ android:shape="oval">
+ <solid android:color="@color/bthumbnail_background" />
+ <stroke android:width="1dp" android:color="@color/bthumbnail_border" />
+ </shape>
+ </item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/drawable/thumbnail_circle_minimal.xml b/libraries/AndroidBootstrap/res/drawable/thumbnail_circle_minimal.xml
new file mode 100644
index 000000000..80d4c41b4
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/drawable/thumbnail_circle_minimal.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item>
+ <shape
+ android:shape="oval">
+ <solid android:color="@color/bthumbnail_background" />
+ </shape>
+ </item>
+
+</selector> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/layout/bootstrap_button.xml b/libraries/AndroidBootstrap/res/layout/bootstrap_button.xml
new file mode 100644
index 000000000..d6260eaba
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/bootstrap_button.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/layout"
+ android:duplicateParentState="true"
+ android:paddingTop="10dp"
+ android:paddingBottom="10dp"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/lblLeft"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingLeft="15dp"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"
+ />
+
+ <TextView
+ android:id="@+id/lblMiddle"
+ android:layout_height="wrap_content"
+ android:layout_width="fill_parent"
+ android:paddingLeft="15dp"
+ android:paddingRight="15dp"
+ android:layout_gravity="center"
+ android:gravity="center_horizontal"
+ android:visibility="gone"
+ />
+
+ <TextView
+ android:id="@+id/lblRight"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:paddingRight="15dp"
+ android:visibility="gone"
+ android:layout_gravity="center_vertical"
+ />
+
+</LinearLayout>
diff --git a/libraries/AndroidBootstrap/res/layout/bootstrap_button_fill.xml b/libraries/AndroidBootstrap/res/layout/bootstrap_button_fill.xml
new file mode 100644
index 000000000..545f9b68a
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/bootstrap_button_fill.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/layout"
+ android:duplicateParentState="true"
+ android:paddingTop="10dp"
+ android:paddingBottom="10dp">
+
+ <TextView
+ android:id="@+id/lblLeft"
+ android:layout_height="fill_parent"
+ android:layout_width="wrap_content"
+ android:paddingLeft="15dp"
+ android:visibility="gone"
+ android:gravity="center_vertical"
+ android:layout_alignParentLeft="true"
+ />
+
+ <TextView
+ android:id="@+id/lblRight"
+ android:layout_height="fill_parent"
+ android:layout_width="wrap_content"
+ android:paddingRight="15dp"
+ android:visibility="gone"
+ android:gravity="center_vertical"
+ android:layout_alignParentRight="true"
+ />
+
+ <TextView
+ android:id="@+id/lblMiddle"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:paddingLeft="15dp"
+ android:paddingRight="15dp"
+ android:visibility="gone"
+ android:gravity="left|center_vertical"
+ android:layout_toLeftOf="@id/lblRight"
+ android:layout_toRightOf="@id/lblLeft"
+ />
+
+</RelativeLayout>
diff --git a/libraries/AndroidBootstrap/res/layout/bootstrap_edit_text.xml b/libraries/AndroidBootstrap/res/layout/bootstrap_edit_text.xml
new file mode 100644
index 000000000..63b5b007a
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/bootstrap_edit_text.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/layout"
+ android:orientation="horizontal">
+ <EditText
+ android:id="@+id/txtText"
+ android:layout_height="fill_parent"
+ android:layout_width="fill_parent"
+ android:layout_gravity="center"
+ android:hint=""
+ />
+</LinearLayout>
diff --git a/libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail.xml b/libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail.xml
new file mode 100644
index 000000000..fb3b43042
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container"
+ android:duplicateParentState="true"
+ android:padding="@dimen/padding_small"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/placeholder"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="16dp">
+
+ <com.beardedhen.androidbootstrap.utils.AutoResizeTextView
+ android:id="@+id/dimensionsLabel"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:ellipsize="none"
+ android:gravity="center"
+ android:maxLines="1"
+ android:textSize="100sp"
+ android:textColor="@color/bthumbnail_font"/>
+
+
+ </LinearLayout>
+
+
+
+</LinearLayout>
diff --git a/libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail_circle.xml b/libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail_circle.xml
new file mode 100644
index 000000000..2e4394160
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/bootstrap_thumbnail_circle.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container"
+ android:duplicateParentState="true"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/placeholder"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:id="@+id/image"/>
+
+ <com.beardedhen.androidbootstrap.utils.AutoResizeTextView
+ android:id="@+id/dimensionsLabel"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
+ android:ellipsize="none"
+ android:gravity="center"
+ android:maxLines="1"
+ android:textSize="100sp"
+ android:textColor="@color/bthumbnail_font"/>
+
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/layout/font_awesome_text.xml b/libraries/AndroidBootstrap/res/layout/font_awesome_text.xml
new file mode 100644
index 000000000..3fd7aeebc
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/font_awesome_text.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/layout"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/lblText"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:padding="3dp"
+ />
+</LinearLayout>
diff --git a/libraries/AndroidBootstrap/res/layout/row_title.xml b/libraries/AndroidBootstrap/res/layout/row_title.xml
new file mode 100644
index 000000000..dd5a3573a
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/row_title.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="10dp">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/lblTitle"
+ android:gravity="left"
+ />
+</LinearLayout> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/layout/row_title_and_subtitle.xml b/libraries/AndroidBootstrap/res/layout/row_title_and_subtitle.xml
new file mode 100644
index 000000000..ec25c0a46
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/row_title_and_subtitle.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="10dp">
+
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/lblTitle"
+ android:gravity="center"
+ />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:id="@+id/lblSubTitle"
+ android:gravity="center"
+ />
+
+
+
+</LinearLayout> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/layout/row_two_columns.xml b/libraries/AndroidBootstrap/res/layout/row_two_columns.xml
new file mode 100644
index 000000000..1029bffb5
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/layout/row_two_columns.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="10dp">
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/lblColA"
+ android:gravity="center"
+ />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:id="@+id/lblColB"
+ android:gravity="center"
+ />
+
+
+
+</LinearLayout> \ No newline at end of file
diff --git a/libraries/AndroidBootstrap/res/menu/main.xml b/libraries/AndroidBootstrap/res/menu/main.xml
new file mode 100644
index 000000000..c00202823
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/menu/main.xml
@@ -0,0 +1,9 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never"
+ android:title="@string/action_settings"/>
+
+</menu>
diff --git a/libraries/AndroidBootstrap/res/values-sw600dp/dimens.xml b/libraries/AndroidBootstrap/res/values-sw600dp/dimens.xml
new file mode 100644
index 000000000..44f01db75
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values-sw600dp/dimens.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as
+ screen margins) for sw600dp devices (e.g. 7" tablets) here.
+ -->
+
+</resources>
diff --git a/libraries/AndroidBootstrap/res/values-sw720dp-land/dimens.xml b/libraries/AndroidBootstrap/res/values-sw720dp-land/dimens.xml
new file mode 100644
index 000000000..61e3fa8fb
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values-sw720dp-land/dimens.xml
@@ -0,0 +1,9 @@
+<resources>
+
+ <!--
+ Customize dimensions originally defined in res/values/dimens.xml (such as
+ screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here.
+ -->
+ <dimen name="activity_horizontal_margin">128dp</dimen>
+
+</resources>
diff --git a/libraries/AndroidBootstrap/res/values-v11/styles.xml b/libraries/AndroidBootstrap/res/values-v11/styles.xml
new file mode 100644
index 000000000..3c02242ad
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/libraries/AndroidBootstrap/res/values-v14/styles.xml b/libraries/AndroidBootstrap/res/values-v14/styles.xml
new file mode 100644
index 000000000..a91fd0372
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources>
diff --git a/libraries/AndroidBootstrap/res/values/attrs.xml b/libraries/AndroidBootstrap/res/values/attrs.xml
new file mode 100644
index 000000000..2e56a622d
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values/attrs.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <declare-styleable name="BootstrapButton">
+ <attr name="bb_type" format="string"/>
+ <attr name="bb_icon_left" format="string"/>
+ <attr name="bb_icon_right" format="string"/>
+ <attr name="bb_roundedCorners" format="boolean"/>
+ <attr name="bb_text_alignment" format="string"/>
+ <attr name="bb_size" format="string"/>
+ <attr name="bb_text_gravity" format="string"/>
+ <attr name="android:textSize"/>
+ <attr name="android:text"/>
+ <attr name="android:enabled"/>
+ <attr name="android:layout_width"/>
+ </declare-styleable>
+
+ <declare-styleable name="FontAwesomeText">
+ <attr name="fa_icon" format="string"/>
+ <attr name="android:textSize"/>
+ <attr name="android:textColor"/>
+ </declare-styleable>
+
+ <declare-styleable name="BootstrapEditText">
+ <attr name="be_roundedCorners" format="boolean"/>
+ <attr name="be_state" format="string"/>
+ <attr name="android:textSize"/>
+ <attr name="android:text"/>
+ <attr name="android:hint"/>
+ <attr name="android:enabled"/>
+
+ </declare-styleable>
+
+ <declare-styleable name="BootstrapThumbnail">
+ <attr name="bt_roundedCorners" format="boolean"/>
+ <attr name="bt_image" format="integer"/>
+ <attr name="bt_width" format="dimension"/>
+ <attr name="bt_height" format="dimension"/>
+ <attr name="bt_inside_padding" format="dimension"/>
+ <attr name="android:text"/>
+ </declare-styleable>
+
+ <declare-styleable name="BootstrapCircleThumbnail">
+ <attr name="bct_image" format="integer"/>
+ <attr name="bct_size" format="string" />
+ <attr name="bct_minimal" format="boolean" />
+ <attr name="android:text"/>
+ </declare-styleable>
+
+</resources>
+
diff --git a/libraries/AndroidBootstrap/res/values/colors.xml b/libraries/AndroidBootstrap/res/values/colors.xml
new file mode 100644
index 000000000..f0615f3ef
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values/colors.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="white">#ffffffff</color>
+ <color name="black">#ff000000</color>
+ <color name="bbutton_edittext_border">#ccc</color>
+ <color name="bbutton_edittext_disabled">#ffe0e0e0</color>
+
+ <color name="bbutton_primary">#ff428bca</color>
+ <color name="bbutton_primary_edge">#ff357ebd</color>
+ <color name="bbutton_primary_pressed">#ff3276b1</color>
+ <color name="bbutton_primary_pressed_edge">#ff285e8e</color>
+ <color name="bbutton_primary_disabled">#a5428bca</color>
+ <color name="bbutton_primary_disabled_edge">#a5357ebd</color>
+
+ <color name="bbutton_danger">#ffd9534f</color>
+ <color name="bbutton_danger_edge">#ffd43f3a</color>
+ <color name="bbutton_danger_pressed">#ffd2322d</color>
+ <color name="bbutton_danger_pressed_edge">#ffac2925</color>
+ <color name="bbutton_danger_disabled">#a5d9534f</color>
+ <color name="bbutton_danger_disabled_edge">#a5d43f3a</color>
+
+
+ <color name="bbutton_success">#ff5cb85c</color>
+ <color name="bbutton_success_edge">#ff4cae4c</color>
+ <color name="bbutton_success_pressed">#ff47a447</color>
+ <color name="bbutton_success_pressed_edge">#ff398439</color>
+ <color name="bbutton_success_disabled">#a55cb85c</color>
+ <color name="bbutton_success_disabled_edge">#a54cae4c</color>
+
+
+ <color name="bbutton_warning">#fff0ad4e</color>
+ <color name="bbutton_warning_edge">#ffeea236</color>
+ <color name="bbutton_warning_pressed">#ffed9c28</color>
+ <color name="bbutton_warning_pressed_edge">#ffd58512</color>
+ <color name="bbutton_warning_disabled">#a5f0ad4e</color>
+ <color name="bbutton_warning_disabled_edge">#a5eea236</color>
+
+ <color name="bbutton_info">#ff5bc0de</color>
+ <color name="bbutton_info_edge">#ff46b8da</color>
+ <color name="bbutton_info_pressed">#ff39b3d7</color>
+ <color name="bbutton_info_pressed_edge">#ff269abc</color>
+ <color name="bbutton_info_disabled">#a55bc0de</color>
+ <color name="bbutton_info_disabled_edge">#a546b8da</color>
+
+ <color name="bbutton_default">#ffffffff</color>
+ <color name="bbutton_default_edge">#ffcccccc</color>
+ <color name="bbutton_default_pressed">#ffebebeb</color>
+ <color name="bbutton_default_pressed_edge">#ffadadad</color>
+ <color name="bbutton_default_disabled">#a5ffffff</color>
+ <color name="bbutton_default_disabled_edge">#a5cccccc</color>
+
+ <color name="bbutton_inverse">#ff0a0a0a</color>
+ <color name="bbutton_inverse_edge">#ff141414</color>
+ <color name="bbutton_inverse_pressed">#ff1f1f1f</color>
+ <color name="bbutton_inverse_pressed_edge">#ff292929</color>
+ <color name="bbutton_inverse_disabled">#a50a0a0a</color>
+ <color name="bbutton_inverse_disabled_edge">#a5141414</color>
+
+ <color name="bthumbnail_background">#ffffffff</color>
+ <color name="bthumbnail_placeholder">#e9e9e9</color>
+ <color name="bthumbnail_border">#dbdbdb</color>
+ <color name="bthumbnail_font">#7e7e7e</color>
+
+
+</resources>
diff --git a/libraries/AndroidBootstrap/res/values/dimens.xml b/libraries/AndroidBootstrap/res/values/dimens.xml
new file mode 100644
index 000000000..ed16a8abe
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values/dimens.xml
@@ -0,0 +1,14 @@
+<resources>
+
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="bbuton_rounded_corner_radius">5dp</dimen>
+
+ <dimen name="bthumbnail_rounded_corner_radius">3dp</dimen>
+
+ <dimen name="padding_micro">4dp</dimen>
+ <dimen name="padding_small">8dp</dimen>
+ <dimen name="padding_medium">12dp</dimen>
+ <dimen name="padding_large">16dp</dimen>
+</resources>
diff --git a/libraries/AndroidBootstrap/res/values/strings.xml b/libraries/AndroidBootstrap/res/values/strings.xml
new file mode 100644
index 000000000..69aff74d5
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">BButton</string>
+ <string name="action_settings">Settings</string>
+ <string name="hello_world">Hello world!</string>
+
+</resources>
diff --git a/libraries/AndroidBootstrap/res/values/styles.xml b/libraries/AndroidBootstrap/res/values/styles.xml
new file mode 100644
index 000000000..150753361
--- /dev/null
+++ b/libraries/AndroidBootstrap/res/values/styles.xml
@@ -0,0 +1,16 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+
+</resources>
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapButton.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapButton.java
new file mode 100644
index 000000000..374d004a8
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapButton.java
@@ -0,0 +1,445 @@
+package com.beardedhen.androidbootstrap;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.R;
+
+
+public class BootstrapButton extends FrameLayout {
+
+ private static Map<String, BootstrapTypes> bbuttonTypeMap;
+ private static Map<String, BootstrapTypes> bbuttonTypeMapRounded;
+ private static Typeface font;
+
+ private static Map<String, String> faMap;
+
+ private TextView lblMiddle;
+ private TextView lblRight;
+ private TextView lblLeft;
+ private ViewGroup layout;
+ private boolean roundedCorners = false;
+ private boolean fillparent = false;
+
+ private static final String FA_ICON_QUESTION = "fa-question";
+
+ static{
+
+ bbuttonTypeMap = new HashMap<String, BootstrapTypes>();
+
+ bbuttonTypeMap.put("default", BootstrapTypes.DEFAULT);
+ bbuttonTypeMap.put("primary", BootstrapTypes.PRIMARY);
+ bbuttonTypeMap.put("success", BootstrapTypes.SUCCESS);
+ bbuttonTypeMap.put("info", BootstrapTypes.INFO);
+ bbuttonTypeMap.put("warning", BootstrapTypes.WARNING);
+ bbuttonTypeMap.put("danger", BootstrapTypes.DANGER);
+ bbuttonTypeMap.put("inverse", BootstrapTypes.INVERSE);
+
+ bbuttonTypeMapRounded = new HashMap<String, BootstrapTypes>();
+
+ bbuttonTypeMapRounded.put("default", BootstrapTypes.DEFAULT_ROUNDED);
+ bbuttonTypeMapRounded.put("primary", BootstrapTypes.PRIMARY_ROUNDED);
+ bbuttonTypeMapRounded.put("success", BootstrapTypes.SUCCESS_ROUNDED);
+ bbuttonTypeMapRounded.put("info", BootstrapTypes.INFO_ROUNDED);
+ bbuttonTypeMapRounded.put("warning", BootstrapTypes.WARNING_ROUNDED);
+ bbuttonTypeMapRounded.put("danger", BootstrapTypes.DANGER_ROUNDED);
+ bbuttonTypeMapRounded.put("inverse", BootstrapTypes.INVERSE_ROUNDED);
+
+
+ faMap = FontAwesome.getFaMap();
+
+ }
+
+ public BootstrapButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initialise(attrs);
+ }
+
+ public BootstrapButton(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialise(attrs);
+ }
+
+ public BootstrapButton(Context context) {
+ super(context);
+ initialise(null);
+ }
+
+ //set up the bootstrap types
+ private enum BootstrapTypes
+ {
+ DEFAULT(R.drawable.bbuton_default, R.color.black),
+ PRIMARY(R.drawable.bbuton_primary, R.color.white),
+ SUCCESS(R.drawable.bbuton_success, R.color.white),
+ INFO(R.drawable.bbuton_info, R.color.white),
+ WARNING(R.drawable.bbuton_warning, R.color.white),
+ DANGER(R.drawable.bbuton_danger, R.color.white),
+ INVERSE(R.drawable.bbuton_inverse, R.color.white),
+
+ DEFAULT_ROUNDED(R.drawable.bbuton_default_rounded, R.color.black),
+ PRIMARY_ROUNDED(R.drawable.bbuton_primary_rounded, R.color.white),
+ SUCCESS_ROUNDED(R.drawable.bbuton_success_rounded, R.color.white),
+ INFO_ROUNDED(R.drawable.bbuton_info_rounded, R.color.white),
+ WARNING_ROUNDED(R.drawable.bbuton_warning_rounded, R.color.white),
+ DANGER_ROUNDED(R.drawable.bbuton_danger_rounded, R.color.white),
+ INVERSE_ROUNDED(R.drawable.bbuton_inverse_rounded, R.color.white);
+
+ private int backgroundDrawable;
+ private int textColour;
+
+ BootstrapTypes(int backgroundDrawable, int textColour)
+ {
+ this.backgroundDrawable = backgroundDrawable;
+ this.textColour = textColour;
+ }
+ }
+
+
+ private void initialise( AttributeSet attrs )
+ {
+ LayoutInflater inflator = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ //get font
+ readFont(getContext());
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.BootstrapButton);
+
+ //defaults
+ BootstrapTypes type = null;
+ String bootstrapType = "default";
+ String iconLeft = "";
+ String iconRight = "";
+ String text = "";
+ //boolean roundedCorners = false;
+ float fontSize = 14.0f;
+ float scale = getResources().getDisplayMetrics().density; //for padding
+ String size = "default";
+ int paddingA = (int) (10 *scale + 0.5f);
+ int paddingB = (int) (15 *scale + 0.5f);
+
+
+ //attribute values
+
+ if (a.getString(R.styleable.BootstrapButton_bb_type) != null) {
+ bootstrapType = a.getString(R.styleable.BootstrapButton_bb_type);
+ }
+
+ if (a.getString(R.styleable.BootstrapButton_bb_roundedCorners) != null) {
+ roundedCorners = a.getBoolean(R.styleable.BootstrapButton_bb_roundedCorners, false) ;
+ }
+
+ if(a.getString(R.styleable.BootstrapButton_bb_size) != null) {
+ size = a.getString(R.styleable.BootstrapButton_bb_size);
+ }
+
+ if ( a.getString(R.styleable.BootstrapButton_bb_icon_left) != null) {
+ iconLeft = a.getString(R.styleable.BootstrapButton_bb_icon_left );
+ }
+
+ if(a.getString(R.styleable.BootstrapButton_bb_icon_right) != null) {
+ iconRight = a.getString(R.styleable.BootstrapButton_bb_icon_right );
+ }
+
+ if(a.getString(R.styleable.BootstrapButton_android_text) != null) {
+ text = a.getString(R.styleable.BootstrapButton_android_text);
+ }
+ String gravity = "";
+ if(a.getString(R.styleable.BootstrapButton_bb_text_gravity) != null) {
+ gravity = a.getString(R.styleable.BootstrapButton_bb_text_gravity);
+ }
+
+ boolean enabled = true;
+ if(a.getString(R.styleable.BootstrapButton_android_enabled) != null) {
+ enabled = a.getBoolean(R.styleable.BootstrapButton_android_enabled, true);
+ }
+
+ int layoutWidth = 0;
+ if(a.getString(R.styleable.BootstrapButton_android_layout_width) != null) {
+ layoutWidth = a.getInt(R.styleable.BootstrapButton_android_layout_width, 0);
+ }
+
+ //works even if it's fill_parent or match_parent
+ if( (layoutWidth == LayoutParams.MATCH_PARENT)) {
+ fillparent = true;
+ }
+
+ if(a.getString(R.styleable.BootstrapButton_android_textSize) != null) {
+
+ //font sizes
+ String xmlProvidedSize = attrs.getAttributeValue(
+ "http://schemas.android.com/apk/res/android", "textSize");
+ final Pattern PATTERN_FONT_SIZE = Pattern
+ .compile("([0-9]+[.]?[0-9]*)sp");
+ Matcher m = PATTERN_FONT_SIZE.matcher(xmlProvidedSize);
+
+ if (m.find()) {
+
+ if (m.groupCount() == 1) {
+
+ fontSize = Float.valueOf(m.group(1));
+ }
+
+ }
+
+ }
+
+ a.recycle();
+ View v = null;
+ if(fillparent){
+ v = inflator.inflate(R.layout.bootstrap_button_fill, null, false);
+ } else {
+ v = inflator.inflate(R.layout.bootstrap_button, null, false);
+ }
+
+
+ //set up font sizes and padding for different button sizes
+ if(size.equals("large")){
+ fontSize = 20.0f;
+ paddingA = (int) (15 *scale + 0.5f);;
+ paddingB = (int) (20 *scale + 0.5f);;
+ }
+
+ if(size.equals("small")){
+ fontSize = 12.0f;
+ paddingA = (int) (5 *scale + 0.5f);;
+ paddingB = (int) (10 *scale + 0.5f);;
+ }
+
+ if(size.equals("xsmall")){
+ fontSize = 10.0f;
+ paddingA = (int) (2 *scale + 0.5f);;
+ paddingB = (int) (5 *scale + 0.5f);;
+ }
+
+ //get layout items
+ layout = (ViewGroup) v.findViewById(R.id.layout);
+ lblLeft = (TextView) v.findViewById(R.id.lblLeft);
+ lblMiddle = (TextView) v.findViewById(R.id.lblMiddle);
+ lblRight = (TextView) v.findViewById(R.id.lblRight);
+
+ //set the background
+ //setBootstrapType(bootstrapType);
+
+ //get the correct background type
+ if(roundedCorners == true)
+ {
+ type = bbuttonTypeMapRounded.get(bootstrapType);
+ } else {
+ type = bbuttonTypeMap.get(bootstrapType);
+ }
+
+ //set up as default
+ if (type == null)
+ {
+ type = BootstrapTypes.DEFAULT;
+ }
+
+ //apply the background type
+ layout.setBackgroundResource(type.backgroundDrawable);
+ lblLeft.setTextColor(getResources().getColor(type.textColour));
+ lblMiddle.setTextColor(getResources().getColor(type.textColour));
+ lblRight.setTextColor(getResources().getColor(type.textColour));
+
+ //set the font awesome icon typeface
+ lblLeft.setTypeface(font);
+ lblRight.setTypeface(font);
+
+ //set up the font size
+ lblLeft.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
+ lblMiddle.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
+ lblRight.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
+
+ //deal with gravity
+
+ if(gravity.length() > 0) {
+ setTextGravity(gravity);
+ }
+
+
+ boolean onlyIcon = true;
+
+ //set the text
+ if(text.length() > 0){
+ lblMiddle.setText(text );
+ lblMiddle.setVisibility(View.VISIBLE);
+ onlyIcon = false;
+ }
+
+ //set up the padding
+
+ if (iconLeft.length() > 0) {
+ //lblLeft.setText(iconLeft);
+ setLeftIcon(iconLeft);
+ lblLeft.setVisibility(View.VISIBLE);
+
+ if (onlyIcon == false){
+ lblLeft.setPadding(paddingB, 0, 0, 0);
+ } else {
+ lblLeft.setPadding(paddingB, 0, paddingB, 0);
+ }
+
+ //padding for symmetry
+ if ( ( iconRight.length() == 0) && onlyIcon == false ) {
+ lblMiddle.setPadding(paddingA, 0, (int) paddingB, 0);
+ }
+
+ }
+
+ if (iconRight.length() > 0) {
+ //lblRight.setText(iconRight);
+ setRightIcon(iconRight);
+ lblRight.setVisibility(View.VISIBLE);
+
+ if (onlyIcon == false){
+ lblRight.setPadding(0, 0, paddingB, 0);
+ }else {
+ lblRight.setPadding(paddingB, 0, paddingB, 0);
+ }
+
+ //padding for symmetry
+ if ( (iconLeft.length() == 0) && onlyIcon == false ) {
+ lblMiddle.setPadding(paddingB, 0, (int) paddingA, 0);
+ }
+ }
+
+ if(iconLeft.length() > 0 && iconRight.length() > 0 )
+ {
+ lblMiddle.setPadding(paddingA, 0, paddingA, 0);
+ }
+ this.setClickable(true);
+
+ this.setEnabled(enabled);
+
+ layout.setPadding(0, paddingB, 0, paddingB);
+
+ addView(v);
+ }
+
+ //static class to read in font
+ private static void readFont(Context context)
+ {
+
+ if(font == null){
+ try {
+ font = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
+ } catch (Exception e) {
+ Log.e("BootstrapButton", "Could not get typeface because " + e.getMessage());
+ font = Typeface.DEFAULT;
+ }
+ }
+
+ }
+
+
+ /**
+ * Changes the button text
+ * @param text - String value for what is displayed on the button
+ */
+ public void setText(String text) {
+ lblMiddle.setText(text);
+ }
+
+
+ /**
+ * Changes the left icon on a BootstrapButton
+ * @param leftIcon- String value for the icon as per http://fortawesome.github.io/Font-Awesome/cheatsheet/
+ */
+ public void setLeftIcon(String leftIcon) {
+
+ String icon = faMap.get(leftIcon);
+
+ if (icon == null)
+ {
+ icon = faMap.get(FA_ICON_QUESTION);
+ }
+
+ lblLeft.setText(icon);
+ }
+
+ /**
+ * Changes the right icon on a BootstrapButton
+ * @param rightIcon - String value for the icon as per http://fortawesome.github.io/Font-Awesome/cheatsheet/
+ */
+ public void setRightIcon(String rightIcon) {
+
+ String icon = faMap.get(rightIcon);
+
+ if (icon == null)
+ {
+ icon = faMap.get(FA_ICON_QUESTION);
+ }
+
+ lblRight.setText(icon);
+
+ }
+
+ /**
+ * Changes the type of BootstrapButton
+ * @param bootstrapType - String value for the type of button e.g. "primary"
+ */
+ public void setBootstrapType(String bootstrapType) {
+
+ BootstrapTypes type = null;
+
+ //get the correct background type
+ if (roundedCorners == true) {
+ type = bbuttonTypeMapRounded.get(bootstrapType);
+ } else {
+ type = bbuttonTypeMap.get(bootstrapType);
+ }
+
+ //set up as default
+ if (type == null) {
+ type = BootstrapTypes.DEFAULT;
+ }
+
+
+ layout.setBackgroundResource(type.backgroundDrawable);
+ lblLeft.setTextColor(getResources().getColor(type.textColour));
+ lblMiddle.setTextColor(getResources().getColor(type.textColour));
+ lblRight.setTextColor(getResources().getColor(type.textColour));
+
+ }
+
+ /**
+ * Specifies whether the BootstrapButton is enabled or disabled
+ * @param enabled - boolean state for either enabled or disabled
+ */
+ public void setBootstrapButtonEnabled(boolean enabled)
+ {
+ this.setEnabled(enabled);
+ }
+
+
+ /**
+ * Changes the gravity for the text on a bootstrap button that is not wrap_content
+ * @param gravity - string for either center, right, or left.
+ */
+ public void setTextGravity(String gravity) {
+ if(gravity.equals("left")) {
+ lblMiddle.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ } else if (gravity.equals("center")) {
+ lblMiddle.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
+ } else if (gravity.equals("right")) {
+ lblMiddle.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL);
+ }
+
+ }
+}
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapCircleThumbnail.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapCircleThumbnail.java
new file mode 100644
index 000000000..1eb353770
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapCircleThumbnail.java
@@ -0,0 +1,215 @@
+package com.beardedhen.androidbootstrap;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.utils.ImageUtils;
+
+public class BootstrapCircleThumbnail extends FrameLayout
+{
+ private static final int PADDING_SMALL = 4;
+ private static final int PADDING_MEDIUM = 4;
+ private static final int PADDING_LARGE = 6;
+ private static final int PADDING_XLARGE = 8;
+
+ private static final int SIZE_SMALL = 48; //dp total size (outer circle)
+ private static final int SIZE_MEDIUM = 80;//dp
+ private static final int SIZE_LARGE = 112;//dp
+ private static final int SIZE_XLARGE = 176;//dp
+ private static final int SIZE_DEFAULT = SIZE_MEDIUM;
+
+ private static final String SMALL = "small";
+ private static final String MEDIUM = "medium";
+ private static final String LARGE = "large";
+ private static final String XLARGE = "xlarge";
+
+ private LinearLayout container;
+ private LinearLayout placeholder;
+ private ImageView image;
+ private TextView dimensionsLabel;
+ private String size = MEDIUM;
+ private boolean minimal = false;//minimal means display just the image, no padding
+ private String text = "";
+ private int imageWidth = SIZE_DEFAULT;
+ private int imageHeight = SIZE_DEFAULT;
+ private int padding = 0;
+
+ public BootstrapCircleThumbnail(Context context, AttributeSet attrs, int defStyle)
+ {
+ super(context, attrs, defStyle);
+ initialise(attrs);
+ }
+
+ public BootstrapCircleThumbnail(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ initialise(attrs);
+ }
+
+ public BootstrapCircleThumbnail(Context context)
+ {
+ super(context);
+ initialise(null);
+ }
+
+ private void initialise( AttributeSet attrs )
+ {
+ LayoutInflater inflator = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.BootstrapCircleThumbnail);
+
+
+ int imageDrawable = 0;
+
+ if(a.getString(R.styleable.BootstrapCircleThumbnail_bct_image) != null)
+ {
+ imageDrawable = a.getResourceId(R.styleable.BootstrapCircleThumbnail_bct_image, 0);
+
+ }
+
+ if(a.getString(R.styleable.BootstrapCircleThumbnail_android_text) != null)
+ {
+ text = a.getString(R.styleable.BootstrapCircleThumbnail_android_text);
+ }
+
+ if(a.getString(R.styleable.BootstrapCircleThumbnail_bct_size) != null)
+ {
+ this.size = a.getString(R.styleable.BootstrapCircleThumbnail_bct_size);
+ }
+
+ if(a.getString(R.styleable.BootstrapCircleThumbnail_bct_minimal) != null)
+ {
+ this.minimal = a.getBoolean(R.styleable.BootstrapCircleThumbnail_bct_minimal, false);
+ }
+
+ a.recycle();
+
+ View v = inflator.inflate(R.layout.bootstrap_thumbnail_circle, null, false);
+ dimensionsLabel = (TextView) v.findViewById(R.id.dimensionsLabel);
+ container = (LinearLayout) v.findViewById(R.id.container);
+ placeholder = (LinearLayout) v.findViewById(R.id.placeholder);
+ image = (ImageView) v.findViewById(R.id.image);
+ float scale = getResources().getDisplayMetrics().density;
+
+
+
+ //small image
+ if(this.size.equals(SMALL))
+ {
+ padding = PADDING_SMALL;
+ imageWidth = SIZE_SMALL;
+ imageHeight = SIZE_SMALL;
+
+ }
+ else if(this.size.equals(MEDIUM))
+ {
+ padding = PADDING_MEDIUM;
+ imageWidth = SIZE_MEDIUM;
+ imageHeight = SIZE_MEDIUM;
+ }
+ else if(this.size.equals(LARGE))
+ {
+ padding = PADDING_LARGE;
+ imageWidth = SIZE_LARGE;
+ imageHeight = SIZE_LARGE;
+ }
+ else if(this.size.equals(XLARGE))
+ {
+ padding = PADDING_XLARGE;
+ imageWidth = SIZE_XLARGE;
+ imageHeight = SIZE_XLARGE;
+ }
+ //no valid size is given, set image to default size
+ else
+ {
+ padding = PADDING_MEDIUM;
+ imageWidth = SIZE_DEFAULT;
+ imageHeight = SIZE_DEFAULT;
+ }
+
+ //convert padding to pixels
+ DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
+ int paddingPX = (int)((padding * scale) + 0.5);
+
+ //convert image size to pixels
+ int imageSizeWidthPX = (int)((imageWidth * scale) + 0.5);
+ int imageSizeHeightPX = (int)((imageHeight * scale) + 0.5);
+
+ //make inner image smaller to compensate for the padding so that entire circle including padding equals the size
+ //ex. small image = 48dp, small padding = 4dp, inner image = 48 - (4 * 2) = 40
+ if(this.minimal == false)
+ {
+ imageSizeWidthPX = imageSizeWidthPX - (paddingPX * 2);
+ imageSizeHeightPX = imageSizeHeightPX - (paddingPX * 2);
+
+ this.container.setPadding(paddingPX, paddingPX, paddingPX, paddingPX);
+ container.setBackgroundResource(R.drawable.thumbnail_circle_container);
+ }
+ else
+ {
+ container.setBackgroundResource(R.drawable.thumbnail_circle_minimal);
+ }
+
+ //if no image is given
+ if(imageDrawable == 0)
+ {
+ this.image.setVisibility(View.GONE);
+ placeholder.setLayoutParams(new LinearLayout.LayoutParams(imageSizeWidthPX, imageSizeHeightPX));
+ placeholder.setPadding(paddingPX, paddingPX, paddingPX, paddingPX);
+
+ //set placeholder image
+ placeholder.setBackgroundResource(R.drawable.thumbnail_circle);
+
+ this.dimensionsLabel.setText(text);
+ }
+ else
+ {
+ placeholder.setPadding(0, 0, 0, 0);
+ this.dimensionsLabel.setVisibility(View.GONE);
+ Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), imageDrawable);
+
+ Bitmap roundBitmap = ImageUtils.getCircleBitmap(bitmap, imageSizeWidthPX, imageSizeHeightPX);
+ image.setImageBitmap(roundBitmap);
+ }
+
+ this.addView(v);
+ }
+
+ public void setImage(int drawable)
+ {
+ Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), drawable);
+
+ float scale = getResources().getDisplayMetrics().density;
+
+ //convert image size to pixels
+ int widthPX = (int)((this.imageWidth * scale) + 0.5);
+ int heightPX = (int)((this.imageHeight * scale) + 0.5);
+
+ int paddingPX = (int)((this.padding * scale) + 0.5);
+
+ if(this.minimal == false)
+ {
+ widthPX = widthPX - (paddingPX * 2);
+ heightPX = heightPX - (paddingPX * 2);
+ }
+
+ Bitmap roundBitmap = ImageUtils.getCircleBitmap(bitmap, widthPX, heightPX);
+ image.setImageBitmap(roundBitmap);
+
+ invalidate();
+ requestLayout();
+ }
+}
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapEditText.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapEditText.java
new file mode 100644
index 000000000..c258f8a09
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapEditText.java
@@ -0,0 +1,188 @@
+package com.beardedhen.androidbootstrap;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.EditText;
+
+public class BootstrapEditText extends EditText {
+
+ private boolean roundedCorners = false;
+
+ public BootstrapEditText(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initialise(attrs);
+ }
+
+ public BootstrapEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialise(attrs);
+ }
+
+ public BootstrapEditText(Context context) {
+ super(context);
+ initialise(null);
+ }
+
+ public static final String BOOTSTRAP_EDIT_TEXT_DEFAULT = "default";
+ public static final String BOOTSTRAP_EDIT_TEXT_SUCCESS = "success";
+ public static final String BOOTSTRAP_EDIT_TEXT_WARNING = "warning";
+ public static final String BOOTSTRAP_EDIT_TEXT_DANGER = "danger";
+
+
+ private void initialise( AttributeSet attrs )
+ {
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.BootstrapEditText);
+
+ //get defaults
+ float fontSize = 14.0f;
+ String state = "default";
+ String text = "";
+ String hint = "";
+ boolean enabled = true;
+
+ //font size
+ if (a.getString(R.styleable.BootstrapEditText_android_textSize) != null) {
+
+ String xmlProvidedSize = attrs.getAttributeValue( "http://schemas.android.com/apk/res/android", "textSize");
+ final Pattern PATTERN_FONT_SIZE = Pattern
+ .compile("([0-9]+[.]?[0-9]*)sp");
+ Matcher m = PATTERN_FONT_SIZE.matcher(xmlProvidedSize);
+
+ if (m.find()) {
+ if (m.groupCount() == 1) {
+ fontSize = Float.valueOf(m.group(1));
+ }
+ }
+ }
+
+ //rounded corners
+ if(a.getString(R.styleable.BootstrapEditText_be_roundedCorners) != null) {
+ roundedCorners = a.getBoolean(R.styleable.BootstrapEditText_be_roundedCorners, false);
+ }
+
+ //state
+ if(a.getString(R.styleable.BootstrapEditText_be_state) != null) {
+ state = a.getString(R.styleable.BootstrapEditText_be_state);
+ }
+
+ //text
+ if(a.getString(R.styleable.BootstrapEditText_android_text) != null) {
+ text = a.getString(R.styleable.BootstrapEditText_android_text);
+ }
+
+ //hint
+ if(a.getString(R.styleable.BootstrapEditText_android_hint) != null) {
+ hint = a.getString(R.styleable.BootstrapEditText_android_hint);
+ }
+
+ //enabled
+ if(a.getString(R.styleable.BootstrapEditText_android_enabled) != null) {
+ enabled = a.getBoolean(R.styleable.BootstrapEditText_android_enabled, true);
+ }
+
+ //set values
+ this.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
+ this.setText(text);
+ this.setHint(hint);
+ this.setEnabled(enabled);
+
+ if (enabled){
+ //work out the right background
+ setBackgroundDrawable(state);
+
+ }
+
+ a.recycle();
+
+ //addView(editTextView);
+ }
+
+
+ private void setBackgroundDrawable(String state)
+ {
+ if(roundedCorners){
+ this.setBackgroundResource(R.drawable.edittext_background_rounded);
+ } else {
+ this.setBackgroundResource(R.drawable.edittext_background);
+ }
+
+ if(roundedCorners){
+
+ if (state.equals(BOOTSTRAP_EDIT_TEXT_SUCCESS)){
+ this.setBackgroundResource(R.drawable.edittext_background_rounded_success);
+ } else if (state.equals(BOOTSTRAP_EDIT_TEXT_WARNING)){
+ this.setBackgroundResource(R.drawable.edittext_background_rounded_warning);
+ } else if (state.equals(BOOTSTRAP_EDIT_TEXT_DANGER)){
+ this.setBackgroundResource(R.drawable.edittext_background_rounded_danger);
+ }
+
+ } else {
+
+ if (state.equals(BOOTSTRAP_EDIT_TEXT_SUCCESS)){
+ this.setBackgroundResource(R.drawable.edittext_background_success);
+ } else if (state.equals(BOOTSTRAP_EDIT_TEXT_WARNING)){
+ this.setBackgroundResource(R.drawable.edittext_background_warning);
+ } else if (state.equals(BOOTSTRAP_EDIT_TEXT_DANGER)){
+ this.setBackgroundResource(R.drawable.edittext_background_danger);
+ }
+
+ }
+ }
+
+
+ /**
+ * Change the BootstrapEditTextState
+ * @param state
+ */
+ public void setState(String state){
+ setBackgroundDrawable(state);
+ }
+
+ /**
+ * Set the BootstrapEditText to a successful state
+ */
+ public void setSuccess()
+ {
+ setBackgroundDrawable(BOOTSTRAP_EDIT_TEXT_SUCCESS);
+ }
+
+ /**
+ * Set the BootstrapEditText to a warning state
+ */
+ public void setWarning()
+ {
+ setBackgroundDrawable(BOOTSTRAP_EDIT_TEXT_WARNING);
+ }
+
+ /**
+ * Set the BootstrapEditText to a danger state
+ */
+ public void setDanger()
+ {
+ setBackgroundDrawable(BOOTSTRAP_EDIT_TEXT_DANGER);
+ }
+
+ /**
+ * Set the BootstrapEditText to a default state
+ */
+ public void setDefault()
+ {
+ setBackgroundDrawable(BOOTSTRAP_EDIT_TEXT_DEFAULT);
+ }
+
+ /**
+ * Specifies whether the BootstrapEditText is enabled or disabled
+ * @param enabled - boolean state for either enabled or disabled
+ */
+ public void setBootstrapEditTextEnabled(boolean enabled)
+ {
+ this.setEnabled(enabled);
+ }
+
+}
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapThumbnail.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapThumbnail.java
new file mode 100644
index 000000000..a4e1ba1bc
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/BootstrapThumbnail.java
@@ -0,0 +1,209 @@
+package com.beardedhen.androidbootstrap;
+
+import java.util.HashMap;
+import java.util.Map;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class BootstrapThumbnail extends FrameLayout
+{
+ private static final int DEFAULT_WIDTH = 150; //width of thumbnail when no width is given
+ private static final int DEFAULT_HEIGHT = 150;//height of thumbnail when no height is given
+ private static final int DEFAULT_MAX_PADDING = 8; //8dp is max padding size when padding isn't specified by user
+ private static final int DEFAULT_MIN_PADDING = 4; //4dp
+ private static final String DEFAULT_TYPE = "rounded";
+
+ private static Map<String, ThumbnailTypes> bThumbnailTypeMap;
+ private static Typeface font;
+ private ViewGroup container;
+ private LinearLayout placeholder;
+ private TextView dimensionsLabel;
+ private boolean roundedCorners = true;
+
+ static{
+ bThumbnailTypeMap = new HashMap<String, ThumbnailTypes>();
+
+ bThumbnailTypeMap.put("rounded", ThumbnailTypes.ROUNDED);//default is rounded if user doesn't specify to use square
+ bThumbnailTypeMap.put("square", ThumbnailTypes.SQUARE);
+ }
+
+ public BootstrapThumbnail(Context context, AttributeSet attrs, int defStyle)
+ {
+ super(context, attrs, defStyle);
+ initialise(attrs);
+ }
+
+ public BootstrapThumbnail(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ initialise(attrs);
+ }
+
+ public BootstrapThumbnail(Context context)
+ {
+ super(context);
+ initialise(null);
+ }
+
+ public void setImage(int drawable)
+ {
+ this.placeholder.setBackgroundResource(drawable);
+ invalidate();
+ requestLayout();
+ }
+
+ //set up the bootstrap types
+ private enum ThumbnailTypes
+ {
+ ROUNDED(R.drawable.bthumbnail_container_rounded, R.drawable.bthumbnail_placeholder_default),
+ SQUARE(R.drawable.bthumbnail_container_square, R.drawable.bthumbnail_placeholder_default);
+
+ private int containerDrawable;
+ private int placeholderDrawable;
+
+ ThumbnailTypes(int containerDrawable, int placeholderDrawable)
+ {
+ this.containerDrawable = containerDrawable;
+ this.placeholderDrawable = placeholderDrawable;
+ }
+ }
+
+ private void initialise( AttributeSet attrs )
+ {
+ LayoutInflater inflator = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ readFont(getContext());
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs,
+ R.styleable.BootstrapThumbnail);
+
+ //defaults
+ ThumbnailTypes type = null;
+ String thumbnailType = DEFAULT_TYPE;
+ String text = "";
+ int imageDrawable = 0;
+ float scale = getResources().getDisplayMetrics().density; //for padding
+ int width = DEFAULT_WIDTH;
+ int height = DEFAULT_HEIGHT;
+ int padding = 0;
+ int paddingDP = 0;
+
+ //attribute values
+ if(a.getString(R.styleable.BootstrapThumbnail_bt_width) != null) {
+ width = (int) a.getDimension(R.styleable.BootstrapThumbnail_bt_width, 0);
+ Log.v("width", Integer.toString(width));
+ }
+
+ if(a.getString(R.styleable.BootstrapThumbnail_bt_height) != null) {
+ height = (int) a.getDimension(R.styleable.BootstrapThumbnail_bt_height, 0);
+ }
+
+ if(a.getString(R.styleable.BootstrapThumbnail_bt_inside_padding) != null) {
+ paddingDP = (int) a.getDimension(R.styleable.BootstrapThumbnail_bt_inside_padding, 0);
+ }
+ else{
+ padding = (int) (((Math.sqrt(width * height)) / 100) * 2);
+ if(padding > DEFAULT_MAX_PADDING)
+ padding = DEFAULT_MAX_PADDING;
+ if(padding < DEFAULT_MIN_PADDING)
+ padding = DEFAULT_MIN_PADDING;
+
+ paddingDP = (int) (padding * scale + 0.5f);//container padding in DP
+ }
+
+ if(a.getString(R.styleable.BootstrapThumbnail_bt_roundedCorners) != null){
+ roundedCorners = a.getBoolean(R.styleable.BootstrapThumbnail_bt_roundedCorners, false) ;
+ }
+
+ if(a.getString(R.styleable.BootstrapThumbnail_bt_image) != null){
+ imageDrawable = a.getResourceId(R.styleable.BootstrapThumbnail_bt_image, 0);
+ }
+
+ a.recycle();
+
+ text = (int)(width/scale) + "x" + (int)(height/scale);
+ View v = inflator.inflate(R.layout.bootstrap_thumbnail, null, false);
+
+ //get layout items
+ container = (ViewGroup) v.findViewById(R.id.container);
+ placeholder = (LinearLayout) v.findViewById(R.id.placeholder);
+ dimensionsLabel = (TextView) v.findViewById(R.id.dimensionsLabel);
+
+ Log.v("size", "width:" + width + " height:" + height);
+
+
+ type = bThumbnailTypeMap.get(thumbnailType);
+
+ //get the correct background type
+ if(roundedCorners == true)
+ {
+ type = bThumbnailTypeMap.get("rounded");
+ } else {
+ type = bThumbnailTypeMap.get("square");
+ }
+
+ //apply the background type
+ container.setBackgroundResource(type.containerDrawable);
+
+ //if no image is provided by user
+ if(imageDrawable == 0){
+ //set default grey placeholder background
+ placeholder.setBackgroundResource(type.placeholderDrawable);
+
+ //set the text
+ if(text.length() > 0){
+ dimensionsLabel.setText(text);
+ dimensionsLabel.setVisibility(View.VISIBLE);
+ }
+ }
+ else{
+ //set background to user's provided image
+ placeholder.setBackgroundResource(imageDrawable);
+
+ //remove textview dimensions
+ dimensionsLabel.setVisibility(View.GONE);
+ }
+
+ //placeholder padding
+ int paddingP = (int) (((Math.sqrt(width * height)) / 100) * 4);
+
+ //convert to DP
+ int paddingDPP = (int) (paddingP * scale + 0.5f);//placeholder padding in DP
+
+ container.setPadding(paddingDP, paddingDP, paddingDP, paddingDP);
+ placeholder.setPadding(paddingDPP, paddingDPP, paddingDPP, paddingDPP);
+
+ placeholder.setLayoutParams(new LinearLayout.LayoutParams(width,height));
+
+ //set the font awesome icon typeface
+ dimensionsLabel.setTypeface(font);
+
+ this.setClickable(true);
+
+ addView(v);
+ }
+
+ //static class to read in font
+ private static void readFont(Context context)
+ {
+ if(font == null){
+ try {
+ font = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
+ } catch (Exception e) {
+ Log.e("BootstrapButton", "Could not get typeface because " + e.getMessage());
+ font = Typeface.DEFAULT;
+ }
+ }
+
+ }
+}
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesome.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesome.java
new file mode 100644
index 000000000..2038094ac
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesome.java
@@ -0,0 +1,390 @@
+package com.beardedhen.androidbootstrap;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class FontAwesome {
+
+ private static Map<String, String> faMap = new HashMap<String, String>();
+
+ //font awesome map as per
+ //http://fortawesome.github.io/Font-Awesome/cheatsheet/
+
+ static {
+ faMap.put("fa-glass", "\uf000");
+ faMap.put("fa-music", "\uf001");
+ faMap.put("fa-search", "\uf002");
+ faMap.put("fa-envelope-o", "\uf003");
+ faMap.put("fa-heart", "\uf004");
+ faMap.put("fa-star", "\uf005");
+ faMap.put("fa-star-o", "\uf006");
+ faMap.put("fa-user", "\uf007");
+ faMap.put("fa-film", "\uf008");
+ faMap.put("fa-th-large", "\uf009");
+ faMap.put("fa-th", "\uf00a");
+ faMap.put("fa-th-list", "\uf00b");
+ faMap.put("fa-check", "\uf00c");
+ faMap.put("fa-times", "\uf00d");
+ faMap.put("fa-search-plus", "\uf00e");
+ faMap.put("fa-search-minus", "\uf010");
+ faMap.put("fa-power-off", "\uf011");
+ faMap.put("fa-signal", "\uf012");
+ faMap.put("fa-cog", "\uf013");
+ faMap.put("fa-trash-o", "\uf014");
+ faMap.put("fa-home", "\uf015");
+ faMap.put("fa-file-o", "\uf016");
+ faMap.put("fa-clock-o", "\uf017");
+ faMap.put("fa-road", "\uf018");
+ faMap.put("fa-download", "\uf019");
+ faMap.put("fa-arrow-circle-o-down", "\uf01a");
+ faMap.put("fa-arrow-circle-o-up", "\uf01b");
+ faMap.put("fa-inbox", "\uf01c");
+ faMap.put("fa-play-circle-o", "\uf01d");
+ faMap.put("fa-repeat", "\uf01e");
+ faMap.put("fa-refresh", "\uf021");
+ faMap.put("fa-list-alt", "\uf022");
+ faMap.put("fa-lock", "\uf023");
+ faMap.put("fa-flag", "\uf024");
+ faMap.put("fa-headphones", "\uf025");
+ faMap.put("fa-volume-off", "\uf026");
+ faMap.put("fa-volume-down", "\uf027");
+ faMap.put("fa-volume-up", "\uf028");
+ faMap.put("fa-qrcode", "\uf029");
+ faMap.put("fa-barcode", "\uf02a");
+ faMap.put("fa-tag", "\uf02b");
+ faMap.put("fa-tags", "\uf02c");
+ faMap.put("fa-book", "\uf02d");
+ faMap.put("fa-bookmark", "\uf02e");
+ faMap.put("fa-print", "\uf02f");
+ faMap.put("fa-camera", "\uf030");
+ faMap.put("fa-font", "\uf031");
+ faMap.put("fa-bold", "\uf032");
+ faMap.put("fa-italic", "\uf033");
+ faMap.put("fa-text-height", "\uf034");
+ faMap.put("fa-text-width", "\uf035");
+ faMap.put("fa-align-left", "\uf036");
+ faMap.put("fa-align-center", "\uf037");
+ faMap.put("fa-align-right", "\uf038");
+ faMap.put("fa-align-justify", "\uf039");
+ faMap.put("fa-list", "\uf03a");
+ faMap.put("fa-outdent", "\uf03b");
+ faMap.put("fa-indent", "\uf03c");
+ faMap.put("fa-video-camera", "\uf03d");
+ faMap.put("fa-picture-o", "\uf03e");
+ faMap.put("fa-pencil", "\uf040");
+ faMap.put("fa-map-marker", "\uf041");
+ faMap.put("fa-adjust", "\uf042");
+ faMap.put("fa-tint", "\uf043");
+ faMap.put("fa-pencil-square-o", "\uf044");
+ faMap.put("fa-share-square-o", "\uf045");
+ faMap.put("fa-check-square-o", "\uf046");
+ faMap.put("fa-move", "\uf047");
+ faMap.put("fa-step-backward", "\uf048");
+ faMap.put("fa-fast-backward", "\uf049");
+ faMap.put("fa-backward", "\uf04a");
+ faMap.put("fa-play", "\uf04b");
+ faMap.put("fa-pause", "\uf04c");
+ faMap.put("fa-stop", "\uf04d");
+ faMap.put("fa-forward", "\uf04e");
+ faMap.put("fa-fast-forward", "\uf050");
+ faMap.put("fa-step-forward", "\uf051");
+ faMap.put("fa-eject", "\uf052");
+ faMap.put("fa-chevron-left", "\uf053");
+ faMap.put("fa-chevron-right", "\uf054");
+ faMap.put("fa-plus-circle", "\uf055");
+ faMap.put("fa-minus-circle", "\uf056");
+ faMap.put("fa-times-circle", "\uf057");
+ faMap.put("fa-check-circle", "\uf058");
+ faMap.put("fa-question-circle", "\uf059");
+ faMap.put("fa-info-circle", "\uf05a");
+ faMap.put("fa-crosshairs", "\uf05b");
+ faMap.put("fa-times-circle-o", "\uf05c");
+ faMap.put("fa-check-circle-o", "\uf05d");
+ faMap.put("fa-ban", "\uf05e");
+ faMap.put("fa-arrow-left", "\uf060");
+ faMap.put("fa-arrow-right", "\uf061");
+ faMap.put("fa-arrow-up", "\uf062");
+ faMap.put("fa-arrow-down", "\uf063");
+ faMap.put("fa-share", "\uf064");
+ faMap.put("fa-resize-full", "\uf065");
+ faMap.put("fa-resize-small", "\uf066");
+ faMap.put("fa-plus", "\uf067");
+ faMap.put("fa-minus", "\uf068");
+ faMap.put("fa-asterisk", "\uf069");
+ faMap.put("fa-exclamation-circle", "\uf06a");
+ faMap.put("fa-gift", "\uf06b");
+ faMap.put("fa-leaf", "\uf06c");
+ faMap.put("fa-fire", "\uf06d");
+ faMap.put("fa-eye", "\uf06e");
+ faMap.put("fa-eye-slash", "\uf070");
+ faMap.put("fa-exclamation-triangle", "\uf071");
+ faMap.put("fa-plane", "\uf072");
+ faMap.put("fa-calendar", "\uf073");
+ faMap.put("fa-random", "\uf074");
+ faMap.put("fa-comment", "\uf075");
+ faMap.put("fa-magnet", "\uf076");
+ faMap.put("fa-chevron-up", "\uf077");
+ faMap.put("fa-chevron-down", "\uf078");
+ faMap.put("fa-retweet", "\uf079");
+ faMap.put("fa-shopping-cart", "\uf07a");
+ faMap.put("fa-folder", "\uf07b");
+ faMap.put("fa-folder-open", "\uf07c");
+ faMap.put("fa-resize-vertical", "\uf07d");
+ faMap.put("fa-resize-horizontal", "\uf07e");
+ faMap.put("fa-bar-chart-o", "\uf080");
+ faMap.put("fa-twitter-square", "\uf081");
+ faMap.put("fa-facebook-square", "\uf082");
+ faMap.put("fa-camera-retro", "\uf083");
+ faMap.put("fa-key", "\uf084");
+ faMap.put("fa-cogs", "\uf085");
+ faMap.put("fa-comments", "\uf086");
+ faMap.put("fa-thumbs-o-up", "\uf087");
+ faMap.put("fa-thumbs-o-down", "\uf088");
+ faMap.put("fa-star-half", "\uf089");
+ faMap.put("fa-heart-o", "\uf08a");
+ faMap.put("fa-sign-out", "\uf08b");
+ faMap.put("fa-linkedin-square", "\uf08c");
+ faMap.put("fa-thumb-tack", "\uf08d");
+ faMap.put("fa-external-link", "\uf08e");
+ faMap.put("fa-sign-in", "\uf090");
+ faMap.put("fa-trophy", "\uf091");
+ faMap.put("fa-github-square", "\uf092");
+ faMap.put("fa-upload", "\uf093");
+ faMap.put("fa-lemon-o", "\uf094");
+ faMap.put("fa-phone", "\uf095");
+ faMap.put("fa-square-o", "\uf096");
+ faMap.put("fa-bookmark-o", "\uf097");
+ faMap.put("fa-phone-square", "\uf098");
+ faMap.put("fa-twitter", "\uf099");
+ faMap.put("fa-facebook", "\uf09a");
+ faMap.put("fa-github", "\uf09b");
+ faMap.put("fa-unlock", "\uf09c");
+ faMap.put("fa-credit-card", "\uf09d");
+ faMap.put("fa-rss", "\uf09e");
+ faMap.put("fa-hdd", "\uf0a0");
+ faMap.put("fa-bullhorn", "\uf0a1");
+ faMap.put("fa-bell", "\uf0f3");
+ faMap.put("fa-certificate", "\uf0a3");
+ faMap.put("fa-hand-o-right", "\uf0a4");
+ faMap.put("fa-hand-o-left", "\uf0a5");
+ faMap.put("fa-hand-o-up", "\uf0a6");
+ faMap.put("fa-hand-o-down", "\uf0a7");
+ faMap.put("fa-arrow-circle-left", "\uf0a8");
+ faMap.put("fa-arrow-circle-right", "\uf0a9");
+ faMap.put("fa-arrow-circle-up", "\uf0aa");
+ faMap.put("fa-arrow-circle-down", "\uf0ab");
+ faMap.put("fa-globe", "\uf0ac");
+ faMap.put("fa-wrench", "\uf0ad");
+ faMap.put("fa-tasks", "\uf0ae");
+ faMap.put("fa-filter", "\uf0b0");
+ faMap.put("fa-briefcase", "\uf0b1");
+ faMap.put("fa-fullscreen", "\uf0b2");
+ faMap.put("fa-group", "\uf0c0");
+ faMap.put("fa-link", "\uf0c1");
+ faMap.put("fa-cloud", "\uf0c2");
+ faMap.put("fa-flask", "\uf0c3");
+ faMap.put("fa-scissors", "\uf0c4");
+ faMap.put("fa-files-o", "\uf0c5");
+ faMap.put("fa-paperclip", "\uf0c6");
+ faMap.put("fa-floppy-o", "\uf0c7");
+ faMap.put("fa-square", "\uf0c8");
+ faMap.put("fa-reorder", "\uf0c9");
+ faMap.put("fa-list-ul", "\uf0ca");
+ faMap.put("fa-list-ol", "\uf0cb");
+ faMap.put("fa-strikethrough", "\uf0cc");
+ faMap.put("fa-underline", "\uf0cd");
+ faMap.put("fa-table", "\uf0ce");
+ faMap.put("fa-magic", "\uf0d0");
+ faMap.put("fa-truck", "\uf0d1");
+ faMap.put("fa-pinterest", "\uf0d2");
+ faMap.put("fa-pinterest-square", "\uf0d3");
+ faMap.put("fa-google-plus-square", "\uf0d4");
+ faMap.put("fa-google-plus", "\uf0d5");
+ faMap.put("fa-money", "\uf0d6");
+ faMap.put("fa-caret-down", "\uf0d7");
+ faMap.put("fa-caret-up", "\uf0d8");
+ faMap.put("fa-caret-left", "\uf0d9");
+ faMap.put("fa-caret-right", "\uf0da");
+ faMap.put("fa-columns", "\uf0db");
+ faMap.put("fa-sort", "\uf0dc");
+ faMap.put("fa-sort-asc", "\uf0dd");
+ faMap.put("fa-sort-desc", "\uf0de");
+ faMap.put("fa-envelope", "\uf0e0");
+ faMap.put("fa-linkedin", "\uf0e1");
+ faMap.put("fa-undo", "\uf0e2");
+ faMap.put("fa-gavel", "\uf0e3");
+ faMap.put("fa-tachometer", "\uf0e4");
+ faMap.put("fa-comment-o", "\uf0e5");
+ faMap.put("fa-comments-o", "\uf0e6");
+ faMap.put("fa-bolt", "\uf0e7");
+ faMap.put("fa-sitemap", "\uf0e8");
+ faMap.put("fa-umbrella", "\uf0e9");
+ faMap.put("fa-clipboard", "\uf0ea");
+ faMap.put("fa-lightbulb-o", "\uf0eb");
+ faMap.put("fa-exchange", "\uf0ec");
+ faMap.put("fa-cloud-download", "\uf0ed");
+ faMap.put("fa-cloud-upload", "\uf0ee");
+ faMap.put("fa-user-md", "\uf0f0");
+ faMap.put("fa-stethoscope", "\uf0f1");
+ faMap.put("fa-suitcase", "\uf0f2");
+ faMap.put("fa-bell-o", "\uf0a2");
+ faMap.put("fa-coffee", "\uf0f4");
+ faMap.put("fa-cutlery", "\uf0f5");
+ faMap.put("fa-file-text-o", "\uf0f6");
+ faMap.put("fa-building", "\uf0f7");
+ faMap.put("fa-hospital", "\uf0f8");
+ faMap.put("fa-ambulance", "\uf0f9");
+ faMap.put("fa-medkit", "\uf0fa");
+ faMap.put("fa-fighter-jet", "\uf0fb");
+ faMap.put("fa-beer", "\uf0fc");
+ faMap.put("fa-h-square", "\uf0fd");
+ faMap.put("fa-plus-square", "\uf0fe");
+ faMap.put("fa-angle-double-left", "\uf100");
+ faMap.put("fa-angle-double-right", "\uf101");
+ faMap.put("fa-angle-double-up", "\uf102");
+ faMap.put("fa-angle-double-down", "\uf103");
+ faMap.put("fa-angle-left", "\uf104");
+ faMap.put("fa-angle-right", "\uf105");
+ faMap.put("fa-angle-up", "\uf106");
+ faMap.put("fa-angle-down", "\uf107");
+ faMap.put("fa-desktop", "\uf108");
+ faMap.put("fa-laptop", "\uf109");
+ faMap.put("fa-tablet", "\uf10a");
+ faMap.put("fa-mobile", "\uf10b");
+ faMap.put("fa-circle-o", "\uf10c");
+ faMap.put("fa-quote-left", "\uf10d");
+ faMap.put("fa-quote-right", "\uf10e");
+ faMap.put("fa-spinner", "\uf110");
+ faMap.put("fa-circle", "\uf111");
+ faMap.put("fa-reply", "\uf112");
+ faMap.put("fa-github-alt", "\uf113");
+ faMap.put("fa-folder-o", "\uf114");
+ faMap.put("fa-folder-open-o", "\uf115");
+ faMap.put("fa-expand-o", "\uf116");
+ faMap.put("fa-collapse-o", "\uf117");
+ faMap.put("fa-smile-o", "\uf118");
+ faMap.put("fa-frown-o", "\uf119");
+ faMap.put("fa-meh-o", "\uf11a");
+ faMap.put("fa-gamepad", "\uf11b");
+ faMap.put("fa-keyboard-o", "\uf11c");
+ faMap.put("fa-flag-o", "\uf11d");
+ faMap.put("fa-flag-checkered", "\uf11e");
+ faMap.put("fa-terminal", "\uf120");
+ faMap.put("fa-code", "\uf121");
+ faMap.put("fa-reply-all", "\uf122");
+ faMap.put("fa-mail-reply-all", "\uf122");
+ faMap.put("fa-star-half-o", "\uf123");
+ faMap.put("fa-location-arrow", "\uf124");
+ faMap.put("fa-crop", "\uf125");
+ faMap.put("fa-code-fork", "\uf126");
+ faMap.put("fa-chain-broken", "\uf127");
+ faMap.put("fa-question", "\uf128");
+ faMap.put("fa-info", "\uf129");
+ faMap.put("fa-exclamation", "\uf12a");
+ faMap.put("fa-superscript", "\uf12b");
+ faMap.put("fa-subscript", "\uf12c");
+ faMap.put("fa-eraser", "\uf12d");
+ faMap.put("fa-puzzle-piece", "\uf12e");
+ faMap.put("fa-microphone", "\uf130");
+ faMap.put("fa-microphone-slash", "\uf131");
+ faMap.put("fa-shield", "\uf132");
+ faMap.put("fa-calendar-o", "\uf133");
+ faMap.put("fa-fire-extinguisher", "\uf134");
+ faMap.put("fa-rocket", "\uf135");
+ faMap.put("fa-maxcdn", "\uf136");
+ faMap.put("fa-chevron-circle-left", "\uf137");
+ faMap.put("fa-chevron-circle-right", "\uf138");
+ faMap.put("fa-chevron-circle-up", "\uf139");
+ faMap.put("fa-chevron-circle-down", "\uf13a");
+ faMap.put("fa-html5", "\uf13b");
+ faMap.put("fa-css3", "\uf13c");
+ faMap.put("fa-anchor", "\uf13d");
+ faMap.put("fa-unlock-o", "\uf13e");
+ faMap.put("fa-bullseye", "\uf140");
+ faMap.put("fa-ellipsis-horizontal", "\uf141");
+ faMap.put("fa-ellipsis-vertical", "\uf142");
+ faMap.put("fa-rss-square", "\uf143");
+ faMap.put("fa-play-circle", "\uf144");
+ faMap.put("fa-ticket", "\uf145");
+ faMap.put("fa-minus-square", "\uf146");
+ faMap.put("fa-minus-square-o", "\uf147");
+ faMap.put("fa-level-up", "\uf148");
+ faMap.put("fa-level-down", "\uf149");
+ faMap.put("fa-check-square", "\uf14a");
+ faMap.put("fa-pencil-square", "\uf14b");
+ faMap.put("fa-external-link-square", "\uf14c");
+ faMap.put("fa-share-square", "\uf14d");
+ faMap.put("fa-compass", "\uf14e");
+ faMap.put("fa-caret-square-o-down", "\uf150");
+ faMap.put("fa-caret-square-o-up", "\uf151");
+ faMap.put("fa-caret-square-o-right", "\uf152");
+ faMap.put("fa-eur", "\uf153");
+ faMap.put("fa-gbp", "\uf154");
+ faMap.put("fa-usd", "\uf155");
+ faMap.put("fa-inr", "\uf156");
+ faMap.put("fa-jpy", "\uf157");
+ faMap.put("fa-rub", "\uf158");
+ faMap.put("fa-krw", "\uf159");
+ faMap.put("fa-btc", "\uf15a");
+ faMap.put("fa-file", "\uf15b");
+ faMap.put("fa-file-text", "\uf15c");
+ faMap.put("fa-sort-alpha-asc", "\uf15d");
+ faMap.put("fa-sort-alpha-desc", "\uf15e");
+ faMap.put("fa-sort-amount-asc", "\uf160");
+ faMap.put("fa-sort-amount-desc", "\uf161");
+ faMap.put("fa-sort-numeric-asc", "\uf162");
+ faMap.put("fa-sort-numeric-desc", "\uf163");
+ faMap.put("fa-thumbs-up", "\uf164");
+ faMap.put("fa-thumbs-down", "\uf165");
+ faMap.put("fa-youtube-square", "\uf166");
+ faMap.put("fa-youtube", "\uf167");
+ faMap.put("fa-xing", "\uf168");
+ faMap.put("fa-xing-square", "\uf169");
+ faMap.put("fa-youtube-play", "\uf16a");
+ faMap.put("fa-dropbox", "\uf16b");
+ faMap.put("fa-stack-overflow", "\uf16c");
+ faMap.put("fa-instagram", "\uf16d");
+ faMap.put("fa-flickr", "\uf16e");
+ faMap.put("fa-adn", "\uf170");
+ faMap.put("fa-bitbucket", "\uf171");
+ faMap.put("fa-bitbucket-square", "\uf172");
+ faMap.put("fa-tumblr", "\uf173");
+ faMap.put("fa-tumblr-square", "\uf174");
+ faMap.put("fa-long-arrow-down", "\uf175");
+ faMap.put("fa-long-arrow-up", "\uf176");
+ faMap.put("fa-long-arrow-left", "\uf177");
+ faMap.put("fa-long-arrow-right", "\uf178");
+ faMap.put("fa-apple", "\uf179");
+ faMap.put("fa-windows", "\uf17a");
+ faMap.put("fa-android", "\uf17b");
+ faMap.put("fa-linux", "\uf17c");
+ faMap.put("fa-dribbble", "\uf17d");
+ faMap.put("fa-skype", "\uf17e");
+ faMap.put("fa-foursquare", "\uf180");
+ faMap.put("fa-trello", "\uf181");
+ faMap.put("fa-female", "\uf182");
+ faMap.put("fa-male", "\uf183");
+ faMap.put("fa-gittip", "\uf184");
+ faMap.put("fa-sun-o", "\uf185");
+ faMap.put("fa-moon-o", "\uf186");
+ faMap.put("fa-archive", "\uf187");
+ faMap.put("fa-bug", "\uf188");
+ faMap.put("fa-vk", "\uf189");
+ faMap.put("fa-weibo", "\uf18a");
+ faMap.put("fa-renren", "\uf18b");
+ faMap.put("fa-pagelines", "\uf18c");
+ faMap.put("fa-stack-exchange", "\uf18d");
+ faMap.put("fa-arrow-circle-o-right", "\uf18e");
+ faMap.put("fa-arrow-circle-o-left", "\uf190");
+ faMap.put("fa-caret-square-o-left", "\uf191");
+ faMap.put("fa-dot-circle-o", "\uf192");
+ faMap.put("fa-wheelchair", "\uf193");
+ faMap.put("fa-vimeo-square", "\uf194");
+ }
+
+ public static Map<String, String> getFaMap()
+ {
+ return faMap;
+ }
+
+}
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesomeText.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesomeText.java
new file mode 100644
index 000000000..75a130c5a
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/FontAwesomeText.java
@@ -0,0 +1,274 @@
+package com.beardedhen.androidbootstrap;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.RotateAnimation;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.R;
+
+public class FontAwesomeText extends FrameLayout {
+
+ private static Typeface font;
+ private static Map<String, String> faMap;
+
+ private TextView tv;
+
+ private static final String FA_ICON_QUESTION = "fa-question";
+
+ public enum AnimationSpeed
+ {
+ FAST,
+ MEDIUM,
+ SLOW;
+ }
+
+ static{
+ faMap = FontAwesome.getFaMap();
+ }
+
+ public FontAwesomeText(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initialise(attrs);
+ }
+
+ public FontAwesomeText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialise(attrs);
+ }
+
+ public FontAwesomeText(Context context) {
+ super(context);
+ initialise(null);
+ }
+
+
+
+
+ private void initialise( AttributeSet attrs )
+ {
+ LayoutInflater inflator = (LayoutInflater)getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ //get font
+ readFont(getContext());
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FontAwesomeText);
+
+ //inflate the view
+ View fontAwesomeTextView = inflator.inflate(R.layout.font_awesome_text, null, false);
+ tv = (TextView)fontAwesomeTextView.findViewById(R.id.lblText);
+
+ String icon = "";
+ float fontSize = 14.0f;
+
+ //icon
+ if (a.getString(R.styleable.FontAwesomeText_fa_icon) != null) {
+ icon = a.getString(R.styleable.FontAwesomeText_fa_icon);
+ }
+
+ //font size
+ if (a.getString(R.styleable.FontAwesomeText_android_textSize) != null) {
+
+ String xmlProvidedSize = attrs.getAttributeValue(
+ "http://schemas.android.com/apk/res/android", "textSize");
+ final Pattern PATTERN_FONT_SIZE = Pattern
+ .compile("([0-9]+[.]?[0-9]*)sp");
+ Matcher m = PATTERN_FONT_SIZE.matcher(xmlProvidedSize);
+
+ if (m.find()) {
+ if (m.groupCount() == 1) {
+ fontSize = Float.valueOf(m.group(1));
+ }
+ }
+ }
+
+ //text colour
+ if(a.getString(R.styleable.FontAwesomeText_android_textColor) != null){
+ tv.setTextColor(a.getColor(R.styleable.FontAwesomeText_android_textColor, R.color.bbutton_inverse));
+ }
+
+ setIcon(icon);
+
+ tv.setTypeface(font);
+ tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
+
+ a.recycle();
+ addView(fontAwesomeTextView);
+ }
+
+ private static void readFont(Context context)
+ {
+
+ if(font == null){
+ try {
+ font = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
+ } catch (Exception e) {
+ Log.e("BButton", "Could not get typeface because " + e.getMessage());
+ font = Typeface.DEFAULT;
+ }
+ }
+
+ }
+
+
+ /**
+ * Used to start flashing a FontAwesomeText item
+ * @param context the current applications context
+ * @param forever whether the item should flash repeatedly or just once
+ * @param speed how fast the item should flash, chose between FontAwesomeText.AnimationSpeed.SLOW /
+ * FontAwesomeText.AnimationSpeed.MEDIUM / FontAwesomeText.AnimationSpeed.FAST
+ */
+ public void startFlashing(Context context, boolean forever, AnimationSpeed speed)
+ {
+
+ Animation fadeIn = new AlphaAnimation(0, 1);
+
+ //set up extra variables
+ fadeIn.setDuration(50);
+ fadeIn.setRepeatMode(Animation.REVERSE);
+
+ //default repeat count is 0, however if user wants, set it up to be infinite
+ fadeIn.setRepeatCount(0);
+ if (forever){
+ fadeIn.setRepeatCount(Animation.INFINITE);
+ }
+
+ //default speed
+ fadeIn.setStartOffset(1000);
+
+ //fast
+ if (speed.equals(AnimationSpeed.FAST))
+ {
+ fadeIn.setStartOffset(200);
+ }
+
+ //medium
+ if (speed.equals(AnimationSpeed.MEDIUM))
+ {
+ fadeIn.setStartOffset(500);
+ }
+
+ //set the new animation to a final animation
+ final Animation animation = fadeIn;
+
+ //run the animation - used to work correctly on older devices
+ tv.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ tv.startAnimation(animation);
+ }
+ }, 100);
+ }
+
+
+ /**
+ * Used to start rotating a FontAwesomeText item
+ * @param context the current applications context
+ * @param clockwise true for clockwise, false for anti clockwise spinning
+ * @param speed how fast the item should flash, chose between FontAwesomeText.AnimationSpeed.SLOW /
+ * FontAwesomeText.AnimationSpeed.MEDIUM / FontAwesomeText.AnimationSpeed.FAST
+ */
+ public void startRotate(Context context, boolean clockwise, AnimationSpeed speed)
+ {
+ Animation rotate;
+
+ //set up the rotation animation
+ if (clockwise){
+ rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);
+ } else {
+ rotate = new RotateAnimation(360, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,0.5f);
+ }
+
+ //set up some extra variables
+ rotate.setRepeatCount(Animation.INFINITE);
+ rotate.setInterpolator(new LinearInterpolator());
+ rotate.setStartOffset(0);
+ rotate.setRepeatMode(Animation.RESTART);
+
+ //defaults
+ rotate.setDuration(2000);
+
+ //fast
+ if (speed.equals(AnimationSpeed.FAST))
+ {
+ rotate.setDuration(500);
+ }
+
+ //medium
+ if (speed.equals(AnimationSpeed.MEDIUM))
+ {
+ rotate.setDuration(1000);
+ }
+
+ //send the new animation to a final animation
+ final Animation animation = rotate;
+
+ //run the animation - used to work correctly on older devices
+ tv.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ tv.startAnimation(animation);
+ }
+ }, 100);
+
+ }
+
+
+ /**
+ * Used to stop animating any FontAwesomeText item
+ */
+ public void stopAnimation(){
+ //stop the animation
+ tv.clearAnimation();
+ }
+
+
+ /**
+ * Used to set the icon for a FontAwesomeText item
+ * @param faIcon - String value for the icon as per http://fortawesome.github.io/Font-Awesome/cheatsheet/
+ */
+ public void setIcon(String faIcon) {
+
+ String icon = faMap.get(faIcon);
+
+ if (icon == null)
+ {
+ icon = faMap.get(FA_ICON_QUESTION);
+ }
+
+ tv.setText(icon);
+ }
+
+ /**
+ * Used to set the text color of the underlying text view.
+ * @param color - Integer value representing a color resource.
+ */
+ public void setTextColor(int color) {
+ tv.setTextColor(color);
+ }
+
+ /**
+ * Used to set the text size of the underlying text view.
+ * @param unit - Integer value representing a unit size
+ * @param size - Float value representing text size
+ */
+ public void setTextSize(int unit, float size) {
+ tv.setTextSize(unit, size);
+ }
+
+}
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/AutoResizeTextView.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/AutoResizeTextView.java
new file mode 100644
index 000000000..be6f328d3
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/AutoResizeTextView.java
@@ -0,0 +1,303 @@
+package com.beardedhen.androidbootstrap.utils;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.RectF;
+import android.os.Build;
+import android.text.Layout.Alignment;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.AttributeSet;
+import android.util.SparseIntArray;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+/**
+ *
+ * Code from user M-WaJeEh on StackOverflow at
+ * http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds/17782522#17782522
+ *
+ */
+
+public class AutoResizeTextView extends TextView {
+private interface SizeTester {
+ /**
+ *
+ * @param suggestedSize
+ * Size of text to be tested
+ * @param availableSpace
+ * available space in which text must fit
+ * @return an integer < 0 if after applying {@code suggestedSize} to
+ * text, it takes less space than {@code availableSpace}, > 0
+ * otherwise
+ */
+ public int onTestSize(int suggestedSize, RectF availableSpace);
+}
+
+private RectF mTextRect = new RectF();
+
+private RectF mAvailableSpaceRect;
+
+private SparseIntArray mTextCachedSizes;
+
+private TextPaint mPaint;
+
+private float mMaxTextSize;
+
+private float mSpacingMult = 1.0f;
+
+private float mSpacingAdd = 0.0f;
+
+private float mMinTextSize = 20;
+
+private int mWidthLimit;
+
+private static final int NO_LINE_LIMIT = -1;
+private int mMaxLines;
+
+private boolean mEnableSizeCache = true;
+private boolean mInitiallized;
+
+public AutoResizeTextView(Context context) {
+ super(context);
+ initialize();
+}
+
+public AutoResizeTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initialize();
+}
+
+public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initialize();
+}
+
+private void initialize() {
+ mPaint = new TextPaint(getPaint());
+ mMaxTextSize = getTextSize();
+ mAvailableSpaceRect = new RectF();
+ mTextCachedSizes = new SparseIntArray();
+ if (mMaxLines == 0) {
+ // no value was assigned during construction
+ mMaxLines = NO_LINE_LIMIT;
+ }
+ mInitiallized = true;
+}
+
+@Override
+public void setText(final CharSequence text, BufferType type) {
+ super.setText(text, type);
+ adjustTextSize(text.toString());
+}
+
+@Override
+public void setTextSize(float size) {
+ mMaxTextSize = size;
+ mTextCachedSizes.clear();
+ adjustTextSize(getText().toString());
+}
+
+@Override
+public void setMaxLines(int maxlines) {
+ super.setMaxLines(maxlines);
+ mMaxLines = maxlines;
+ reAdjust();
+}
+
+public int getMaxLines() {
+ return mMaxLines;
+}
+
+@Override
+public void setSingleLine() {
+ super.setSingleLine();
+ mMaxLines = 1;
+ reAdjust();
+}
+
+@Override
+public void setSingleLine(boolean singleLine) {
+ super.setSingleLine(singleLine);
+ if (singleLine) {
+ mMaxLines = 1;
+ } else {
+ mMaxLines = NO_LINE_LIMIT;
+ }
+ reAdjust();
+}
+
+@Override
+public void setLines(int lines) {
+ super.setLines(lines);
+ mMaxLines = lines;
+ reAdjust();
+}
+
+@Override
+public void setTextSize(int unit, float size) {
+ Context c = getContext();
+ Resources r;
+
+ if (c == null)
+ r = Resources.getSystem();
+ else
+ r = c.getResources();
+ mMaxTextSize = TypedValue.applyDimension(unit, size,
+ r.getDisplayMetrics());
+ mTextCachedSizes.clear();
+ adjustTextSize(getText().toString());
+}
+
+@Override
+public void setLineSpacing(float add, float mult) {
+ super.setLineSpacing(add, mult);
+ mSpacingMult = mult;
+ mSpacingAdd = add;
+}
+
+/**
+ * Set the lower text size limit and invalidate the view
+ *
+ * @param minTextSize
+ */
+public void setMinTextSize(float minTextSize) {
+ mMinTextSize = minTextSize;
+ reAdjust();
+}
+
+private void reAdjust() {
+ adjustTextSize(getText().toString());
+}
+
+private void adjustTextSize(String string) {
+ if (!mInitiallized) {
+ return;
+ }
+ int startSize = (int) mMinTextSize;
+ int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom()
+ - getCompoundPaddingTop();
+ mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft()
+ - getCompoundPaddingRight();
+ mAvailableSpaceRect.right = mWidthLimit;
+ mAvailableSpaceRect.bottom = heightLimit;
+ super.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ efficientTextSizeSearch(startSize, (int) mMaxTextSize,
+ mSizeTester, mAvailableSpaceRect));
+}
+
+private final SizeTester mSizeTester = new SizeTester() {
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public int onTestSize(int suggestedSize, RectF availableSPace) {
+ mPaint.setTextSize(suggestedSize);
+ String text = getText().toString();
+ boolean singleline = getMaxLines() == 1;
+ if (singleline) {
+ mTextRect.bottom = mPaint.getFontSpacing();
+ mTextRect.right = mPaint.measureText(text);
+ } else {
+ StaticLayout layout = new StaticLayout(text, mPaint,
+ mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
+ mSpacingAdd, true);
+ // return early if we have more lines
+ if (getMaxLines() != NO_LINE_LIMIT
+ && layout.getLineCount() > getMaxLines()) {
+ return 1;
+ }
+ mTextRect.bottom = layout.getHeight();
+ int maxWidth = -1;
+ for (int i = 0; i < layout.getLineCount(); i++) {
+ if (maxWidth < layout.getLineWidth(i)) {
+ maxWidth = (int) layout.getLineWidth(i);
+ }
+ }
+ mTextRect.right = maxWidth;
+ }
+
+ mTextRect.offsetTo(0, 0);
+ if (availableSPace.contains(mTextRect)) {
+ // may be too small, don't worry we will find the best match
+ return -1;
+ } else {
+ // too big
+ return 1;
+ }
+ }
+};
+
+/**
+ * Enables or disables size caching, enabling it will improve performance
+ * where you are animating a value inside TextView. This stores the font
+ * size against getText().length() Be careful though while enabling it as 0
+ * takes more space than 1 on some fonts and so on.
+ *
+ * @param enable
+ * enable font size caching
+ */
+public void enableSizeCache(boolean enable) {
+ mEnableSizeCache = enable;
+ mTextCachedSizes.clear();
+ adjustTextSize(getText().toString());
+}
+
+private int efficientTextSizeSearch(int start, int end,
+ SizeTester sizeTester, RectF availableSpace) {
+ if (!mEnableSizeCache) {
+ return binarySearch(start, end, sizeTester, availableSpace);
+ }
+ String text = getText().toString();
+ int key = text == null ? 0 : text.length();
+ int size = mTextCachedSizes.get(key);
+ if (size != 0) {
+ return size;
+ }
+ size = binarySearch(start, end, sizeTester, availableSpace);
+ mTextCachedSizes.put(key, size);
+ return size;
+}
+
+private static int binarySearch(int start, int end, SizeTester sizeTester,
+ RectF availableSpace) {
+ int lastBest = start;
+ int lo = start;
+ int hi = end - 1;
+ int mid = 0;
+ while (lo <= hi) {
+ mid = (lo + hi) >>> 1;
+ int midValCmp = sizeTester.onTestSize(mid, availableSpace);
+ if (midValCmp < 0) {
+ lastBest = lo;
+ lo = mid + 1;
+ } else if (midValCmp > 0) {
+ hi = mid - 1;
+ lastBest = hi;
+ } else {
+ return mid;
+ }
+ }
+ // make sure to return last best
+ // this is what should always be returned
+ return lastBest;
+
+}
+
+@Override
+protected void onTextChanged(final CharSequence text, final int start,
+ final int before, final int after) {
+ super.onTextChanged(text, start, before, after);
+ reAdjust();
+}
+
+@Override
+protected void onSizeChanged(int width, int height, int oldwidth,
+ int oldheight) {
+ mTextCachedSizes.clear();
+ super.onSizeChanged(width, height, oldwidth, oldheight);
+ if (width != oldwidth || height != oldheight) {
+ reAdjust();
+ }
+}
+}
diff --git a/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/ImageUtils.java b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/ImageUtils.java
new file mode 100644
index 000000000..3eefa9366
--- /dev/null
+++ b/libraries/AndroidBootstrap/src/com/beardedhen/androidbootstrap/utils/ImageUtils.java
@@ -0,0 +1,77 @@
+package com.beardedhen.androidbootstrap.utils;
+
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Bitmap.Config;
+import android.graphics.PorterDuff.Mode;
+
+public class ImageUtils
+{
+
+ public static Bitmap getCircleBitmap(Bitmap bitmap)
+ {
+ return getCircleBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight());
+ }
+
+ public static Bitmap getCircleBitmap(Bitmap bitmap, int width, int height)
+ {
+ Bitmap croppedBitmap = scaleCenterCrop(bitmap, width, height);
+ Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+
+ final int color = 0xff424242;
+ final Paint paint = new Paint();
+
+ final Rect rect = new Rect(0, 0, width, height);
+ final RectF rectF = new RectF(rect);
+
+ paint.setAntiAlias(true);
+ canvas.drawARGB(0, 0, 0, 0);
+ paint.setColor(color);
+
+ int radius = 0;
+ if(width > height)
+ {
+ radius = height / 2;
+ }
+ else
+ {
+ radius = width / 2;
+ }
+
+ canvas.drawCircle(width / 2, height / 2, radius, paint);
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ canvas.drawBitmap(croppedBitmap, rect, rect, paint);
+
+ return output;
+ }
+
+ public static Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth)
+ {
+ int sourceWidth = source.getWidth();
+ int sourceHeight = source.getHeight();
+
+ float xScale = (float) newWidth / sourceWidth;
+ float yScale = (float) newHeight / sourceHeight;
+ float scale = Math.max(xScale, yScale);
+
+ float scaledWidth = scale * sourceWidth;
+ float scaledHeight = scale * sourceHeight;
+
+ float left = (newWidth - scaledWidth) / 2;
+ float top = (newHeight - scaledHeight) / 2;
+
+ RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
+
+ Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
+ Canvas canvas = new Canvas(dest);
+ canvas.drawBitmap(source, null, targetRect, null);
+
+ return dest;
+ }
+} \ No newline at end of file
diff --git a/libraries/StickyListHeaders/.gitignore b/libraries/StickyListHeaders/.gitignore
new file mode 100644
index 000000000..71d84cca6
--- /dev/null
+++ b/libraries/StickyListHeaders/.gitignore
@@ -0,0 +1,98 @@
+###Android###
+
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Eclipse project files
+.classpath
+.project
+
+
+###Eclipse###
+
+*.pydevproject
+.project
+.metadata
+bin/**
+tmp/**
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+
+###Maven###
+
+target/
+
+
+###Gradle###
+
+.gradle/
+build/
+
+
+###IntelliJ###
+
+*.iml
+*.ipr
+*.iws
+.idea/
+
+
+###Actionscript###
+
+# Build and Release Folders
+bin/
+bin-debug/
+bin-release/
+
+# Project property files
+.actionScriptProperties
+.flexProperties
+.settings/
+.project
+
+###OSX###
+
+.DS_Store
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+
diff --git a/libraries/StickyListHeaders/LICENSE b/libraries/StickyListHeaders/LICENSE
new file mode 100644
index 000000000..37ec93a14
--- /dev/null
+++ b/libraries/StickyListHeaders/LICENSE
@@ -0,0 +1,191 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/libraries/StickyListHeaders/README.md b/libraries/StickyListHeaders/README.md
new file mode 100644
index 000000000..2cfff2991
--- /dev/null
+++ b/libraries/StickyListHeaders/README.md
@@ -0,0 +1,203 @@
+StickyListHeaders
+=================
+StickyListHeaders is an Android library that makes it easy to integrate section headers in your `ListView`. These section headers stick to the top like in the new People app of Android 4.0 Ice Cream Sandwich. This behavior is also found in lists with sections on iOS devices. This library can also be used without the sticky functionality if you just want section headers.
+
+StickyListHeaders actively supports android versions 2.3 (gingerbread) and above.
+That said, it works all the way down to 2.1 but is not actively tested or working perfectly.
+
+Here is a short gif showing the functionality you get with this library:
+
+![alt text](https://github.com/emilsjolander/StickyListHeaders/raw/master/demo.gif "Demo gif")
+
+
+Goal
+----
+The goal of this project is to deliver a high performance replacement to `ListView`. You should with minimal effort and time be able to add section headers to a list. This should be done via a simple to use API without any special features. This library will always priorities general use cases over special ones. This means that the library will add very few public methods to the standard `ListView` and will not try to work for every use case. While I will want to support even narrow use cases I will not do so if it compromises the API or any other feature.
+
+
+Installing
+---------------
+###Gradle
+Add the following gradle dependency exchanging `x.x.x` for the latest release.
+```groovy
+dependencies {
+ compile 'se.emilsjolander:stickylistheaders:x.x.x'
+}
+```
+
+###Cloning
+First of all you will have to clone the library.
+```shell
+git clone https://github.com/emilsjolander/StickyListHeaders.git
+```
+
+Now that you have the library you will have to import it into Android Studio.
+In Android Studio navigate the menus like this.
+```
+File -> Import Project ...
+```
+In the following dialog navigate to StickyListHeaders which you cloned to your computer in the previous steps and select the `build.gradle`.
+
+Getting Started
+---------------
+Ok lets start with your activities or fragments xml file. It might look something like this.
+```xml
+<se.emilsjolander.stickylistheaders.StickyListHeadersListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+```
+
+Now in your activities `onCreate()` or your fragments `onCreateView()` you would want to do something like this
+```java
+StickyListHeadersListView stickyList = (StickyListHeadersListView) findViewById(R.id.list);
+MyAdapter adapter = new MyAdapter(this);
+stickyList.setAdapter(adapter);
+```
+
+`MyAdapter` in the above example would look something like this if your list was a list of countries where each header was for a letter in the alphabet.
+```java
+public class MyAdapter extends BaseAdapter implements StickyListHeadersAdapter {
+
+ private String[] countries;
+ private LayoutInflater inflater;
+
+ public TestBaseAdapter(Context context) {
+ inflater = LayoutInflater.from(context);
+ countries = context.getResources().getStringArray(R.array.countries);
+ }
+
+ @Override
+ public int getCount() {
+ return countries.length;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return countries[position];
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder holder;
+
+ if (convertView == null) {
+ holder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.test_list_item_layout, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.text);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ holder.text.setText(countries[position]);
+
+ return convertView;
+ }
+
+ @Override
+ public View getHeaderView(int position, View convertView, ViewGroup parent) {
+ HeaderViewHolder holder;
+ if (convertView == null) {
+ holder = new HeaderViewHolder();
+ convertView = inflater.inflate(R.layout.header, parent, false);
+ holder.text = (TextView) convertView.findViewById(R.id.text);
+ convertView.setTag(holder);
+ } else {
+ holder = (HeaderViewHolder) convertView.getTag();
+ }
+ //set header text as first char in name
+ String headerText = "" + countries[position].subSequence(0, 1).charAt(0);
+ holder.text.setText(headerText);
+ return convertView;
+ }
+
+ @Override
+ public long getHeaderId(int position) {
+ //return the first character of the country as ID because this is what headers are based upon
+ return countries[position].subSequence(0, 1).charAt(0);
+ }
+
+ class HeaderViewHolder {
+ TextView text;
+ }
+
+ class ViewHolder {
+ TextView text;
+ }
+
+}
+```
+
+That's it! Look through the API docs below to get know about things to customize and if you have any problems getting started please open an issue as it probably means the getting started guide need some improvement!
+
+Upgrading from 1.x versions
+---------------------------
+First of all the package name has changed from `com.emilsjolander.components.stickylistheaders` -> `se.emilsjolander.stickylistheaders` so update all your imports and xml files using StickyListHeaders!
+
+If you are Upgrading from a version prior to 2.x you might run into the following problems.
+1. `StickyListHeadersListView` is no longer a `ListView` subclass. This means that it cannot be passed into a method expecting a ListView. You can retrieve an instance of the `ListView` via `getWrappedList()` but use this with caution as things will probably break if you start setting things directly on that list.
+2. Because `StickyListHeadersListView` is no longer a `ListView` it does not support all the methods. I have implemented delegate methods for all the usual methods and gladly accept pull requests for more.
+
+API
+---
+###StickyListHeadersAdapter
+```java
+public interface StickyListHeadersAdapter extends ListAdapter {
+ View getHeaderView(int position, View convertView, ViewGroup parent);
+ long getHeaderId(int position);
+}
+```
+Your adapter must implement this interface to function with `StickyListHeadersListView`.
+`getHeaderId()` must return a unique integer for every section. A valid implementation for a list with alphabetical sections is the return the char value of the section that `position` is a part of.
+
+`getHeaderView()` works exactly like `getView()` in a regular `ListAdapter`.
+
+
+###StickyListHeadersListView
+Headers are sticky by default but that can easily be changed with this setter. There is of course also a matching getter for the sticky property.
+```java
+public void setAreHeadersSticky(boolean areHeadersSticky);
+public boolean areHeadersSticky();
+```
+
+A `OnHeaderClickListener` is the header version of OnItemClickListener. This is the setter for it and the interface of the listener. The currentlySticky boolean flag indicated if the header that was clicked was sticking to the top at the time it was clicked.
+```java
+public void setOnHeaderClickListener(OnHeaderClickListener listener);
+
+public interface OnHeaderClickListener {
+ public void onHeaderClick(StickyListHeadersListView l, View header, int itemPosition, long headerId, boolean currentlySticky);
+}
+```
+
+A `OnStickyHeaderOffsetChangedListener` is a Listener used for listening to when the sticky header slides out of the screen. The offset parameter will slowly grow to be the same size as the headers height. Use the listeners callback to transform the header in any way you see fit, the standard android contacts app dims the text for example.
+```java
+public void setOnStickyHeaderOffsetChangedListener(OnStickyHeaderOffsetChangedListener listener);
+
+public interface OnStickyHeaderOffsetChangedListener {
+ public void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset);
+}
+```
+
+Here are two methods added to the API for inspecting the children of the underlying `ListView`. I could not override the normal `getChildAt()` and `getChildCount()` methods as that would mess up the underlying measurement system of the `FrameLayout` wrapping the `ListView`.
+```java
+public View getListChildAt(int index);
+public int getListChildCount();
+```
+
+This is a setter and getter for an internal attribute that controls if the list should be drawn under the stuck header. The default value is true. If you do not want to see the list scroll under your header you will want to set this attribute to `false`.
+```java
+public void setDrawingListUnderStickyHeader(boolean drawingListUnderStickyHeader);
+public boolean isDrawingListUnderStickyHeader();
+```
+
+Contributing
+------------
+Contributions are very welcome. Now that this library has grown in popularity i have a hard time keeping upp with all the issues while tending to a multitude of other projects as well as school. So if you find a bug in the library or want a feature and think you can fix it yourself, fork + pull request and i will greatly appreciate it!
+
+I love getting pull requests for new features as well as bugs. However, when it comes to new features please also explain the use case and way you think the library should include it. If you don't want to start coding a feature without knowing if the feature will have chance of being included, open an issue and we can discuss the feature!
diff --git a/libraries/StickyListHeaders/library/.gitignore b/libraries/StickyListHeaders/library/.gitignore
new file mode 100644
index 000000000..f8b92c3aa
--- /dev/null
+++ b/libraries/StickyListHeaders/library/.gitignore
@@ -0,0 +1,2 @@
+.gradle
+build
diff --git a/libraries/StickyListHeaders/library/AndroidManifest.xml b/libraries/StickyListHeaders/library/AndroidManifest.xml
new file mode 100644
index 000000000..328dcc65c
--- /dev/null
+++ b/libraries/StickyListHeaders/library/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="se.emilsjolander.stickylistheaders"
+ android:versionCode="2"
+ android:versionName="2.0" >
+
+ <uses-sdk
+ android:minSdkVersion="7"
+ android:targetSdkVersion="18" />
+
+</manifest> \ No newline at end of file
diff --git a/libraries/StickyListHeaders/library/build.gradle b/libraries/StickyListHeaders/library/build.gradle
new file mode 100644
index 000000000..21050fc98
--- /dev/null
+++ b/libraries/StickyListHeaders/library/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.0'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
diff --git a/libraries/StickyListHeaders/library/build.xml b/libraries/StickyListHeaders/library/build.xml
new file mode 100644
index 000000000..2f6f323a2
--- /dev/null
+++ b/libraries/StickyListHeaders/library/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="library" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <property file="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- if sdk.dir was not set from one of the property file, then
+ get it from the ANDROID_HOME env var.
+ This must be done before we load project.properties since
+ the proguard config can use sdk.dir -->
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+
+ <!--
+ Import per project custom build rules if present at the root of the project.
+ This is the place to put custom intermediary targets such as:
+ -pre-build
+ -pre-compile
+ -post-compile (This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir})
+ -post-package
+ -post-build
+ -pre-clean
+ -->
+ <import file="custom_rules.xml" optional="true" />
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/libraries/StickyListHeaders/library/proguard-project.txt b/libraries/StickyListHeaders/library/proguard-project.txt
new file mode 100644
index 000000000..f2fe1559a
--- /dev/null
+++ b/libraries/StickyListHeaders/library/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/libraries/StickyListHeaders/library/project.properties b/libraries/StickyListHeaders/library/project.properties
new file mode 100644
index 000000000..91d2b0246
--- /dev/null
+++ b/libraries/StickyListHeaders/library/project.properties
@@ -0,0 +1,15 @@
+# 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 edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/libraries/StickyListHeaders/library/res/values/attrs.xml b/libraries/StickyListHeaders/library/res/values/attrs.xml
new file mode 100644
index 000000000..8d09a9c1b
--- /dev/null
+++ b/libraries/StickyListHeaders/library/res/values/attrs.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <declare-styleable name="StickyListHeadersListView">
+ <!-- View attributes -->
+ <attr name="android:clipToPadding" />
+ <attr name="android:padding" />
+ <attr name="android:paddingLeft" />
+ <attr name="android:paddingTop" />
+ <attr name="android:paddingRight" />
+ <attr name="android:paddingBottom" />
+
+ <!-- ListView attributes -->
+ <attr name="android:fadingEdgeLength" />
+ <attr name="android:requiresFadingEdge" />
+ <attr name="android:cacheColorHint" />
+ <attr name="android:choiceMode" />
+ <attr name="android:drawSelectorOnTop" />
+ <attr name="android:fastScrollEnabled" />
+ <attr name="android:fastScrollAlwaysVisible" />
+ <attr name="android:listSelector" />
+ <attr name="android:scrollingCache" />
+ <attr name="android:scrollbarStyle" />
+ <attr name="android:divider" />
+ <attr name="android:dividerHeight" />
+
+ <!-- StickyListHeaders attributes -->
+ <attr name="hasStickyHeaders" format="boolean" />
+ <attr name="isDrawingListUnderStickyHeader" format="boolean" />
+ </declare-styleable>
+
+</resources> \ No newline at end of file
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java
new file mode 100644
index 000000000..e67de0dbf
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/AdapterWrapper.java
@@ -0,0 +1,225 @@
+package se.emilsjolander.stickylistheaders;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.content.Context;
+import android.database.DataSetObserver;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Checkable;
+import android.widget.ListAdapter;
+
+/**
+ * A {@link ListAdapter} which wraps a {@link StickyListHeadersAdapter} and
+ * automatically handles wrapping the result of
+ * {@link StickyListHeadersAdapter#getView(int, android.view.View, android.view.ViewGroup)}
+ * and
+ * {@link StickyListHeadersAdapter#getHeaderView(int, android.view.View, android.view.ViewGroup)}
+ * appropriately.
+ *
+ * @author Jake Wharton (jakewharton@gmail.com)
+ */
+class AdapterWrapper extends BaseAdapter implements StickyListHeadersAdapter {
+
+ interface OnHeaderClickListener {
+ public void onHeaderClick(View header, int itemPosition, long headerId);
+ }
+
+ final StickyListHeadersAdapter mDelegate;
+ private final List<View> mHeaderCache = new LinkedList<View>();
+ private final Context mContext;
+ private Drawable mDivider;
+ private int mDividerHeight;
+ private OnHeaderClickListener mOnHeaderClickListener;
+ private DataSetObserver mDataSetObserver = new DataSetObserver() {
+
+ @Override
+ public void onInvalidated() {
+ mHeaderCache.clear();
+ AdapterWrapper.super.notifyDataSetInvalidated();
+ }
+
+ @Override
+ public void onChanged() {
+ AdapterWrapper.super.notifyDataSetChanged();
+ }
+ };
+
+ AdapterWrapper(Context context,
+ StickyListHeadersAdapter delegate) {
+ this.mContext = context;
+ this.mDelegate = delegate;
+ delegate.registerDataSetObserver(mDataSetObserver);
+ }
+
+ void setDivider(Drawable divider, int dividerHeight) {
+ this.mDivider = divider;
+ this.mDividerHeight = dividerHeight;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return mDelegate.areAllItemsEnabled();
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return mDelegate.isEnabled(position);
+ }
+
+ @Override
+ public int getCount() {
+ return mDelegate.getCount();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mDelegate.getItem(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mDelegate.getItemId(position);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return mDelegate.hasStableIds();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mDelegate.getItemViewType(position);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return mDelegate.getViewTypeCount();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mDelegate.isEmpty();
+ }
+
+ /**
+ * Will recycle header from {@link WrapperView} if it exists
+ */
+ private void recycleHeaderIfExists(WrapperView wv) {
+ View header = wv.mHeader;
+ if (header != null) {
+ // reset the headers visibility when adding it to the cache
+ header.setVisibility(View.VISIBLE);
+ mHeaderCache.add(header);
+ }
+ }
+
+ /**
+ * Get a header view. This optionally pulls a header from the supplied
+ * {@link WrapperView} and will also recycle the divider if it exists.
+ */
+ private View configureHeader(WrapperView wv, final int position) {
+ View header = wv.mHeader == null ? popHeader() : wv.mHeader;
+ header = mDelegate.getHeaderView(position, header, wv);
+ if (header == null) {
+ throw new NullPointerException("Header view must not be null.");
+ }
+ //if the header isn't clickable, the listselector will be drawn on top of the header
+ header.setClickable(true);
+ header.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if(mOnHeaderClickListener != null){
+ long headerId = mDelegate.getHeaderId(position);
+ mOnHeaderClickListener.onHeaderClick(v, position, headerId);
+ }
+ }
+ });
+ return header;
+ }
+
+ private View popHeader() {
+ if(mHeaderCache.size() > 0) {
+ return mHeaderCache.remove(0);
+ }
+ return null;
+ }
+
+ /** Returns {@code true} if the previous position has the same header ID. */
+ private boolean previousPositionHasSameHeader(int position) {
+ return position != 0
+ && mDelegate.getHeaderId(position) == mDelegate
+ .getHeaderId(position - 1);
+ }
+
+ @Override
+ public WrapperView getView(int position, View convertView, ViewGroup parent) {
+ WrapperView wv = (convertView == null) ? new WrapperView(mContext) : (WrapperView) convertView;
+ View item = mDelegate.getView(position, wv.mItem, parent);
+ View header = null;
+ if (previousPositionHasSameHeader(position)) {
+ recycleHeaderIfExists(wv);
+ } else {
+ header = configureHeader(wv, position);
+ }
+ if((item instanceof Checkable) && !(wv instanceof CheckableWrapperView)) {
+ // Need to create Checkable subclass of WrapperView for ListView to work correctly
+ wv = new CheckableWrapperView(mContext);
+ } else if(!(item instanceof Checkable) && (wv instanceof CheckableWrapperView)) {
+ wv = new WrapperView(mContext);
+ }
+ wv.update(item, header, mDivider, mDividerHeight);
+ return wv;
+ }
+
+ public void setOnHeaderClickListener(OnHeaderClickListener onHeaderClickListener){
+ this.mOnHeaderClickListener = onHeaderClickListener;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return mDelegate.equals(o);
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ return ((BaseAdapter) mDelegate).getDropDownView(position, convertView, parent);
+ }
+
+ @Override
+ public int hashCode() {
+ return mDelegate.hashCode();
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ ((BaseAdapter) mDelegate).notifyDataSetChanged();
+ }
+
+ @Override
+ public void notifyDataSetInvalidated() {
+ ((BaseAdapter) mDelegate).notifyDataSetInvalidated();
+ }
+
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+
+ @Override
+ public View getHeaderView(int position, View convertView, ViewGroup parent) {
+ return mDelegate.getHeaderView(position, convertView, parent);
+ }
+
+ @Override
+ public long getHeaderId(int position) {
+ return mDelegate.getHeaderId(position);
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java
new file mode 100644
index 000000000..5b0a83827
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/ApiLevelTooLowException.java
@@ -0,0 +1,11 @@
+package se.emilsjolander.stickylistheaders;
+
+public class ApiLevelTooLowException extends RuntimeException {
+
+ private static final long serialVersionUID = -5480068364264456757L;
+
+ public ApiLevelTooLowException(int versionCode) {
+ super("Requires API level " + versionCode);
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java
new file mode 100644
index 000000000..9039c3f5c
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/CheckableWrapperView.java
@@ -0,0 +1,31 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.content.Context;
+import android.widget.Checkable;
+
+/**
+ * A WrapperView that implements the checkable interface
+ *
+ * @author Emil Sjölander
+ */
+class CheckableWrapperView extends WrapperView implements Checkable {
+
+ public CheckableWrapperView(final Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return ((Checkable) mItem).isChecked();
+ }
+
+ @Override
+ public void setChecked(final boolean checked) {
+ ((Checkable) mItem).setChecked(checked);
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!isChecked());
+ }
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java
new file mode 100644
index 000000000..ff7e293af
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java
@@ -0,0 +1,32 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.content.Context;
+import android.widget.SectionIndexer;
+
+class SectionIndexerAdapterWrapper extends
+ AdapterWrapper implements SectionIndexer {
+
+ final SectionIndexer mSectionIndexerDelegate;
+
+ SectionIndexerAdapterWrapper(Context context,
+ StickyListHeadersAdapter delegate) {
+ super(context, delegate);
+ mSectionIndexerDelegate = (SectionIndexer) delegate;
+ }
+
+ @Override
+ public int getPositionForSection(int section) {
+ return mSectionIndexerDelegate.getPositionForSection(section);
+ }
+
+ @Override
+ public int getSectionForPosition(int position) {
+ return mSectionIndexerDelegate.getSectionForPosition(position);
+ }
+
+ @Override
+ public Object[] getSections() {
+ return mSectionIndexerDelegate.getSections();
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java
new file mode 100644
index 000000000..8b80b71f1
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java
@@ -0,0 +1,38 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListAdapter;
+
+public interface StickyListHeadersAdapter extends ListAdapter {
+ /**
+ * Get a View that displays the header data at the specified position in the
+ * set. You can either create a View manually or inflate it from an XML layout
+ * file.
+ *
+ * @param position
+ * The position of the item within the adapter's data set of the item whose
+ * header view we want.
+ * @param convertView
+ * The old view to reuse, if possible. Note: You should check that this view is
+ * non-null and of an appropriate type before using. If it is not possible to
+ * convert this view to display the correct data, this method can create a new
+ * view.
+ * @param parent
+ * The parent that this view will eventually be attached to.
+ * @return
+ * A View corresponding to the data at the specified position.
+ */
+ View getHeaderView(int position, View convertView, ViewGroup parent);
+
+ /**
+ * Get the header id associated with the specified position in the list.
+ *
+ * @param position
+ * The position of the item within the adapter's data set whose header id we
+ * want.
+ * @return
+ * The id of the header at the specified position.
+ */
+ long getHeaderId(int position);
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
new file mode 100644
index 000000000..476f6cfad
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/StickyListHeadersListView.java
@@ -0,0 +1,925 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.AdapterView.OnItemLongClickListener;
+import android.widget.FrameLayout;
+import android.widget.ListView;
+import android.widget.SectionIndexer;
+
+import se.emilsjolander.stickylistheaders.WrapperViewList.LifeCycleListener;
+
+/**
+ * Even though this is a FrameLayout subclass we it is called a ListView. This
+ * is because of 2 reasons. 1. It acts like as ListView 2. It used to be a
+ * ListView subclass and i did not was to change to name causing compatibility
+ * errors.
+ *
+ * @author Emil Sjölander
+ */
+public class StickyListHeadersListView extends FrameLayout {
+
+ public interface OnHeaderClickListener {
+ public void onHeaderClick(StickyListHeadersListView l, View header,
+ int itemPosition, long headerId, boolean currentlySticky);
+ }
+
+ /**
+ * Notifies the listener when the sticky headers top offset has changed.
+ */
+ public interface OnStickyHeaderOffsetChangedListener {
+ /**
+ * @param l The view parent
+ * @param header The currently sticky header being offset.
+ * This header is not guaranteed to have it's measurements set.
+ * It is however guaranteed that this view has been measured,
+ * therefor you should user getMeasured* methods instead of
+ * get* methods for determining the view's size.
+ * @param offset The amount the sticky header is offset by towards to top of the screen.
+ */
+ public void onStickyHeaderOffsetChanged(StickyListHeadersListView l, View header, int offset);
+ }
+
+ /* --- Children --- */
+ private WrapperViewList mList;
+ private View mHeader;
+
+ /* --- Header state --- */
+ private Long mHeaderId;
+ // used to not have to call getHeaderId() all the time
+ private Integer mHeaderPosition;
+ private Integer mHeaderOffset;
+
+ /* --- Delegates --- */
+ private OnScrollListener mOnScrollListenerDelegate;
+ private AdapterWrapper mAdapter;
+
+ /* --- Settings --- */
+ private boolean mAreHeadersSticky = true;
+ private boolean mClippingToPadding = true;
+ private boolean mIsDrawingListUnderStickyHeader = true;
+ private int mPaddingLeft = 0;
+ private int mPaddingTop = 0;
+ private int mPaddingRight = 0;
+ private int mPaddingBottom = 0;
+
+ /* --- Other --- */
+ private OnHeaderClickListener mOnHeaderClickListener;
+ private OnStickyHeaderOffsetChangedListener mOnStickyHeaderOffsetChangedListener;
+ private AdapterWrapperDataSetObserver mDataSetObserver;
+ private Drawable mDivider;
+ private int mDividerHeight;
+
+ public StickyListHeadersListView(Context context) {
+ this(context, null);
+ }
+
+ public StickyListHeadersListView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public StickyListHeadersListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ // Initialize the wrapped list
+ mList = new WrapperViewList(context);
+
+ // null out divider, dividers are handled by adapter so they look good with headers
+ mDivider = mList.getDivider();
+ mDividerHeight = mList.getDividerHeight();
+ mList.setDivider(null);
+ mList.setDividerHeight(0);
+
+ mList.setVerticalScrollBarEnabled(isVerticalScrollBarEnabled());
+ mList.setHorizontalScrollBarEnabled(isHorizontalScrollBarEnabled());
+
+ if (attrs != null) {
+ TypedArray a = context.getTheme().obtainStyledAttributes(attrs,R.styleable.StickyListHeadersListView, 0, 0);
+
+ try {
+ // -- View attributes --
+ int padding = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_padding, 0);
+ mPaddingLeft = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingLeft, padding);
+ mPaddingTop = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingTop, padding);
+ mPaddingRight = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingRight, padding);
+ mPaddingBottom = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_paddingBottom, padding);
+
+ setPadding(mPaddingLeft, mPaddingTop, mPaddingRight,
+ mPaddingBottom);
+
+ // Set clip to padding on the list and reset value to default on
+ // wrapper
+ mClippingToPadding = a.getBoolean(R.styleable.StickyListHeadersListView_android_clipToPadding, true);
+ super.setClipToPadding(true);
+ mList.setClipToPadding(mClippingToPadding);
+
+ // -- ListView attributes --
+ mList.setFadingEdgeLength(a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_fadingEdgeLength,
+ mList.getVerticalFadingEdgeLength()));
+ final int fadingEdge = a.getInt(R.styleable.StickyListHeadersListView_android_requiresFadingEdge, 0);
+ if (fadingEdge == 0x00001000) {
+ mList.setVerticalFadingEdgeEnabled(false);
+ mList.setHorizontalFadingEdgeEnabled(true);
+ } else if (fadingEdge == 0x00002000) {
+ mList.setVerticalFadingEdgeEnabled(true);
+ mList.setHorizontalFadingEdgeEnabled(false);
+ } else {
+ mList.setVerticalFadingEdgeEnabled(false);
+ mList.setHorizontalFadingEdgeEnabled(false);
+ }
+ mList.setCacheColorHint(a
+ .getColor(R.styleable.StickyListHeadersListView_android_cacheColorHint, mList.getCacheColorHint()));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mList.setChoiceMode(a.getInt(R.styleable.StickyListHeadersListView_android_choiceMode,
+ mList.getChoiceMode()));
+ }
+ mList.setDrawSelectorOnTop(a.getBoolean(R.styleable.StickyListHeadersListView_android_drawSelectorOnTop, false));
+ mList.setFastScrollEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_fastScrollEnabled,
+ mList.isFastScrollEnabled()));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mList.setFastScrollAlwaysVisible(a.getBoolean(
+ R.styleable.StickyListHeadersListView_android_fastScrollAlwaysVisible,
+ mList.isFastScrollAlwaysVisible()));
+ }
+
+ mList.setScrollBarStyle(a.getInt(R.styleable.StickyListHeadersListView_android_scrollbarStyle, 0));
+
+ if (a.hasValue(R.styleable.StickyListHeadersListView_android_listSelector)) {
+ mList.setSelector(a.getDrawable(R.styleable.StickyListHeadersListView_android_listSelector));
+ }
+
+ mList.setScrollingCacheEnabled(a.getBoolean(R.styleable.StickyListHeadersListView_android_scrollingCache,
+ mList.isScrollingCacheEnabled()));
+
+ if (a.hasValue(R.styleable.StickyListHeadersListView_android_divider)) {
+ mDivider = a.getDrawable(R.styleable.StickyListHeadersListView_android_divider);
+ }
+
+ mDividerHeight = a.getDimensionPixelSize(R.styleable.StickyListHeadersListView_android_dividerHeight,
+ mDividerHeight);
+
+ // -- StickyListHeaders attributes --
+ mAreHeadersSticky = a.getBoolean(R.styleable.StickyListHeadersListView_hasStickyHeaders, true);
+ mIsDrawingListUnderStickyHeader = a.getBoolean(
+ R.styleable.StickyListHeadersListView_isDrawingListUnderStickyHeader,
+ true);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ // attach some listeners to the wrapped list
+ mList.setLifeCycleListener(new WrapperViewListLifeCycleListener());
+ mList.setOnScrollListener(new WrapperListScrollListener());
+
+ addView(mList);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ measureHeader(mHeader);
+ }
+
+ private void ensureHeaderHasCorrectLayoutParams(View header) {
+ ViewGroup.LayoutParams lp = header.getLayoutParams();
+ if (lp == null) {
+ lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ } else if (lp.height == LayoutParams.MATCH_PARENT) {
+ lp.height = LayoutParams.WRAP_CONTENT;
+ }
+ header.setLayoutParams(lp);
+ }
+
+ private void measureHeader(View header) {
+ if (header != null) {
+ final int width = getMeasuredWidth() - mPaddingLeft - mPaddingRight;
+ final int parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ width, MeasureSpec.EXACTLY);
+ final int parentHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0,
+ MeasureSpec.UNSPECIFIED);
+ measureChild(header, parentWidthMeasureSpec,
+ parentHeightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right,
+ int bottom) {
+ mList.layout(0, 0, mList.getMeasuredWidth(), getHeight());
+ if (mHeader != null) {
+ MarginLayoutParams lp = (MarginLayoutParams) mHeader
+ .getLayoutParams();
+ int headerTop = lp.topMargin
+ + (mClippingToPadding ? mPaddingTop : 0);
+ // The left parameter must for some reason be set to 0.
+ // I think it should be set to mPaddingLeft but apparently not
+ mHeader.layout(mPaddingLeft, headerTop, mHeader.getMeasuredWidth()
+ + mPaddingLeft, headerTop + mHeader.getMeasuredHeight());
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ // Only draw the list here.
+ // The header should be drawn right after the lists children are drawn.
+ // This is done so that the header is above the list items
+ // but below the list decorators (scroll bars etc).
+ drawChild(canvas, mList, 0);
+ }
+
+ // Reset values tied the header. also remove header form layout
+ // This is called in response to the data set or the adapter changing
+ private void clearHeader() {
+ if (mHeader != null) {
+ removeView(mHeader);
+ mHeader = null;
+ mHeaderId = null;
+ mHeaderPosition = null;
+ mHeaderOffset = null;
+
+ // reset the top clipping length
+ mList.setTopClippingLength(0);
+ updateHeaderVisibilities();
+ }
+ }
+
+ private void updateOrClearHeader(int firstVisiblePosition) {
+ final int adapterCount = mAdapter == null ? 0 : mAdapter.getCount();
+ if (adapterCount == 0 || !mAreHeadersSticky) {
+ return;
+ }
+
+ final int headerViewCount = mList.getHeaderViewsCount();
+ final int realFirstVisibleItem = firstVisiblePosition - headerViewCount;
+
+ // It is not a mistake to call getFirstVisiblePosition() here.
+ // Most of the time getFixedFirstVisibleItem() should be called
+ // but that does not work great together with getChildAt()
+ final boolean doesListHaveChildren = mList.getChildCount() != 0;
+ final boolean isFirstViewBelowTop = doesListHaveChildren && mList
+ .getFirstVisiblePosition() == 0
+ && mList.getChildAt(0).getTop() > (mClippingToPadding ? mPaddingTop : 0);
+ final boolean isFirstVisibleItemOutsideAdapterRange = realFirstVisibleItem > adapterCount - 1
+ || realFirstVisibleItem < 0;
+ if (!doesListHaveChildren || isFirstVisibleItemOutsideAdapterRange
+ || isFirstViewBelowTop) {
+ clearHeader();
+ return;
+ }
+
+ updateHeader(realFirstVisibleItem);
+ }
+
+ private void updateHeader(int firstVisiblePosition) {
+
+ // check if there is a new header should be sticky
+ if (mHeaderPosition == null || mHeaderPosition != firstVisiblePosition) {
+ mHeaderPosition = firstVisiblePosition;
+ final long headerId = mAdapter.getHeaderId(firstVisiblePosition);
+ if (mHeaderId == null || mHeaderId != headerId) {
+ mHeaderId = headerId;
+ final View header = mAdapter.getHeaderView(mHeaderPosition,
+ mHeader, this);
+ if (mHeader != header) {
+ if (header == null) {
+ throw new NullPointerException("header may not be null");
+ }
+ swapHeader(header);
+ }
+
+ ensureHeaderHasCorrectLayoutParams(mHeader);
+ measureHeader(mHeader);
+
+ // Reset mHeaderOffset to null ensuring
+ // that it will be set on the header and
+ // not skipped for performance reasons.
+ mHeaderOffset = null;
+ }
+ }
+
+ int headerOffset = 0;
+
+ // Calculate new header offset
+ // Skip looking at the first view. it never matters because it always
+ // results in a headerOffset = 0
+ int headerBottom = mHeader.getMeasuredHeight()
+ + (mClippingToPadding ? mPaddingTop : 0);
+ for (int i = 0; i < mList.getChildCount(); i++) {
+ final View child = mList.getChildAt(i);
+ final boolean doesChildHaveHeader = child instanceof WrapperView
+ && ((WrapperView) child).hasHeader();
+ final boolean isChildFooter = mList.containsFooterView(child);
+ if (child.getTop() >= (mClippingToPadding ? mPaddingTop : 0)
+ && (doesChildHaveHeader || isChildFooter)) {
+ headerOffset = Math.min(child.getTop() - headerBottom, 0);
+ break;
+ }
+ }
+
+ setHeaderOffet(headerOffset);
+
+ if (!mIsDrawingListUnderStickyHeader) {
+ mList.setTopClippingLength(mHeader.getMeasuredHeight()
+ + mHeaderOffset);
+ }
+
+ updateHeaderVisibilities();
+ }
+
+ private void swapHeader(View newHeader) {
+ if (mHeader != null) {
+ removeView(mHeader);
+ }
+ mHeader = newHeader;
+ addView(mHeader);
+ mHeader.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (mOnHeaderClickListener != null) {
+ mOnHeaderClickListener.onHeaderClick(
+ StickyListHeadersListView.this, mHeader,
+ mHeaderPosition, mHeaderId, true);
+ }
+ }
+
+ });
+ }
+
+ // hides the headers in the list under the sticky header.
+ // Makes sure the other ones are showing
+ private void updateHeaderVisibilities() {
+ int top;
+ if (mHeader != null) {
+ top = mHeader.getMeasuredHeight()
+ + (mHeaderOffset != null ? mHeaderOffset : 0);
+ } else {
+ top = mClippingToPadding ? mPaddingTop : 0;
+ }
+ int childCount = mList.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+
+ // ensure child is a wrapper view
+ View child = mList.getChildAt(i);
+ if (!(child instanceof WrapperView)) {
+ continue;
+ }
+
+ // ensure wrapper view child has a header
+ WrapperView wrapperViewChild = (WrapperView) child;
+ if (!wrapperViewChild.hasHeader()) {
+ continue;
+ }
+
+ // update header views visibility
+ View childHeader = wrapperViewChild.mHeader;
+ if (wrapperViewChild.getTop() < top) {
+ if (childHeader.getVisibility() != View.INVISIBLE) {
+ childHeader.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ if (childHeader.getVisibility() != View.VISIBLE) {
+ childHeader.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+ }
+
+ // Wrapper around setting the header offset in different ways depending on
+ // the API version
+ @SuppressLint("NewApi")
+ private void setHeaderOffet(int offset) {
+ if (mHeaderOffset == null || mHeaderOffset != offset) {
+ mHeaderOffset = offset;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mHeader.setTranslationY(mHeaderOffset);
+ } else {
+ MarginLayoutParams params = (MarginLayoutParams) mHeader
+ .getLayoutParams();
+ params.topMargin = mHeaderOffset;
+ mHeader.setLayoutParams(params);
+ }
+ if (mOnStickyHeaderOffsetChangedListener != null) {
+ mOnStickyHeaderOffsetChangedListener
+ .onStickyHeaderOffsetChanged(this, mHeader, -mHeaderOffset);
+ }
+ }
+ }
+
+ private class AdapterWrapperDataSetObserver extends DataSetObserver {
+
+ @Override
+ public void onChanged() {
+ clearHeader();
+ }
+
+ @Override
+ public void onInvalidated() {
+ clearHeader();
+ }
+
+ }
+
+ private class WrapperListScrollListener implements OnScrollListener {
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem,
+ int visibleItemCount, int totalItemCount) {
+ if (mOnScrollListenerDelegate != null) {
+ mOnScrollListenerDelegate.onScroll(view, firstVisibleItem,
+ visibleItemCount, totalItemCount);
+ }
+ updateOrClearHeader(mList.getFixedFirstVisibleItem());
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (mOnScrollListenerDelegate != null) {
+ mOnScrollListenerDelegate.onScrollStateChanged(view,
+ scrollState);
+ }
+ }
+
+ }
+
+ private class WrapperViewListLifeCycleListener implements LifeCycleListener {
+
+ @Override
+ public void onDispatchDrawOccurred(Canvas canvas) {
+ // onScroll is not called often at all before froyo
+ // therefor we need to update the header here as well.
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
+ updateOrClearHeader(mList.getFixedFirstVisibleItem());
+ }
+ if (mHeader != null) {
+ if (mClippingToPadding) {
+ canvas.save();
+ canvas.clipRect(0, mPaddingTop, getRight(), getBottom());
+ drawChild(canvas, mHeader, 0);
+ canvas.restore();
+ } else {
+ drawChild(canvas, mHeader, 0);
+ }
+ }
+ }
+
+ }
+
+ private class AdapterWrapperHeaderClickHandler implements
+ AdapterWrapper.OnHeaderClickListener {
+
+ @Override
+ public void onHeaderClick(View header, int itemPosition, long headerId) {
+ mOnHeaderClickListener.onHeaderClick(
+ StickyListHeadersListView.this, header, itemPosition,
+ headerId, false);
+ }
+
+ }
+
+ private boolean isStartOfSection(int position) {
+ return position == 0
+ || mAdapter.getHeaderId(position) != mAdapter
+ .getHeaderId(position - 1);
+ }
+
+ private int getHeaderOverlap(int position) {
+ boolean isStartOfSection = isStartOfSection(position);
+ if (!isStartOfSection) {
+ View header = mAdapter.getHeaderView(position, null, mList);
+ if (header == null) {
+ throw new NullPointerException("header may not be null");
+ }
+ ensureHeaderHasCorrectLayoutParams(header);
+ measureHeader(header);
+ return header.getMeasuredHeight();
+ }
+ return 0;
+ }
+
+ /* ---------- StickyListHeaders specific API ---------- */
+
+ public void setAreHeadersSticky(boolean areHeadersSticky) {
+ mAreHeadersSticky = areHeadersSticky;
+ if (!areHeadersSticky) {
+ clearHeader();
+ } else {
+ updateOrClearHeader(mList.getFixedFirstVisibleItem());
+ }
+ // invalidating the list will trigger dispatchDraw()
+ mList.invalidate();
+ }
+
+ public boolean areHeadersSticky() {
+ return mAreHeadersSticky;
+ }
+
+ /**
+ * Use areHeadersSticky() method instead
+ */
+ @Deprecated
+ public boolean getAreHeadersSticky() {
+ return areHeadersSticky();
+ }
+
+ public void setDrawingListUnderStickyHeader(
+ boolean drawingListUnderStickyHeader) {
+ mIsDrawingListUnderStickyHeader = drawingListUnderStickyHeader;
+ // reset the top clipping length
+ mList.setTopClippingLength(0);
+ }
+
+ public boolean isDrawingListUnderStickyHeader() {
+ return mIsDrawingListUnderStickyHeader;
+ }
+
+ public void setOnHeaderClickListener(OnHeaderClickListener listener) {
+ mOnHeaderClickListener = listener;
+ if (mAdapter != null) {
+ if (mOnHeaderClickListener != null) {
+ mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
+ } else {
+ mAdapter.setOnHeaderClickListener(null);
+ }
+ }
+ }
+
+ public void setOnStickyHeaderOffsetChangedListener(OnStickyHeaderOffsetChangedListener listener) {
+ mOnStickyHeaderOffsetChangedListener = listener;
+ }
+
+ public View getListChildAt(int index) {
+ return mList.getChildAt(index);
+ }
+
+ public int getListChildCount() {
+ return mList.getChildCount();
+ }
+
+ /**
+ * Use the method with extreme caution!! Changing any values on the
+ * underlying ListView might break everything.
+ *
+ * @return the ListView backing this view.
+ */
+ public ListView getWrappedList() {
+ return mList;
+ }
+
+ /* ---------- ListView delegate methods ---------- */
+
+ public void setAdapter(StickyListHeadersAdapter adapter) {
+ if (adapter == null) {
+ mList.setAdapter(null);
+ clearHeader();
+ return;
+ }
+ if (mAdapter != null) {
+ mAdapter.unregisterDataSetObserver(mDataSetObserver);
+ }
+
+ if (adapter instanceof SectionIndexer) {
+ mAdapter = new SectionIndexerAdapterWrapper(getContext(), adapter);
+ } else {
+ mAdapter = new AdapterWrapper(getContext(), adapter);
+ }
+ mDataSetObserver = new AdapterWrapperDataSetObserver();
+ mAdapter.registerDataSetObserver(mDataSetObserver);
+
+ if (mOnHeaderClickListener != null) {
+ mAdapter.setOnHeaderClickListener(new AdapterWrapperHeaderClickHandler());
+ } else {
+ mAdapter.setOnHeaderClickListener(null);
+ }
+
+ mAdapter.setDivider(mDivider, mDividerHeight);
+
+ mList.setAdapter(mAdapter);
+ clearHeader();
+ }
+
+ public StickyListHeadersAdapter getAdapter() {
+ return mAdapter == null ? null : mAdapter.mDelegate;
+ }
+
+ public void setDivider(Drawable divider) {
+ mDivider = divider;
+ if (mAdapter != null) {
+ mAdapter.setDivider(mDivider, mDividerHeight);
+ }
+ }
+
+ public void setDividerHeight(int dividerHeight) {
+ mDividerHeight = dividerHeight;
+ if (mAdapter != null) {
+ mAdapter.setDivider(mDivider, mDividerHeight);
+ }
+ }
+
+ public Drawable getDivider() {
+ return mDivider;
+ }
+
+ public int getDividerHeight() {
+ return mDividerHeight;
+ }
+
+ public void setOnScrollListener(OnScrollListener onScrollListener) {
+ mOnScrollListenerDelegate = onScrollListener;
+ }
+
+ public void setOnItemClickListener(OnItemClickListener listener) {
+ mList.setOnItemClickListener(listener);
+ }
+
+ public void setOnItemLongClickListener(OnItemLongClickListener listener) {
+ mList.setOnItemLongClickListener(listener);
+ }
+
+ public void addHeaderView(View v, Object data, boolean isSelectable) {
+ mList.addHeaderView(v, data, isSelectable);
+ }
+
+ public void addHeaderView(View v) {
+ mList.addHeaderView(v);
+ }
+
+ public void removeHeaderView(View v) {
+ mList.removeHeaderView(v);
+ }
+
+ public int getHeaderViewsCount() {
+ return mList.getHeaderViewsCount();
+ }
+
+ public void addFooterView(View v) {
+ mList.addFooterView(v);
+ }
+
+ public void removeFooterView(View v) {
+ mList.removeFooterView(v);
+ }
+
+ public int getFooterViewsCount() {
+ return mList.getFooterViewsCount();
+ }
+
+ public void setEmptyView(View v) {
+ mList.setEmptyView(v);
+ }
+
+ public View getEmptyView() {
+ return mList.getEmptyView();
+ }
+
+ @Override
+ public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) {
+ mList.setVerticalScrollBarEnabled(verticalScrollBarEnabled);
+ }
+
+ @Override
+ public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
+ mList.setHorizontalScrollBarEnabled(horizontalScrollBarEnabled);
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollBy(int distance, int duration) {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ mList.smoothScrollBy(distance, duration);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollByOffset(int offset) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ mList.smoothScrollByOffset(offset);
+ }
+
+ @SuppressLint("NewApi")
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollToPosition(int position) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ mList.smoothScrollToPosition(position);
+ } else {
+ int offset = mAdapter == null ? 0 : getHeaderOverlap(position);
+ offset -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.smoothScrollToPositionFromTop(position, offset);
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public void smoothScrollToPosition(int position, int boundPosition) {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ mList.smoothScrollToPosition(position, boundPosition);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollToPositionFromTop(int position, int offset) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ offset += mAdapter == null ? 0 : getHeaderOverlap(position);
+ offset -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.smoothScrollToPositionFromTop(position, offset);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void smoothScrollToPositionFromTop(int position, int offset,
+ int duration) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ offset += mAdapter == null ? 0 : getHeaderOverlap(position);
+ offset -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.smoothScrollToPositionFromTop(position, offset, duration);
+ }
+
+ public void setSelection(int position) {
+ setSelectionFromTop(position, 0);
+ }
+
+ public void setSelectionAfterHeaderView() {
+ mList.setSelectionAfterHeaderView();
+ }
+
+ public void setSelectionFromTop(int position, int y) {
+ y += mAdapter == null ? 0 : getHeaderOverlap(position);
+ y -= mClippingToPadding ? 0 : mPaddingTop;
+ mList.setSelectionFromTop(position, y);
+ }
+
+ public void setSelector(Drawable sel) {
+ mList.setSelector(sel);
+ }
+
+ public void setSelector(int resID) {
+ mList.setSelector(resID);
+ }
+
+ public int getFirstVisiblePosition() {
+ return mList.getFirstVisiblePosition();
+ }
+
+ public int getLastVisiblePosition() {
+ return mList.getLastVisiblePosition();
+ }
+
+ public void setChoiceMode(int choiceMode) {
+ mList.setChoiceMode(choiceMode);
+ }
+
+ public void setItemChecked(int position, boolean value) {
+ mList.setItemChecked(position, value);
+ }
+
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public int getCheckedItemCount() {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ return mList.getCheckedItemCount();
+ }
+
+ @TargetApi(Build.VERSION_CODES.FROYO)
+ public long[] getCheckedItemIds() {
+ requireSdkVersion(Build.VERSION_CODES.FROYO);
+ return mList.getCheckedItemIds();
+ }
+
+ public int getCheckedItemPosition() {
+ return mList.getCheckedItemPosition();
+ }
+
+ public SparseBooleanArray getCheckedItemPositions() {
+ return mList.getCheckedItemPositions();
+ }
+
+ public int getCount() {
+ return mList.getCount();
+ }
+
+ public Object getItemAtPosition(int position) {
+ return mList.getItemAtPosition(position);
+ }
+
+ public long getItemIdAtPosition(int position) {
+ return mList.getItemIdAtPosition(position);
+ }
+
+ @Override
+ public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
+ mList.setOnCreateContextMenuListener(l);
+ }
+
+ @Override
+ public boolean showContextMenu() {
+ return mList.showContextMenu();
+ }
+
+ public void invalidateViews() {
+ mList.invalidateViews();
+ }
+
+ @Override
+ public void setClipToPadding(boolean clipToPadding) {
+ if (mList != null) {
+ mList.setClipToPadding(clipToPadding);
+ }
+ mClippingToPadding = clipToPadding;
+ }
+
+ @Override
+ public void setPadding(int left, int top, int right, int bottom) {
+ mPaddingLeft = left;
+ mPaddingTop = top;
+ mPaddingRight = right;
+ mPaddingBottom = bottom;
+
+ if (mList != null) {
+ mList.setPadding(left, top, right, bottom);
+ }
+ super.setPadding(0, 0, 0, 0);
+ requestLayout();
+ }
+
+ /*
+ * Overrides an @hide method in View
+ */
+ protected void recomputePadding() {
+ setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
+ }
+
+ @Override
+ public int getPaddingLeft() {
+ return mPaddingLeft;
+ }
+
+ @Override
+ public int getPaddingTop() {
+ return mPaddingTop;
+ }
+
+ @Override
+ public int getPaddingRight() {
+ return mPaddingRight;
+ }
+
+ @Override
+ public int getPaddingBottom() {
+ return mPaddingBottom;
+ }
+
+ public void setFastScrollEnabled(boolean fastScrollEnabled) {
+ mList.setFastScrollEnabled(fastScrollEnabled);
+ }
+
+ /**
+ * @throws ApiLevelTooLowException on pre-Honeycomb device.
+ * @see android.widget.AbsListView#setFastScrollAlwaysVisible(boolean)
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void setFastScrollAlwaysVisible(boolean alwaysVisible) {
+ requireSdkVersion(Build.VERSION_CODES.HONEYCOMB);
+ mList.setFastScrollAlwaysVisible(alwaysVisible);
+ }
+
+ /**
+ * @return true if the fast scroller will always show. False on pre-Honeycomb devices.
+ * @see android.widget.AbsListView#isFastScrollAlwaysVisible()
+ */
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public boolean isFastScrollAlwaysVisible() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ return false;
+ }
+ return mList.isFastScrollAlwaysVisible();
+ }
+
+ public void setScrollBarStyle(int style) {
+ mList.setScrollBarStyle(style);
+ }
+
+ public int getScrollBarStyle() {
+ return mList.getScrollBarStyle();
+ }
+
+ private void requireSdkVersion(int versionCode) {
+ if (Build.VERSION.SDK_INT < versionCode) {
+ throw new ApiLevelTooLowException(versionCode);
+ }
+ }
+
+ public int getPositionForView(View view) {
+ return mList.getPositionForView(view);
+ }
+
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java
new file mode 100644
index 000000000..f51416c1c
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperView.java
@@ -0,0 +1,150 @@
+package se.emilsjolander.stickylistheaders;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+
+/**
+ *
+ * the view that wrapps a divider header and a normal list item. The listview sees this as 1 item
+ *
+ * @author Emil Sjölander
+ */
+public class WrapperView extends ViewGroup {
+
+ View mItem;
+ Drawable mDivider;
+ int mDividerHeight;
+ View mHeader;
+ int mItemTop;
+
+ WrapperView(Context c) {
+ super(c);
+ }
+
+ public boolean hasHeader() {
+ return mHeader != null;
+ }
+
+ public View getItem() {
+ return mItem;
+ }
+
+ public View getHeader() {
+ return mHeader;
+ }
+
+ void update(View item, View header, Drawable divider, int dividerHeight) {
+
+ //every wrapperview must have a list item
+ if (item == null) {
+ throw new NullPointerException("List view item must not be null.");
+ }
+
+ //only remove the current item if it is not the same as the new item. this can happen if wrapping a recycled view
+ if (this.mItem != item) {
+ removeView(this.mItem);
+ this.mItem = item;
+ final ViewParent parent = item.getParent();
+ if(parent != null && parent != this) {
+ if(parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(item);
+ }
+ }
+ addView(item);
+ }
+
+ //same logik as above but for the header
+ if (this.mHeader != header) {
+ if (this.mHeader != null) {
+ removeView(this.mHeader);
+ }
+ this.mHeader = header;
+ if (header != null) {
+ addView(header);
+ }
+ }
+
+ if (this.mDivider != divider) {
+ this.mDivider = divider;
+ this.mDividerHeight = dividerHeight;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth,
+ MeasureSpec.EXACTLY);
+ int measuredHeight = 0;
+
+ //measure header or divider. when there is a header visible it acts as the divider
+ if (mHeader != null) {
+ ViewGroup.LayoutParams params = mHeader.getLayoutParams();
+ if (params != null && params.height > 0) {
+ mHeader.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY));
+ } else {
+ mHeader.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ }
+ measuredHeight += mHeader.getMeasuredHeight();
+ } else if (mDivider != null) {
+ measuredHeight += mDividerHeight;
+ }
+
+ //measure item
+ ViewGroup.LayoutParams params = mItem.getLayoutParams();
+ if (params != null && params.height > 0) {
+ mItem.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY));
+ } else {
+ mItem.measure(childWidthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ }
+ measuredHeight += mItem.getMeasuredHeight();
+
+ setMeasuredDimension(measuredWidth, measuredHeight);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+
+ l = 0;
+ t = 0;
+ r = getWidth();
+ b = getHeight();
+
+ if (mHeader != null) {
+ int headerHeight = mHeader.getMeasuredHeight();
+ mHeader.layout(l, t, r, headerHeight);
+ mItemTop = headerHeight;
+ mItem.layout(l, headerHeight, r, b);
+ } else if (mDivider != null) {
+ mDivider.setBounds(l, t, r, mDividerHeight);
+ mItemTop = mDividerHeight;
+ mItem.layout(l, mDividerHeight, r, b);
+ } else {
+ mItemTop = t;
+ mItem.layout(l, t, r, b);
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (mHeader == null && mDivider != null) {
+ // Drawable.setBounds() does not seem to work pre-honeycomb. So have
+ // to do this instead
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ canvas.clipRect(0, 0, getWidth(), mDividerHeight);
+ }
+ mDivider.draw(canvas);
+ }
+ }
+}
diff --git a/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java
new file mode 100644
index 000000000..3d68e98db
--- /dev/null
+++ b/libraries/StickyListHeaders/library/src/se/emilsjolander/stickylistheaders/WrapperViewList.java
@@ -0,0 +1,175 @@
+package se.emilsjolander.stickylistheaders;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.View;
+import android.widget.AbsListView;
+import android.widget.ListView;
+
+class WrapperViewList extends ListView {
+
+ interface LifeCycleListener {
+ void onDispatchDrawOccurred(Canvas canvas);
+ }
+
+ private LifeCycleListener mLifeCycleListener;
+ private List<View> mFooterViews;
+ private int mTopClippingLength;
+ private Rect mSelectorRect = new Rect();// for if reflection fails
+ private Field mSelectorPositionField;
+ private boolean mClippingToPadding = true;
+
+ public WrapperViewList(Context context) {
+ super(context);
+
+ // Use reflection to be able to change the size/position of the list
+ // selector so it does not come under/over the header
+ try {
+ Field selectorRectField = AbsListView.class.getDeclaredField("mSelectorRect");
+ selectorRectField.setAccessible(true);
+ mSelectorRect = (Rect) selectorRectField.get(this);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+ mSelectorPositionField = AbsListView.class.getDeclaredField("mSelectorPosition");
+ mSelectorPositionField.setAccessible(true);
+ }
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean performItemClick(View view, int position, long id) {
+ if (view instanceof WrapperView) {
+ view = ((WrapperView) view).mItem;
+ }
+ return super.performItemClick(view, position, id);
+ }
+
+ private void positionSelectorRect() {
+ if (!mSelectorRect.isEmpty()) {
+ int selectorPosition = getSelectorPosition();
+ if (selectorPosition >= 0) {
+ int firstVisibleItem = getFixedFirstVisibleItem();
+ View v = getChildAt(selectorPosition - firstVisibleItem);
+ if (v instanceof WrapperView) {
+ WrapperView wrapper = ((WrapperView) v);
+ mSelectorRect.top = wrapper.getTop() + wrapper.mItemTop;
+ }
+ }
+ }
+ }
+
+ private int getSelectorPosition() {
+ if (mSelectorPositionField == null) { // not all supported andorid
+ // version have this variable
+ for (int i = 0; i < getChildCount(); i++) {
+ if (getChildAt(i).getBottom() == mSelectorRect.bottom) {
+ return i + getFixedFirstVisibleItem();
+ }
+ }
+ } else {
+ try {
+ return mSelectorPositionField.getInt(this);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ positionSelectorRect();
+ if (mTopClippingLength != 0) {
+ canvas.save();
+ Rect clipping = canvas.getClipBounds();
+ clipping.top = mTopClippingLength;
+ canvas.clipRect(clipping);
+ super.dispatchDraw(canvas);
+ canvas.restore();
+ } else {
+ super.dispatchDraw(canvas);
+ }
+ mLifeCycleListener.onDispatchDrawOccurred(canvas);
+ }
+
+ void setLifeCycleListener(LifeCycleListener lifeCycleListener) {
+ mLifeCycleListener = lifeCycleListener;
+ }
+
+ @Override
+ public void addFooterView(View v) {
+ super.addFooterView(v);
+ if (mFooterViews == null) {
+ mFooterViews = new ArrayList<View>();
+ }
+ mFooterViews.add(v);
+ }
+
+ @Override
+ public boolean removeFooterView(View v) {
+ if (super.removeFooterView(v)) {
+ mFooterViews.remove(v);
+ return true;
+ }
+ return false;
+ }
+
+ boolean containsFooterView(View v) {
+ if (mFooterViews == null) {
+ return false;
+ }
+ return mFooterViews.contains(v);
+ }
+
+ void setTopClippingLength(int topClipping) {
+ mTopClippingLength = topClipping;
+ }
+
+ int getFixedFirstVisibleItem() {
+ int firstVisibleItem = getFirstVisiblePosition();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ return firstVisibleItem;
+ }
+
+ // first getFirstVisiblePosition() reports items
+ // outside the view sometimes on old versions of android
+ for (int i = 0; i < getChildCount(); i++) {
+ if (getChildAt(i).getBottom() >= 0) {
+ firstVisibleItem += i;
+ break;
+ }
+ }
+
+ // work around to fix bug with firstVisibleItem being to high
+ // because list view does not take clipToPadding=false into account
+ // on old versions of android
+ if (!mClippingToPadding && getPaddingTop() > 0 && firstVisibleItem > 0) {
+ if (getChildAt(0).getTop() > 0) {
+ firstVisibleItem -= 1;
+ }
+ }
+
+ return firstVisibleItem;
+ }
+
+ @Override
+ public void setClipToPadding(boolean clipToPadding) {
+ mClippingToPadding = clipToPadding;
+ super.setClipToPadding(clipToPadding);
+ }
+
+}
diff --git a/libraries/zxing/AndroidManifest.xml b/libraries/zxing/AndroidManifest.xml
new file mode 100644
index 000000000..ef720bdb6
--- /dev/null
+++ b/libraries/zxing/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.zxing"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk
+ android:minSdkVersion="8"
+ android:targetSdkVersion="19" />
+
+</manifest> \ No newline at end of file
diff --git a/libraries/zxing/build.gradle b/libraries/zxing/build.gradle
new file mode 100644
index 000000000..21050fc98
--- /dev/null
+++ b/libraries/zxing/build.gradle
@@ -0,0 +1,14 @@
+apply plugin: 'android-library'
+
+android {
+ compileSdkVersion 19
+ buildToolsVersion '19.0.0'
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+}
diff --git a/libraries/zxing/project.properties b/libraries/zxing/project.properties
new file mode 100644
index 000000000..91d2b0246
--- /dev/null
+++ b/libraries/zxing/project.properties
@@ -0,0 +1,15 @@
+# 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 edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-19
+android.library=true
diff --git a/OpenPGP-Keychain/src/com/google/zxing/BarcodeFormat.java b/libraries/zxing/src/com/google/zxing/BarcodeFormat.java
index 1e5d47958..1e5d47958 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/BarcodeFormat.java
+++ b/libraries/zxing/src/com/google/zxing/BarcodeFormat.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Binarizer.java b/libraries/zxing/src/com/google/zxing/Binarizer.java
index 912a3b556..912a3b556 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/Binarizer.java
+++ b/libraries/zxing/src/com/google/zxing/Binarizer.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/BinaryBitmap.java b/libraries/zxing/src/com/google/zxing/BinaryBitmap.java
index b97e46705..b97e46705 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/BinaryBitmap.java
+++ b/libraries/zxing/src/com/google/zxing/BinaryBitmap.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ChecksumException.java b/libraries/zxing/src/com/google/zxing/ChecksumException.java
index dedb4be99..dedb4be99 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/ChecksumException.java
+++ b/libraries/zxing/src/com/google/zxing/ChecksumException.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/DecodeHintType.java b/libraries/zxing/src/com/google/zxing/DecodeHintType.java
index 20b922ca1..20b922ca1 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/DecodeHintType.java
+++ b/libraries/zxing/src/com/google/zxing/DecodeHintType.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/EncodeHintType.java b/libraries/zxing/src/com/google/zxing/EncodeHintType.java
index 35afc1530..35afc1530 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/EncodeHintType.java
+++ b/libraries/zxing/src/com/google/zxing/EncodeHintType.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/FormatException.java b/libraries/zxing/src/com/google/zxing/FormatException.java
index 6967e93de..6967e93de 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/FormatException.java
+++ b/libraries/zxing/src/com/google/zxing/FormatException.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/LuminanceSource.java b/libraries/zxing/src/com/google/zxing/LuminanceSource.java
index 4b6d4539f..4b6d4539f 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/LuminanceSource.java
+++ b/libraries/zxing/src/com/google/zxing/LuminanceSource.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/NotFoundException.java b/libraries/zxing/src/com/google/zxing/NotFoundException.java
index dedab8dfc..dedab8dfc 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/NotFoundException.java
+++ b/libraries/zxing/src/com/google/zxing/NotFoundException.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Reader.java b/libraries/zxing/src/com/google/zxing/Reader.java
index 47e843ba6..47e843ba6 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/Reader.java
+++ b/libraries/zxing/src/com/google/zxing/Reader.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ReaderException.java b/libraries/zxing/src/com/google/zxing/ReaderException.java
index 224a497e5..224a497e5 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/ReaderException.java
+++ b/libraries/zxing/src/com/google/zxing/ReaderException.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Result.java b/libraries/zxing/src/com/google/zxing/Result.java
index ee1af527e..ee1af527e 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/Result.java
+++ b/libraries/zxing/src/com/google/zxing/Result.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ResultMetadataType.java b/libraries/zxing/src/com/google/zxing/ResultMetadataType.java
index 33d69d9c5..33d69d9c5 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/ResultMetadataType.java
+++ b/libraries/zxing/src/com/google/zxing/ResultMetadataType.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ResultPoint.java b/libraries/zxing/src/com/google/zxing/ResultPoint.java
index 366ae3855..366ae3855 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/ResultPoint.java
+++ b/libraries/zxing/src/com/google/zxing/ResultPoint.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/ResultPointCallback.java b/libraries/zxing/src/com/google/zxing/ResultPointCallback.java
index 0c85410bc..0c85410bc 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/ResultPointCallback.java
+++ b/libraries/zxing/src/com/google/zxing/ResultPointCallback.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/Writer.java b/libraries/zxing/src/com/google/zxing/Writer.java
index 6474ca7e2..6474ca7e2 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/Writer.java
+++ b/libraries/zxing/src/com/google/zxing/Writer.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/WriterException.java b/libraries/zxing/src/com/google/zxing/WriterException.java
index 0c19af01d..0c19af01d 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/WriterException.java
+++ b/libraries/zxing/src/com/google/zxing/WriterException.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/BitArray.java b/libraries/zxing/src/com/google/zxing/common/BitArray.java
index 6eb0d57c6..6eb0d57c6 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/BitArray.java
+++ b/libraries/zxing/src/com/google/zxing/common/BitArray.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/BitMatrix.java b/libraries/zxing/src/com/google/zxing/common/BitMatrix.java
index 8bf75b289..8bf75b289 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/BitMatrix.java
+++ b/libraries/zxing/src/com/google/zxing/common/BitMatrix.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/BitSource.java b/libraries/zxing/src/com/google/zxing/common/BitSource.java
index a61ac5105..a61ac5105 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/BitSource.java
+++ b/libraries/zxing/src/com/google/zxing/common/BitSource.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/CharacterSetECI.java b/libraries/zxing/src/com/google/zxing/common/CharacterSetECI.java
index 42b7fa9f6..42b7fa9f6 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/CharacterSetECI.java
+++ b/libraries/zxing/src/com/google/zxing/common/CharacterSetECI.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/Collections.java b/libraries/zxing/src/com/google/zxing/common/Collections.java
index 319ebfe6d..319ebfe6d 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/Collections.java
+++ b/libraries/zxing/src/com/google/zxing/common/Collections.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/Comparator.java b/libraries/zxing/src/com/google/zxing/common/Comparator.java
index e1be15e31..e1be15e31 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/Comparator.java
+++ b/libraries/zxing/src/com/google/zxing/common/Comparator.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/DecoderResult.java b/libraries/zxing/src/com/google/zxing/common/DecoderResult.java
index 7e0855333..7e0855333 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/DecoderResult.java
+++ b/libraries/zxing/src/com/google/zxing/common/DecoderResult.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/DefaultGridSampler.java b/libraries/zxing/src/com/google/zxing/common/DefaultGridSampler.java
index 74c9e7c6b..74c9e7c6b 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/DefaultGridSampler.java
+++ b/libraries/zxing/src/com/google/zxing/common/DefaultGridSampler.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/DetectorResult.java b/libraries/zxing/src/com/google/zxing/common/DetectorResult.java
index ea4794d17..ea4794d17 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/DetectorResult.java
+++ b/libraries/zxing/src/com/google/zxing/common/DetectorResult.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/ECI.java b/libraries/zxing/src/com/google/zxing/common/ECI.java
index 444c779c2..444c779c2 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/ECI.java
+++ b/libraries/zxing/src/com/google/zxing/common/ECI.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/GlobalHistogramBinarizer.java b/libraries/zxing/src/com/google/zxing/common/GlobalHistogramBinarizer.java
index 4fa2a887b..4fa2a887b 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/GlobalHistogramBinarizer.java
+++ b/libraries/zxing/src/com/google/zxing/common/GlobalHistogramBinarizer.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/GridSampler.java b/libraries/zxing/src/com/google/zxing/common/GridSampler.java
index 7f26c264e..7f26c264e 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/GridSampler.java
+++ b/libraries/zxing/src/com/google/zxing/common/GridSampler.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/HybridBinarizer.java b/libraries/zxing/src/com/google/zxing/common/HybridBinarizer.java
index b482c1a22..b482c1a22 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/HybridBinarizer.java
+++ b/libraries/zxing/src/com/google/zxing/common/HybridBinarizer.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/PerspectiveTransform.java b/libraries/zxing/src/com/google/zxing/common/PerspectiveTransform.java
index 9e65baff1..9e65baff1 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/PerspectiveTransform.java
+++ b/libraries/zxing/src/com/google/zxing/common/PerspectiveTransform.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/StringUtils.java b/libraries/zxing/src/com/google/zxing/common/StringUtils.java
index 97999f997..97999f997 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/StringUtils.java
+++ b/libraries/zxing/src/com/google/zxing/common/StringUtils.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java b/libraries/zxing/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
index 950a22364..950a22364 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
+++ b/libraries/zxing/src/com/google/zxing/common/detector/MonochromeRectangleDetector.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/detector/WhiteRectangleDetector.java b/libraries/zxing/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
index 31d87e9d0..31d87e9d0 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
+++ b/libraries/zxing/src/com/google/zxing/common/detector/WhiteRectangleDetector.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGF.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGF.java
index 859c379ee..859c379ee 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGF.java
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGF.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
index 056802287..056802287 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/GenericGFPoly.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
index b523fd34b..b523fd34b 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonDecoder.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
index 05e2ae03a..05e2ae03a 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonEncoder.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
index d5b45a612..d5b45a612 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
+++ b/libraries/zxing/src/com/google/zxing/common/reedsolomon/ReedSolomonException.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/ByQuadrantReader.java b/libraries/zxing/src/com/google/zxing/multi/ByQuadrantReader.java
index 35904d364..35904d364 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/ByQuadrantReader.java
+++ b/libraries/zxing/src/com/google/zxing/multi/ByQuadrantReader.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java b/libraries/zxing/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
index 70d454251..70d454251 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
+++ b/libraries/zxing/src/com/google/zxing/multi/GenericMultipleBarcodeReader.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/MultipleBarcodeReader.java b/libraries/zxing/src/com/google/zxing/multi/MultipleBarcodeReader.java
index 5f0c7eb5d..5f0c7eb5d 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/MultipleBarcodeReader.java
+++ b/libraries/zxing/src/com/google/zxing/multi/MultipleBarcodeReader.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
index 584c41404..584c41404 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
+++ b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiDetector.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
index 1162324e2..1162324e2 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
+++ b/libraries/zxing/src/com/google/zxing/multi/qrcode/detector/MultiFinderPatternFinder.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java b/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java
index fff4f5d1e..fff4f5d1e 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/QRCodeWriter.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/QRCodeWriter.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
index 9d131a554..9d131a554 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/BitMatrixParser.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java
index 12959d9c1..12959d9c1 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataBlock.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataBlock.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java
index d29dbd47f..d29dbd47f 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DataMask.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DataMask.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
index ff374ac50..ff374ac50 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/DecodedBitStreamParser.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
index e8d6c2589..e8d6c2589 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/ErrorCorrectionLevel.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java
index 1b76b0de5..1b76b0de5 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/FormatInformation.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/FormatInformation.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java
index 3c66217d3..3c66217d3 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Mode.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Mode.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java
index ba795de42..ba795de42 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/decoder/Version.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/decoder/Version.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
index 6fc1a2c88..6fc1a2c88 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPattern.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
index 3aadf284f..3aadf284f 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/AlignmentPatternFinder.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java
index 724d39d59..724d39d59 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/Detector.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/Detector.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java
index 7a9914d76..7a9914d76 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPattern.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPattern.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
index 01b3bde2a..01b3bde2a 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternFinder.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
index 3c3401085..3c3401085 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/detector/FinderPatternInfo.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java
index 5714d9c3a..5714d9c3a 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/BlockPair.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/BlockPair.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
index eb248a26c..eb248a26c 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/ByteMatrix.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java
index 8796511ab..8796511ab 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/Encoder.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/Encoder.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java
index 61ccf48c1..61ccf48c1 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MaskUtil.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MaskUtil.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
index 3d434e675..3d434e675 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/MatrixUtil.java
diff --git a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java b/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java
index 05c818513..05c818513 100644
--- a/OpenPGP-Keychain/src/com/google/zxing/qrcode/encoder/QRCode.java
+++ b/libraries/zxing/src/com/google/zxing/qrcode/encoder/QRCode.java
diff --git a/settings.gradle b/settings.gradle
index 2e582798c..5602503ee 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,4 +1,6 @@
include ':OpenPGP-Keychain'
include ':libraries:ActionBarSherlock'
include ':libraries:HtmlTextView'
-include ':libraries:pinned-section-listview:library' \ No newline at end of file
+include ':libraries:StickyListHeaders:library'
+include ':libraries:zxing'
+include ':libraries:AndroidBootstrap'