aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java24
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java6
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/UncachedKeyringTestingHelper.java297
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java143
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java516
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java397
-rw-r--r--OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java125
-rw-r--r--OpenKeychain/build.gradle1
-rw-r--r--OpenKeychain/src/main/AndroidManifest.xml203
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java97
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java38
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java)24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java)4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java)47
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java)18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java (renamed from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java)55
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java187
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java265
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java76
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java161
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java87
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java81
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java143
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java59
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java192
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java249
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java214
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java744
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java25
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java48
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java102
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java41
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java83
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java114
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java155
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java52
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java82
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java45
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java40
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java90
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java46
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java111
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java4
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml8
-rw-r--r--OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml16
-rw-r--r--OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml17
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.pngbin0 -> 2259 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.pngbin1268 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.pngbin1252 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.pngbin898 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.pngbin900 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.pngbin757 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.pngbin1278 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.pngbin0 -> 675 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.pngbin0 -> 748 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.pngbin0 -> 675 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.pngbin0 -> 723 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.pngbin0 -> 789 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.pngbin0 -> 528 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.pngbin0 -> 444 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.pngbin0 -> 714 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.pngbin0 -> 861 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.pngbin0 -> 640 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.pngbin0 -> 740 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.pngbin0 -> 536 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.pngbin0 -> 813 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.pngbin0 -> 541 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.pngbin0 -> 695 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.pngbin652 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.pngbin646 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.pngbin507 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.pngbin663 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.pngbin0 -> 1766 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.pngbin884 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.pngbin862 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.pngbin627 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.pngbin688 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.pngbin555 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.pngbin853 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.pngbin0 -> 528 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.pngbin0 -> 622 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.pngbin0 -> 522 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.pngbin0 -> 601 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.pngbin0 -> 643 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.pngbin0 -> 463 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.pngbin0 -> 410 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.pngbin0 -> 613 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.pngbin0 -> 685 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.pngbin0 -> 517 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.pngbin0 -> 589 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.pngbin0 -> 469 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.pngbin0 -> 667 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.pngbin0 -> 476 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.pngbin0 -> 557 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.pngbin0 -> 3176 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.pngbin1676 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.pngbin1606 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.pngbin1203 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.pngbin1091 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.pngbin940 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.pngbin1646 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.pngbin0 -> 911 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.pngbin0 -> 1008 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.pngbin0 -> 911 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.pngbin0 -> 1049 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.pngbin0 -> 1179 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.pngbin0 -> 736 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.pngbin0 -> 676 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.pngbin0 -> 1033 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.pngbin0 -> 1218 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.pngbin0 -> 906 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.pngbin0 -> 1043 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.pngbin0 -> 706 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.pngbin0 -> 1166 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.pngbin0 -> 784 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.pngbin0 -> 1017 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.pngbin0 -> 4039 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.pngbin2381 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.pngbin2364 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.pngbin2217 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.pngbin1366 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.pngbin1282 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.pngbin2215 -> 0 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.pngbin0 -> 1160 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.pngbin0 -> 1316 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.pngbin0 -> 1165 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.pngbin0 -> 1429 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.pngbin0 -> 1590 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.pngbin0 -> 840 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.pngbin0 -> 694 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.pngbin0 -> 1353 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.pngbin0 -> 1660 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.pngbin0 -> 1231 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.pngbin0 -> 1377 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.pngbin0 -> 946 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.pngbin0 -> 1555 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.pngbin0 -> 1012 bytes
-rw-r--r--OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.pngbin0 -> 1319 bytes
-rw-r--r--OpenKeychain/src/main/res/layout/api_account_settings_activity.xml30
-rw-r--r--OpenKeychain/src/main/res/layout/api_app_settings_activity.xml50
-rw-r--r--OpenKeychain/src/main/res/layout/certify_key_activity.xml331
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_activity.xml55
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_final_fragment.xml152
-rw-r--r--OpenKeychain/src/main/res/layout/create_key_input_fragment.xml144
-rw-r--r--OpenKeychain/src/main/res/layout/decrypt_result_include.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/edit_key_activity.xml53
-rw-r--r--OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/first_time_activity.xml147
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_activity.xml13
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_fragment.xml4
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_header.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/key_list_item.xml30
-rw-r--r--OpenKeychain/src/main/res/layout/notify_area.xml2
-rw-r--r--OpenKeychain/src/main/res/layout/qr_code_activity.xml (renamed from OpenKeychain/src/main/res/layout/edit_key_activity_new.xml)11
-rw-r--r--OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml19
-rw-r--r--OpenKeychain/src/main/res/layout/upload_key_activity.xml101
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_activity.xml46
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_main_fragment.xml60
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_share_fragment.xml23
-rw-r--r--OpenKeychain/src/main/res/layout/view_key_user_id_item.xml3
-rw-r--r--OpenKeychain/src/main/res/menu/key_list.xml7
-rw-r--r--OpenKeychain/src/main/res/menu/key_view.xml17
-rw-r--r--OpenKeychain/src/main/res/values-de/strings.xml4
-rw-r--r--OpenKeychain/src/main/res/values-es/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-fr/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-it/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-ja/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-ru/strings.xml8
-rw-r--r--OpenKeychain/src/main/res/values-sl/strings.xml4
-rw-r--r--OpenKeychain/src/main/res/values-uk/strings.xml6
-rw-r--r--OpenKeychain/src/main/res/values/colors.xml15
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml81
-rw-r--r--README.md4
-rw-r--r--Resources/graphics/create_key_robot.svg102
-rw-r--r--Resources/graphics/first_time_1.png (renamed from Resources/gnupg-infographic/first_time_1.png)bin43898 -> 43898 bytes
-rw-r--r--Resources/graphics/first_time_1.svg (renamed from Resources/gnupg-infographic/first_time_1.svg)0
-rw-r--r--Resources/graphics/originals/gnupg-infographic/README (renamed from Resources/gnupg-infographic/README)0
-rw-r--r--Resources/graphics/originals/gnupg-infographic/gnupg-infographic.png (renamed from Resources/gnupg-infographic/gnupg-infographic.png)bin486199 -> 486199 bytes
-rw-r--r--Resources/graphics/originals/gnupg-infographic/gnupg-infographic.svg (renamed from Resources/gnupg-infographic/gnupg-infographic.svg)0
-rw-r--r--Resources/graphics/originals/ic_action_nfc/NFC.png (renamed from Resources/graphics/ic_action_nfc/NFC.png)bin88038 -> 88038 bytes
-rw-r--r--Resources/graphics/originals/ic_action_qr_code/ic_menu_qr_code.svg (renamed from Resources/graphics/ic_action_qr_code/ic_menu_qr_code.svg)0
-rw-r--r--Resources/graphics/originals/ic_launcher/AUTHORS (renamed from Resources/graphics/ic_launcher/AUTHORS)0
-rw-r--r--Resources/graphics/originals/ic_launcher/COPYING (renamed from Resources/graphics/ic_launcher/COPYING)0
-rw-r--r--Resources/graphics/originals/ic_launcher/kgpg_key2_kopete.svgz (renamed from Resources/graphics/ic_launcher/kgpg_key2_kopete.svgz)bin36830 -> 36830 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/README.md97
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.pngbin0 -> 4406 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.pngbin0 -> 6746 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.pngbin0 -> 9753 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.pngbin0 -> 17387 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error.pngbin0 -> 4627 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.pngbin0 -> 7152 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.pngbin0 -> 10220 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.pngbin0 -> 18697 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open.pngbin0 -> 4426 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.pngbin0 -> 6812 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.pngbin0 -> 9704 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.pngbin0 -> 17498 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.pngbin0 -> 7848 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.pngbin0 -> 14346 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.pngbin0 -> 21322 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.pngbin0 -> 38093 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key.pngbin0 -> 4176 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.pngbin0 -> 5987 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.pngbin0 -> 8197 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.pngbin0 -> 14384 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.pngbin0 -> 5003 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.pngbin0 -> 7965 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.pngbin0 -> 11515 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.pngbin0 -> 21420 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.pngbin0 -> 5037 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.pngbin0 -> 7936 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.pngbin0 -> 11420 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.pngbin0 -> 20650 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.pngbin0 -> 3643 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.pngbin0 -> 5165 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.pngbin0 -> 7712 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.pngbin0 -> 14332 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.pngbin0 -> 4024 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.pngbin0 -> 5969 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.pngbin0 -> 8833 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.pngbin0 -> 15993 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.pngbin0 -> 5061 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.pngbin0 -> 7818 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.pngbin0 -> 11578 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.pngbin0 -> 22423 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.pngbin0 -> 5083 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.pngbin0 -> 7928 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.pngbin0 -> 11727 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.pngbin0 -> 22153 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.pngbin0 -> 4845 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.pngbin0 -> 7571 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.pngbin0 -> 10941 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.pngbin0 -> 20160 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.pngbin0 -> 4723 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.pngbin0 -> 7337 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.pngbin0 -> 10570 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.pngbin0 -> 19036 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.pngbin0 -> 5004 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.pngbin0 -> 7911 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.pngbin0 -> 11425 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.pngbin0 -> 20909 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.pngbin0 -> 4236 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.pngbin0 -> 6283 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.pngbin0 -> 8964 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.pngbin0 -> 15762 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.pngbin0 -> 4976 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.pngbin0 -> 8034 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.pngbin0 -> 12079 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.pngbin0 -> 23287 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.pngbin0 -> 4374 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg12
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.pngbin0 -> 6840 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.pngbin0 -> 9773 bytes
-rw-r--r--Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.pngbin0 -> 17688 bytes
-rw-r--r--Resources/graphics/status_lock_closed.svg12
-rw-r--r--Resources/graphics/status_lock_error.svg12
-rw-r--r--Resources/graphics/status_lock_open.svg12
-rw-r--r--Resources/graphics/status_signature_expired.svg12
-rw-r--r--Resources/graphics/status_signature_expired_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_invalid.svg12
-rw-r--r--Resources/graphics/status_signature_invalid_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_revoked.svg12
-rw-r--r--Resources/graphics/status_signature_revoked_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_unknown.svg12
-rw-r--r--Resources/graphics/status_signature_unknown_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_unverified.svg12
-rw-r--r--Resources/graphics/status_signature_unverified_cutout.svg12
-rw-r--r--Resources/graphics/status_signature_verified.svg12
-rw-r--r--Resources/graphics/status_signature_verified_cutout.svg12
-rwxr-xr-xResources/graphics/update-drawables.sh19
-rw-r--r--build.gradle11
m---------extern/AppMsg0
-rw-r--r--settings.gradle1
317 files changed, 5014 insertions, 2966 deletions
diff --git a/.gitmodules b/.gitmodules
index 93ca9d49b..919f7e1db 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,9 +7,6 @@
[submodule "extern/zxing-qr-code"]
path = extern/zxing-qr-code
url = https://github.com/open-keychain/zxing-qr-code.git
-[submodule "extern/AppMsg"]
- path = extern/AppMsg
- url = https://github.com/open-keychain/Android-AppMsg.git
[submodule "extern/spongycastle"]
path = extern/spongycastle
url = https://github.com/open-keychain/spongycastle.git
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
index 3fa668e6e..015e134ea 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/KeyringTestingHelper.java
@@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -61,7 +62,7 @@ public class KeyringTestingHelper {
boolean saveSuccess = saveKeyringResult.success();
// Now re-retrieve the saved key. Should not throw an exception.
- providerHelper.getWrappedPublicKeyRing(masterKeyId);
+ providerHelper.getCanonicalizedPublicKeyRing(masterKeyId);
// A different ID should still fail
retrieveKeyAndExpectNotFound(providerHelper, masterKeyId - 1);
@@ -331,13 +332,32 @@ public class KeyringTestingHelper {
}
+ public static <E> E getNth(Iterator<E> it, int position) {
+ while(position-- > 0) {
+ it.next();
+ }
+ return it.next();
+ }
+
+ public static long getSubkeyId(UncachedKeyRing ring, int position) {
+ return getNth(ring.getPublicKeys(), position).getKeyId();
+ }
+
private void retrieveKeyAndExpectNotFound(ProviderHelper providerHelper, long masterKeyId) {
try {
- providerHelper.getWrappedPublicKeyRing(masterKeyId);
+ providerHelper.getCanonicalizedPublicKeyRing(masterKeyId);
throw new AssertionError("Was expecting the previous call to fail!");
} catch (ProviderHelper.NotFoundException expectedException) {
// good
}
}
+ public static <E> List<E> itToList(Iterator<E> it) {
+ List<E> result = new ArrayList<E>();
+ while(it.hasNext()) {
+ result.add(it.next());
+ }
+ return result;
+ }
+
}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
index f06fe0072..2cd0c67a2 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/ProviderHelperStub.java
@@ -20,7 +20,7 @@ package org.sufficientlysecure.keychain.support;
import android.content.Context;
import android.net.Uri;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
/**
@@ -32,8 +32,8 @@ class ProviderHelperStub extends ProviderHelper {
}
@Override
- public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException {
+ public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri id) throws NotFoundException {
byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob"));
- return new WrappedPublicKeyRing(data, false, 0);
+ return new CanonicalizedPublicKeyRing(data, 0);
}
}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/UncachedKeyringTestingHelper.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/UncachedKeyringTestingHelper.java
deleted file mode 100644
index 6467d3f32..000000000
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/support/UncachedKeyringTestingHelper.java
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- * Copyright (C) Art O Cathain
- *
- * 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.support;
-
-import org.spongycastle.bcpg.BCPGKey;
-import org.spongycastle.bcpg.PublicKeyPacket;
-import org.spongycastle.bcpg.SignatureSubpacket;
-import org.spongycastle.openpgp.PGPException;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPSignature;
-import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
-import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector;
-import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
-import org.sufficientlysecure.keychain.service.OperationResultParcel;
-
-import java.util.Arrays;
-
-/**
- * Created by art on 28/06/14.
- */
-public class UncachedKeyringTestingHelper {
-
- public static boolean compareRing(UncachedKeyRing keyRing1, UncachedKeyRing keyRing2) {
- OperationResultParcel.OperationLog operationLog = new OperationResultParcel.OperationLog();
- UncachedKeyRing canonicalized = keyRing1.canonicalize(operationLog, 0);
-
- if (canonicalized == null) {
- throw new AssertionError("Canonicalization failed; messages: [" + operationLog.toList() + "]");
- }
-
- return TestDataUtil.iterEquals(canonicalized.getPublicKeys(), keyRing2.getPublicKeys(), new
- TestDataUtil.EqualityChecker<UncachedPublicKey>() {
- @Override
- public boolean areEquals(UncachedPublicKey lhs, UncachedPublicKey rhs) {
- return comparePublicKey(lhs, rhs);
- }
- });
- }
-
- public static boolean comparePublicKey(UncachedPublicKey key1, UncachedPublicKey key2) {
- boolean equal = true;
-
- if (key1.canAuthenticate() != key2.canAuthenticate()) {
- return false;
- }
- if (key1.canCertify() != key2.canCertify()) {
- return false;
- }
- if (key1.canEncrypt() != key2.canEncrypt()) {
- return false;
- }
- if (key1.canSign() != key2.canSign()) {
- return false;
- }
- if (key1.getAlgorithm() != key2.getAlgorithm()) {
- return false;
- }
- if (key1.getBitStrength() != key2.getBitStrength()) {
- return false;
- }
- if (!TestDataUtil.equals(key1.getCreationTime(), key2.getCreationTime())) {
- return false;
- }
- if (!TestDataUtil.equals(key1.getExpiryTime(), key2.getExpiryTime())) {
- return false;
- }
- if (!Arrays.equals(key1.getFingerprint(), key2.getFingerprint())) {
- return false;
- }
- if (key1.getKeyId() != key2.getKeyId()) {
- return false;
- }
- if (key1.getKeyUsage() != key2.getKeyUsage()) {
- return false;
- }
- if (!TestDataUtil.equals(key1.getPrimaryUserId(), key2.getPrimaryUserId())) {
- return false;
- }
-
- // Ooops, getPublicKey is due to disappear. But then how to compare?
- if (!keysAreEqual(key1.getPublicKey(), key2.getPublicKey())) {
- return false;
- }
-
- return equal;
- }
-
- public static boolean keysAreEqual(PGPPublicKey a, PGPPublicKey b) {
-
- if (a.getAlgorithm() != b.getAlgorithm()) {
- return false;
- }
-
- if (a.getBitStrength() != b.getBitStrength()) {
- return false;
- }
-
- if (!TestDataUtil.equals(a.getCreationTime(), b.getCreationTime())) {
- return false;
- }
-
- if (!Arrays.equals(a.getFingerprint(), b.getFingerprint())) {
- return false;
- }
-
- if (a.getKeyID() != b.getKeyID()) {
- return false;
- }
-
- if (!pubKeyPacketsAreEqual(a.getPublicKeyPacket(), b.getPublicKeyPacket())) {
- return false;
- }
-
- if (a.getVersion() != b.getVersion()) {
- return false;
- }
-
- if (a.getValidDays() != b.getValidDays()) {
- return false;
- }
-
- if (a.getValidSeconds() != b.getValidSeconds()) {
- return false;
- }
-
- if (!Arrays.equals(a.getTrustData(), b.getTrustData())) {
- return false;
- }
-
- if (!TestDataUtil.iterEquals(a.getUserIDs(), b.getUserIDs())) {
- return false;
- }
-
- if (!TestDataUtil.iterEquals(a.getUserAttributes(), b.getUserAttributes(),
- new TestDataUtil.EqualityChecker<PGPUserAttributeSubpacketVector>() {
- public boolean areEquals(PGPUserAttributeSubpacketVector lhs, PGPUserAttributeSubpacketVector rhs) {
- // For once, BC defines equals, so we use it implicitly.
- return TestDataUtil.equals(lhs, rhs);
- }
- }
- )) {
- return false;
- }
-
-
- if (!TestDataUtil.iterEquals(a.getSignatures(), b.getSignatures(),
- new TestDataUtil.EqualityChecker<PGPSignature>() {
- public boolean areEquals(PGPSignature lhs, PGPSignature rhs) {
- return signaturesAreEqual(lhs, rhs);
- }
- }
- )) {
- return false;
- }
-
- return true;
- }
-
- public static boolean signaturesAreEqual(PGPSignature a, PGPSignature b) {
-
- if (a.getVersion() != b.getVersion()) {
- return false;
- }
-
- if (a.getKeyAlgorithm() != b.getKeyAlgorithm()) {
- return false;
- }
-
- if (a.getHashAlgorithm() != b.getHashAlgorithm()) {
- return false;
- }
-
- if (a.getSignatureType() != b.getSignatureType()) {
- return false;
- }
-
- try {
- if (!Arrays.equals(a.getSignature(), b.getSignature())) {
- return false;
- }
- } catch (PGPException ex) {
- throw new RuntimeException(ex);
- }
-
- if (a.getKeyID() != b.getKeyID()) {
- return false;
- }
-
- if (!TestDataUtil.equals(a.getCreationTime(), b.getCreationTime())) {
- return false;
- }
-
- if (!Arrays.equals(a.getSignatureTrailer(), b.getSignatureTrailer())) {
- return false;
- }
-
- if (!subPacketVectorsAreEqual(a.getHashedSubPackets(), b.getHashedSubPackets())) {
- return false;
- }
-
- if (!subPacketVectorsAreEqual(a.getUnhashedSubPackets(), b.getUnhashedSubPackets())) {
- return false;
- }
-
- return true;
- }
-
- private static boolean subPacketVectorsAreEqual(PGPSignatureSubpacketVector aHashedSubPackets, PGPSignatureSubpacketVector bHashedSubPackets) {
- for (int i = 0; i < Byte.MAX_VALUE; i++) {
- if (!TestDataUtil.iterEquals(Arrays.asList(aHashedSubPackets.getSubpackets(i)).iterator(),
- Arrays.asList(bHashedSubPackets.getSubpackets(i)).iterator(),
- new TestDataUtil.EqualityChecker<SignatureSubpacket>() {
- @Override
- public boolean areEquals(SignatureSubpacket lhs, SignatureSubpacket rhs) {
- return signatureSubpacketsAreEqual(lhs, rhs);
- }
- }
- )) {
- return false;
- }
-
- }
- return true;
- }
-
- private static boolean signatureSubpacketsAreEqual(SignatureSubpacket lhs, SignatureSubpacket rhs) {
- if (lhs.getType() != rhs.getType()) {
- return false;
- }
- if (!Arrays.equals(lhs.getData(), rhs.getData())) {
- return false;
- }
- return true;
- }
-
- public static boolean pubKeyPacketsAreEqual(PublicKeyPacket a, PublicKeyPacket b) {
-
- if (a.getAlgorithm() != b.getAlgorithm()) {
- return false;
- }
-
- if (!bcpgKeysAreEqual(a.getKey(), b.getKey())) {
- return false;
- }
-
- if (!TestDataUtil.equals(a.getTime(), b.getTime())) {
- return false;
- }
-
- if (a.getValidDays() != b.getValidDays()) {
- return false;
- }
-
- if (a.getVersion() != b.getVersion()) {
- return false;
- }
-
- return true;
- }
-
- public static boolean bcpgKeysAreEqual(BCPGKey a, BCPGKey b) {
-
- if (!TestDataUtil.equals(a.getFormat(), b.getFormat())) {
- return false;
- }
-
- if (!Arrays.equals(a.getEncoded(), b.getEncoded())) {
- return false;
- }
-
- return true;
- }
-
-
- public void doTestCanonicalize(UncachedKeyRing inputKeyRing, UncachedKeyRing expectedKeyRing) {
- if (!compareRing(inputKeyRing, expectedKeyRing)) {
- throw new AssertionError("Expected [" + inputKeyRing + "] to match [" + expectedKeyRing + "]");
- }
- }
-
-}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java
index 5c6072c25..4f6694049 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/PgpKeyOperationTest.java
@@ -19,12 +19,13 @@ import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.choice.algorithm;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
-import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
@@ -82,8 +83,7 @@ public class PgpKeyOperationTest {
parcel.mNewPassphrase = passphrase;
PgpKeyOperation op = new PgpKeyOperation(null);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- staticRing = op.createSecretKeyRing(parcel, log, 0);
+ staticRing = op.createSecretKeyRing(parcel).getRing();
Assert.assertNotNull("initial test key creation must succeed", staticRing);
@@ -107,9 +107,7 @@ public class PgpKeyOperationTest {
}
@Test
- public void testAlgorithmChoice() {
-
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ public void createSecretKeyRingTests() {
{
parcel.reset();
@@ -118,7 +116,7 @@ public class PgpKeyOperationTest {
parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase;
- UncachedKeyRing ring = op.createSecretKeyRing(parcel, log, 0);
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
Assert.assertNull("creating ring with < 512 bytes keysize should fail", ring);
}
@@ -130,7 +128,7 @@ public class PgpKeyOperationTest {
parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase;
- UncachedKeyRing ring = op.createSecretKeyRing(parcel, log, 0);
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
Assert.assertNull("creating ring with ElGamal master key should fail", ring);
}
@@ -142,7 +140,7 @@ public class PgpKeyOperationTest {
parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase;
- UncachedKeyRing ring = op.createSecretKeyRing(parcel, log, 0);
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
Assert.assertNull("creating ring with bad algorithm choice should fail", ring);
}
@@ -153,7 +151,7 @@ public class PgpKeyOperationTest {
parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase;
- UncachedKeyRing ring = op.createSecretKeyRing(parcel, log, 0);
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
Assert.assertNull("creating ring with non-certifying master key should fail", ring);
}
@@ -163,7 +161,7 @@ public class PgpKeyOperationTest {
Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
parcel.mNewPassphrase = passphrase;
- UncachedKeyRing ring = op.createSecretKeyRing(parcel, log, 0);
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
Assert.assertNull("creating ring without user ids should fail", ring);
}
@@ -172,7 +170,7 @@ public class PgpKeyOperationTest {
parcel.mAddUserIds.add("shy");
parcel.mNewPassphrase = passphrase;
- UncachedKeyRing ring = op.createSecretKeyRing(parcel, log, 0);
+ UncachedKeyRing ring = op.createSecretKeyRing(parcel).getRing();
Assert.assertNull("creating ring without subkeys should fail", ring);
}
@@ -186,11 +184,10 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, null));
parcel.mAddUserIds.add("luna");
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- ring = op.createSecretKeyRing(parcel, log, 0);
+ ring = op.createSecretKeyRing(parcel).getRing();
Assert.assertEquals("the keyring should contain only the master key",
- 1, ring.getAvailableSubkeys().size());
+ 1, KeyringTestingHelper.itToList(ring.getPublicKeys()).size());
Assert.assertEquals("first (master) key must have both flags",
KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA, ring.getPublicKey().getKeyUsage());
@@ -212,7 +209,7 @@ public class PgpKeyOperationTest {
2, ring.getPublicKey().getUnorderedUserIds().size());
Assert.assertEquals("number of subkeys must be three",
- 3, ring.getAvailableSubkeys().size());
+ 3, KeyringTestingHelper.itToList(ring.getPublicKeys()).size());
Assert.assertTrue("key ring should have been created in the last 120 seconds",
ring.getPublicKey().getCreationTime().after(new Date(new Date().getTime()-1000*120)));
@@ -250,9 +247,8 @@ public class PgpKeyOperationTest {
parcel.mMasterKeyId = ring.getMasterKeyId() -1;
parcel.mFingerprint = ring.getFingerprint();
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("keyring modification with bad master key id should fail", modified);
}
@@ -263,9 +259,8 @@ public class PgpKeyOperationTest {
parcel.mMasterKeyId = null;
parcel.mFingerprint = ring.getFingerprint();
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("keyring modification with null master key id should fail", modified);
}
@@ -277,9 +272,8 @@ public class PgpKeyOperationTest {
// some byte, off by one
parcel.mFingerprint[5] += 1;
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("keyring modification with bad fingerprint should fail", modified);
}
@@ -289,17 +283,19 @@ public class PgpKeyOperationTest {
parcel.mMasterKeyId = ring.getMasterKeyId();
parcel.mFingerprint = null;
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("keyring modification with null fingerprint should fail", modified);
}
{
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, "bad passphrase", log, 0);
+ String badphrase = "";
+ if (badphrase.equals(passphrase)) {
+ badphrase = "a";
+ }
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, badphrase).getRing();
Assert.assertNull("keyring modification with bad passphrase should fail", modified);
}
@@ -352,11 +348,11 @@ public class PgpKeyOperationTest {
{ // bad keysize should fail
parcel.reset();
- parcel.mAddSubKeys.add(new SubkeyAdd(algorithm.rsa, 77, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SubkeyAdd(
+ algorithm.rsa, new Random().nextInt(512), KeyFlags.SIGN_DATA, null));
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("creating a subkey with keysize < 512 should fail", modified);
}
@@ -366,9 +362,8 @@ public class PgpKeyOperationTest {
parcel.mAddSubKeys.add(new SubkeyAdd(algorithm.rsa, 1024, KeyFlags.SIGN_DATA,
new Date().getTime()/1000-10));
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("creating subkey with past expiry date should fail", modified);
}
@@ -379,12 +374,7 @@ public class PgpKeyOperationTest {
public void testSubkeyModify() throws Exception {
long expiry = new Date().getTime()/1000 + 1024;
- long keyId;
- {
- Iterator<UncachedPublicKey> it = ring.getPublicKeys();
- it.next();
- keyId = it.next().getKeyId();
- }
+ long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
UncachedKeyRing modified = ring;
{
@@ -440,9 +430,8 @@ public class PgpKeyOperationTest {
parcel.reset();
parcel.mChangeSubKeys.add(new SubkeyChange(keyId, null, new Date().getTime()/1000-10));
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("setting subkey expiry to a past date should fail", modified);
}
@@ -451,9 +440,8 @@ public class PgpKeyOperationTest {
parcel.reset();
parcel.mChangeSubKeys.add(new SubkeyChange(123, null, null));
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("modifying non-existent subkey should fail", modified);
}
@@ -463,13 +451,7 @@ public class PgpKeyOperationTest {
@Test
public void testSubkeyRevoke() throws Exception {
- long keyId;
- {
- Iterator<UncachedPublicKey> it = ring.getPublicKeys();
- it.next();
- keyId = it.next().getKeyId();
- }
-
+ long keyId = KeyringTestingHelper.getSubkeyId(ring, 1);
int flags = ring.getPublicKey(keyId).getKeyUsage();
UncachedKeyRing modified;
@@ -479,9 +461,8 @@ public class PgpKeyOperationTest {
parcel.reset();
parcel.mRevokeSubKeys.add(123L);
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("revoking a nonexistent subkey should fail", otherModified);
@@ -582,9 +563,8 @@ public class PgpKeyOperationTest {
parcel.reset();
parcel.mChangePrimaryUserId = uid;
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(modified.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(modified.getEncoded(), false, 0);
+ UncachedKeyRing otherModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("setting primary user id to a revoked user id should fail", otherModified);
@@ -629,6 +609,14 @@ public class PgpKeyOperationTest {
@Test
public void testUserIdAdd() throws Exception {
+ {
+ parcel.mAddUserIds.add("");
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ UncachedKeyRing modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
+ Assert.assertNull("adding an empty user id should fail", modified);
+ }
+
+ parcel.reset();
parcel.mAddUserIds.add("rainbow");
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB);
@@ -689,10 +677,12 @@ public class PgpKeyOperationTest {
parcel.reset();
//noinspection SpellCheckingInspection
parcel.mChangePrimaryUserId = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+ if (parcel.mChangePrimaryUserId.equals(passphrase)) {
+ parcel.mChangePrimaryUserId += "A";
+ }
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- modified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNull("changing primary user id to a non-existent one should fail", modified);
}
@@ -716,14 +706,14 @@ public class PgpKeyOperationTest {
ArrayList<RawPacket> onlyB,
boolean canonicalize,
boolean constantCanonicalize) {
+
try {
Assert.assertTrue("modified keyring must be secret", ring.isSecret());
- WrappedSecretKeyRing secretRing = new WrappedSecretKeyRing(ring.getEncoded(), false, 0);
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);
PgpKeyOperation op = new PgpKeyOperation(null);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing rawModified = op.modifySecretKeyRing(secretRing, parcel, passphrase, log, 0);
+ UncachedKeyRing rawModified = op.modifySecretKeyRing(secretRing, parcel, passphrase).getRing();
Assert.assertNotNull("key modification failed", rawModified);
if (!canonicalize) {
@@ -732,7 +722,7 @@ public class PgpKeyOperationTest {
return rawModified;
}
- UncachedKeyRing modified = rawModified.canonicalize(log, 0);
+ CanonicalizedKeyRing modified = rawModified.canonicalize(new OperationLog(), 0);
if (constantCanonicalize) {
Assert.assertTrue("key must be constant through canonicalization",
!KeyringTestingHelper.diffKeyrings(
@@ -741,7 +731,8 @@ public class PgpKeyOperationTest {
}
Assert.assertTrue("keyring must differ from original", KeyringTestingHelper.diffKeyrings(
ring.getEncoded(), modified.getEncoded(), onlyA, onlyB));
- return modified;
+
+ return modified.getUncachedKeyRing();
} catch (IOException e) {
throw new AssertionFailedError("error during encoding!");
@@ -754,12 +745,8 @@ public class PgpKeyOperationTest {
UncachedKeyRing expectedKeyRing = KeyringBuilder.correctRing();
UncachedKeyRing inputKeyRing = KeyringBuilder.ringWithExtraIncorrectSignature();
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- UncachedKeyRing canonicalizedRing = inputKeyRing.canonicalize(log, 0);
-
- if (canonicalizedRing == null) {
- throw new AssertionError("Canonicalization failed; messages: [" + log + "]");
- }
+ CanonicalizedKeyRing canonicalized = inputKeyRing.canonicalize(new OperationLog(), 0);
+ Assert.assertNotNull("canonicalization must succeed", canonicalized);
ArrayList onlyA = new ArrayList<RawPacket>();
ArrayList onlyB = new ArrayList<RawPacket>();
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java
index 6f3cf31b5..97e0d8a68 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringCanonicalizeTest.java
@@ -12,20 +12,43 @@ import org.spongycastle.bcpg.Packet;
import org.spongycastle.bcpg.PacketTags;
import org.spongycastle.bcpg.UserIDPacket;
import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
+import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
+import java.util.Date;
import java.util.Iterator;
+
+/** Tests for the UncachedKeyring.canonicalize method.
+ *
+ * This is a complex and crypto-relevant method, which takes care of sanitizing keyrings.
+ * Test cases are made for all its assertions.
+ */
+
@RunWith(RobolectricTestRunner.class)
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
public class UncachedKeyringCanonicalizeTest {
@@ -36,6 +59,8 @@ public class UncachedKeyringCanonicalizeTest {
ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ PGPSignatureSubpacketGenerator subHashedPacketsGen;
+ PGPSecretKey secretKey;
@BeforeClass
public static void setUpOnce() throws Exception {
@@ -55,9 +80,9 @@ public class UncachedKeyringCanonicalizeTest {
parcel.mNewPassphrase = "";
PgpKeyOperation op = new PgpKeyOperation(null);
- OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
- staticRing = op.createSecretKeyRing(parcel, log, 0);
-
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ Assert.assertTrue("initial test key creation must succeed", result.success());
+ staticRing = result.getRing();
Assert.assertNotNull("initial test key creation must succeed", staticRing);
// just for later reference
@@ -71,8 +96,13 @@ public class UncachedKeyringCanonicalizeTest {
// show Log.x messages in system.out
ShadowLog.stream = System.out;
ring = staticRing;
+
+ subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ secretKey = new PGPSecretKeyRing(ring.getEncoded(), new JcaKeyFingerprintCalculator())
+ .getSecretKey();
}
+ /** Make sure the assumptions made about the generated ring packet structure are valid. */
@Test public void testGeneratedRingStructure() throws Exception {
Iterator<RawPacket> it = KeyringTestingHelper.parseKeyring(ring.getEncoded());
@@ -107,43 +137,6 @@ public class UncachedKeyringCanonicalizeTest {
}
- @Test public void testBrokenSignature() throws Exception {
-
- byte[] brokenSig;
- {
- UncachedPublicKey masterKey = ring.getPublicKey();
- WrappedSignature sig = masterKey.getSignaturesForId("twi").next();
- brokenSig = sig.getEncoded();
- // break the signature
- brokenSig[brokenSig.length - 5] += 1;
- }
-
- byte[] reng = ring.getEncoded();
- for(int i = 0; i < totalPackets; i++) {
-
- byte[] brokenBytes = KeyringTestingHelper.injectPacket(reng, brokenSig, i);
- Assert.assertEquals("broken ring must be original + injected size",
- reng.length + brokenSig.length, brokenBytes.length);
-
- try {
- UncachedKeyRing brokenRing = UncachedKeyRing.decodeFromData(brokenBytes);
-
- brokenRing = brokenRing.canonicalize(log, 0);
- if (brokenRing == null) {
- System.out.println("ok, canonicalization failed.");
- continue;
- }
-
- Assert.assertArrayEquals("injected bad signature must be gone after canonicalization",
- ring.getEncoded(), brokenRing.getEncoded());
-
- } catch (Exception e) {
- System.out.println("ok, rejected with: " + e.getMessage());
- }
- }
-
- }
-
@Test public void testUidSignature() throws Exception {
UncachedPublicKey masterKey = ring.getPublicKey();
@@ -156,31 +149,29 @@ public class UncachedKeyringCanonicalizeTest {
{ // bad certificates get stripped
UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, brokenSig.getEncoded(), 3);
- modified = modified.canonicalize(log, 0);
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
Assert.assertTrue("canonicalized keyring with invalid extra sig must be same as original one",
!KeyringTestingHelper.diffKeyrings(
- ring.getEncoded(), modified.getEncoded(), onlyA, onlyB));
+ ring.getEncoded(), canonicalized.getEncoded(), onlyA, onlyB));
}
// remove user id certificate for one user
final UncachedKeyRing base = KeyringTestingHelper.removePacket(ring, 2);
{ // user id without certificate should be removed
- UncachedKeyRing modified = base.canonicalize(log, 0);
+ CanonicalizedKeyRing modified = base.canonicalize(log, 0);
Assert.assertTrue("canonicalized keyring must differ", KeyringTestingHelper.diffKeyrings(
ring.getEncoded(), modified.getEncoded(), onlyA, onlyB));
Assert.assertEquals("two packets should be stripped after canonicalization", 2, onlyA.size());
Assert.assertEquals("no new packets after canonicalization", 0, onlyB.size());
- Packet p;
- p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
+ Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
Assert.assertTrue("first stripped packet must be user id", p instanceof UserIDPacket);
Assert.assertEquals("missing user id must be the expected one",
"twi", ((UserIDPacket) p).getID());
- p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket();
Assert.assertArrayEquals("second stripped packet must be signature we removed",
sig.getEncoded(), onlyA.get(1).buf);
@@ -189,25 +180,448 @@ public class UncachedKeyringCanonicalizeTest {
{ // add error to signature
UncachedKeyRing modified = KeyringTestingHelper.injectPacket(base, brokenSig.getEncoded(), 3);
- modified = modified.canonicalize(log, 0);
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
Assert.assertTrue("canonicalized keyring must differ", KeyringTestingHelper.diffKeyrings(
- ring.getEncoded(), modified.getEncoded(), onlyA, onlyB));
+ ring.getEncoded(), canonicalized.getEncoded(), onlyA, onlyB));
Assert.assertEquals("two packets should be missing after canonicalization", 2, onlyA.size());
Assert.assertEquals("no new packets after canonicalization", 0, onlyB.size());
- Packet p;
- p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
+ Packet p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(0).buf)).readPacket();
Assert.assertTrue("first stripped packet must be user id", p instanceof UserIDPacket);
Assert.assertEquals("missing user id must be the expected one",
"twi", ((UserIDPacket) p).getID());
- p = new BCPGInputStream(new ByteArrayInputStream(onlyA.get(1).buf)).readPacket();
Assert.assertArrayEquals("second stripped packet must be signature we removed",
sig.getEncoded(), onlyA.get(1).buf);
}
}
+ @Test public void testUidDestroy() throws Exception {
+
+ // signature for "twi"
+ ring = KeyringTestingHelper.removePacket(ring, 2);
+ // signature for "pink"
+ ring = KeyringTestingHelper.removePacket(ring, 3);
+
+ // canonicalization should fail, because there are no valid uids left
+ CanonicalizedKeyRing canonicalized = ring.canonicalize(log, 0);
+ Assert.assertNull("canonicalization of keyring with no valid uids should fail", canonicalized);
+
+ }
+
+ @Test public void testRevocationRedundant() throws Exception {
+
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.KEY_REVOCATION, subHashedPacketsGen, secretKey.getPublicKey());
+
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, revocation.getEncoded(), 1);
+
+ // try to add the same packet again, it should be rejected in all positions
+ injectEverywhere(modified, revocation.getEncoded());
+
+ // an older (but different!) revocation should be rejected as well
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ revocation = forgeSignature(
+ secretKey, PGPSignature.KEY_REVOCATION, subHashedPacketsGen, secretKey.getPublicKey());
+
+ injectEverywhere(modified, revocation.getEncoded());
+
+ }
+
+ @Test public void testUidRedundant() throws Exception {
+
+ // an older uid certificate should be rejected
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.DEFAULT_CERTIFICATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ injectEverywhere(ring, revocation.getEncoded());
+
+ }
+
+ @Test public void testUidRevocationOutdated() throws Exception {
+ // an older uid revocation cert should be rejected
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ injectEverywhere(ring, revocation.getEncoded());
+
+ }
+
+ @Test public void testUidRevocationRedundant() throws Exception {
+
+ PGPSignature revocation = forgeSignature(
+ secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ // add that revocation to the base, and check if the redundant one will be rejected as well
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, revocation.getEncoded(), 2);
+
+ injectEverywhere(modified, revocation.getEncoded());
+
+ // an older (but different!) uid revocation should be rejected as well
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ revocation = forgeSignature(
+ secretKey, PGPSignature.CERTIFICATION_REVOCATION, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ injectEverywhere(modified, revocation.getEncoded());
+
+ }
+
+ @Test public void testSignatureBroken() throws Exception {
+
+ injectEverytype(secretKey, ring, subHashedPacketsGen, true);
+
+ }
+
+ @Test public void testForeignSignature() throws Exception {
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddUserIds.add("trix");
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ UncachedKeyRing foreign = op.createSecretKeyRing(parcel).getRing();
+
+ Assert.assertNotNull("initial test key creation must succeed", foreign);
+ PGPSecretKey foreignSecretKey =
+ new PGPSecretKeyRing(foreign.getEncoded(), new JcaKeyFingerprintCalculator())
+ .getSecretKey();
+
+ injectEverytype(foreignSecretKey, ring, subHashedPacketsGen);
+
+ }
+
+ @Test public void testSignatureFuture() throws Exception {
+
+ // generate future
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() + 1000 * 1000));
+
+ injectEverytype(secretKey, ring, subHashedPacketsGen);
+
+
+ }
+
+ @Test public void testSignatureLocal() throws Exception {
+
+ // generate future
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setExportable(false, false);
+
+ injectEverytype(secretKey, ring, subHashedPacketsGen);
+
+ }
+
+ @Test public void testSubkeyDestroy() throws Exception {
+
+ // signature for second key (first subkey)
+ UncachedKeyRing modified = KeyringTestingHelper.removePacket(ring, 6);
+
+ // canonicalization should fail, because there are no valid uids left
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertTrue("keyring with missing subkey binding sig should differ from intact one after canonicalization",
+ KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+
+ Assert.assertEquals("canonicalized keyring should have two extra packets", 2, onlyA.size());
+ Assert.assertEquals("canonicalized keyring should have no extra packets", 0, onlyB.size());
+
+ Assert.assertEquals("first missing packet should be the subkey",
+ PacketTags.SECRET_SUBKEY, onlyA.get(0).tag);
+ Assert.assertEquals("second missing packet should be subkey's signature",
+ PacketTags.SIGNATURE, onlyA.get(1).tag);
+ Assert.assertEquals("second missing packet should be next to subkey",
+ onlyA.get(0).position + 1, onlyA.get(1).position);
+
+ }
+
+ @Test public void testSubkeyBindingNoPKB() throws Exception {
+
+ UncachedPublicKey pKey = KeyringTestingHelper.getNth(ring.getPublicKeys(), 1);
+ Assert.assertTrue("second subkey must be able to sign", pKey.canSign());
+
+ PGPSignature sig;
+
+ subHashedPacketsGen.setKeyFlags(false, KeyFlags.SIGN_DATA);
+
+ {
+ // forge a (newer) signature, which has the sign flag but no primary key binding sig
+ PGPSignatureSubpacketGenerator unhashedSubs = new PGPSignatureSubpacketGenerator();
+
+ // just add any random signature, because why not
+ unhashedSubs.setEmbeddedSignature(false, forgeSignature(
+ secretKey, PGPSignature.POSITIVE_CERTIFICATION, subHashedPacketsGen,
+ secretKey.getPublicKey()
+ )
+ );
+
+ sig = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, unhashedSubs,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ // inject in the right position
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig.getEncoded(), 6);
+
+ // canonicalize, and check if we lose the bad signature
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertFalse("subkey binding signature should be gone after canonicalization",
+ KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+ }
+
+ { // now try one with a /bad/ primary key binding signature
+
+ PGPSignatureSubpacketGenerator unhashedSubs = new PGPSignatureSubpacketGenerator();
+ // this one is signed by the primary key itself, not the subkey - but it IS primary binding
+ unhashedSubs.setEmbeddedSignature(false, forgeSignature(
+ secretKey, PGPSignature.PRIMARYKEY_BINDING, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey()
+ )
+ );
+
+ sig = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen, unhashedSubs,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ // inject in the right position
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig.getEncoded(), 6);
+
+ // canonicalize, and check if we lose the bad signature
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertFalse("subkey binding signature should be gone after canonicalization",
+ KeyringTestingHelper.diffKeyrings(ring.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+ }
+
+ }
+
+ @Test public void testSubkeyBindingRedundant() throws Exception {
+
+ UncachedPublicKey pKey = KeyringTestingHelper.getNth(ring.getPublicKeys(), 2);
+
+ subHashedPacketsGen.setKeyFlags(false, KeyFlags.ENCRYPT_COMMS);
+ PGPSignature sig2 = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -1000*1000));
+ PGPSignature sig1 = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_REVOCATION, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, new Date(new Date().getTime() -100*1000));
+ PGPSignature sig3 = forgeSignature(
+ secretKey, PGPSignature.SUBKEY_BINDING, subHashedPacketsGen,
+ secretKey.getPublicKey(), pKey.getPublicKey());
+
+ UncachedKeyRing modified = KeyringTestingHelper.injectPacket(ring, sig1.getEncoded(), 8);
+ modified = KeyringTestingHelper.injectPacket(modified, sig2.getEncoded(), 9);
+ modified = KeyringTestingHelper.injectPacket(modified, sig1.getEncoded(), 10);
+ modified = KeyringTestingHelper.injectPacket(modified, sig3.getEncoded(), 11);
+
+ // canonicalize, and check if we lose the bad signature
+ CanonicalizedKeyRing canonicalized = modified.canonicalize(log, 0);
+ Assert.assertTrue("subkey binding signature should be gone after canonicalization",
+ KeyringTestingHelper.diffKeyrings(modified.getEncoded(), canonicalized.getEncoded(),
+ onlyA, onlyB)
+ );
+
+ Assert.assertEquals("canonicalized keyring should have lost two packets", 3, onlyA.size());
+ Assert.assertEquals("canonicalized keyring should have no extra packets", 0, onlyB.size());
+
+ Assert.assertEquals("first missing packet should be the subkey",
+ PacketTags.SIGNATURE, onlyA.get(0).tag);
+ Assert.assertEquals("second missing packet should be a signature",
+ PacketTags.SIGNATURE, onlyA.get(1).tag);
+ Assert.assertEquals("second missing packet should be a signature",
+ PacketTags.SIGNATURE, onlyA.get(2).tag);
+
+ }
+
+ private static final int[] sigtypes_direct = new int[] {
+ PGPSignature.KEY_REVOCATION,
+ PGPSignature.DIRECT_KEY,
+ };
+ private static final int[] sigtypes_uid = new int[] {
+ PGPSignature.DEFAULT_CERTIFICATION,
+ PGPSignature.NO_CERTIFICATION,
+ PGPSignature.CASUAL_CERTIFICATION,
+ PGPSignature.POSITIVE_CERTIFICATION,
+ PGPSignature.CERTIFICATION_REVOCATION,
+ };
+ private static final int[] sigtypes_subkey = new int[] {
+ PGPSignature.SUBKEY_BINDING,
+ PGPSignature.PRIMARYKEY_BINDING,
+ PGPSignature.SUBKEY_REVOCATION,
+ };
+
+ private static void injectEverytype(PGPSecretKey secretKey,
+ UncachedKeyRing ring,
+ PGPSignatureSubpacketGenerator subHashedPacketsGen)
+ throws Exception {
+ injectEverytype(secretKey, ring, subHashedPacketsGen, false);
+ }
+
+ private static void injectEverytype(PGPSecretKey secretKey,
+ UncachedKeyRing ring,
+ PGPSignatureSubpacketGenerator subHashedPacketsGen,
+ boolean breakSig)
+ throws Exception {
+
+ for (int sigtype : sigtypes_direct) {
+ PGPSignature sig = forgeSignature(
+ secretKey, sigtype, subHashedPacketsGen, secretKey.getPublicKey());
+ byte[] encoded = sig.getEncoded();
+ if (breakSig) {
+ encoded[encoded.length-10] += 1;
+ }
+ injectEverywhere(ring, encoded);
+ }
+
+ for (int sigtype : sigtypes_uid) {
+ PGPSignature sig = forgeSignature(
+ secretKey, sigtype, subHashedPacketsGen, "twi", secretKey.getPublicKey());
+
+ byte[] encoded = sig.getEncoded();
+ if (breakSig) {
+ encoded[encoded.length-10] += 1;
+ }
+ injectEverywhere(ring, encoded);
+ }
+
+ for (int sigtype : sigtypes_subkey) {
+ PGPSignature sig = forgeSignature(
+ secretKey, sigtype, subHashedPacketsGen,
+ secretKey.getPublicKey(), secretKey.getPublicKey());
+
+ byte[] encoded = sig.getEncoded();
+ if (breakSig) {
+ encoded[encoded.length-10] += 1;
+ }
+ injectEverywhere(ring, encoded);
+ }
+
+ }
+
+ private static void injectEverywhere(UncachedKeyRing ring, byte[] packet) throws Exception {
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+
+ byte[] encodedRing = ring.getEncoded();
+
+ for(int i = 0; i < totalPackets; i++) {
+
+ byte[] brokenEncoded = KeyringTestingHelper.injectPacket(encodedRing, packet, i);
+
+ try {
+
+ UncachedKeyRing brokenRing = UncachedKeyRing.decodeFromData(brokenEncoded);
+
+ CanonicalizedKeyRing canonicalized = brokenRing.canonicalize(log, 0);
+ if (canonicalized == null) {
+ System.out.println("ok, canonicalization failed.");
+ continue;
+ }
+
+ Assert.assertArrayEquals("injected bad signature must be gone after canonicalization",
+ ring.getEncoded(), canonicalized.getEncoded());
+
+ } catch (Exception e) {
+ System.out.println("ok, rejected with: " + e.getMessage());
+ }
+ }
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator subpackets,
+ PGPPublicKey publicKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(subpackets.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(publicKey);
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator subpackets,
+ String userId, PGPPublicKey publicKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(subpackets.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(userId, publicKey);
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator subpackets,
+ PGPPublicKey publicKey, PGPPublicKey signedKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(subpackets.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(publicKey, signedKey);
+
+ }
+
+ private static PGPSignature forgeSignature(PGPSecretKey key, int type,
+ PGPSignatureSubpacketGenerator hashedSubs,
+ PGPSignatureSubpacketGenerator unhashedSubs,
+ PGPPublicKey publicKey, PGPPublicKey signedKey)
+ throws Exception {
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ PGPPrivateKey privateKey = key.extractPrivateKey(keyDecryptor);
+
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ publicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.setHashedSubpackets(hashedSubs.generate());
+ sGen.setUnhashedSubpackets(unhashedSubs.generate());
+ sGen.init(type, privateKey);
+ return sGen.generateCertification(publicKey, signedKey);
+
+ }
+
}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java
new file mode 100644
index 000000000..977ddfc52
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringMergeTest.java
@@ -0,0 +1,397 @@
+package org.sufficientlysecure.keychain.tests;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowLog;
+import org.spongycastle.bcpg.PacketTags;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/** Tests for the UncachedKeyring.merge method.
+ *
+ * This is another complex, crypto-related method. It merges information from one keyring into
+ * another, keeping information from the base (ie, called object) keyring in case of conflicts.
+ * The types of keys may be Public or Secret and can be mixed, For mixed types the result type
+ * will be the same as the base keyring.
+ *
+ * Test cases:
+ * - Merging keyrings with different masterKeyIds should fail
+ * - Merging a key with itself should be a no-operation
+ * - Merging a key with an extra revocation certificate, it should have that certificate
+ * - Merging a key with an extra user id, it should have that extra user id and its certificates
+ * - Merging a key with an extra user id certificate, it should have that certificate
+ * - Merging a key with an extra subkey, it should have that subkey
+ * - Merging a key with an extra subkey certificate, it should have that certificate
+ * - All of the above operations should work regardless of the key types. This means in particular
+ * that for new subkeys, an equivalent subkey of the proper type must be generated.
+ * - In case of two secret keys with the same id but different S2K, the key of the base keyring
+ * should be preferred (TODO or should it?)
+ *
+ * Note that the merge operation does not care about certificate validity, a bad certificate or
+ * packet will be copied regardless. Filtering out bad packets is done with canonicalization.
+ *
+ */
+@RunWith(RobolectricTestRunner.class)
+@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+public class UncachedKeyringMergeTest {
+
+ static UncachedKeyRing staticRingA, staticRingB;
+ UncachedKeyRing ringA, ringB;
+ ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
+ ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ PgpKeyOperation op;
+ SaveKeyringParcel parcel;
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ ShadowLog.stream = System.out;
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+
+ parcel.mAddUserIds.add("twi");
+ parcel.mAddUserIds.add("pink");
+ // passphrase is tested in PgpKeyOperationTest, just use empty here
+ parcel.mNewPassphrase = "";
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ staticRingA = result.getRing();
+ }
+
+ {
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+
+ parcel.mAddUserIds.add("shy");
+ // passphrase is tested in PgpKeyOperationTest, just use empty here
+ parcel.mNewPassphrase = "";
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ OperationResultParcel.OperationLog log = new OperationResultParcel.OperationLog();
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ staticRingB = result.getRing();
+ }
+
+ Assert.assertNotNull("initial test key creation must succeed", staticRingA);
+ Assert.assertNotNull("initial test key creation must succeed", staticRingB);
+
+ // we sleep here for a second, to make sure all new certificates have different timestamps
+ Thread.sleep(1000);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ // show Log.x messages in system.out
+ ShadowLog.stream = System.out;
+ ringA = staticRingA;
+ ringB = staticRingB;
+
+ // setting up some parameters just to reduce code duplication
+ op = new PgpKeyOperation(new ProgressScaler(null, 0, 100, 100));
+
+ // set this up, gonna need it more than once
+ parcel = new SaveKeyringParcel();
+ parcel.mMasterKeyId = ringA.getMasterKeyId();
+ parcel.mFingerprint = ringA.getFingerprint();
+ }
+
+ public void testSelfNoOp() throws Exception {
+
+ UncachedKeyRing merged = mergeWithChecks(ringA, ringA, null);
+ Assert.assertArrayEquals("keyring merged with itself must be identical",
+ ringA.getEncoded(), merged.getEncoded()
+ );
+
+ }
+
+ @Test
+ public void testDifferentMasterKeyIds() throws Exception {
+
+ Assert.assertNotEquals("generated key ids must be different",
+ ringA.getMasterKeyId(), ringB.getMasterKeyId());
+
+ Assert.assertNull("merging keys with differing key ids must fail",
+ ringA.merge(ringB, log, 0));
+ Assert.assertNull("merging keys with differing key ids must fail",
+ ringB.merge(ringA, log, 0));
+
+ }
+
+ @Test
+ public void testAddedUserId() throws Exception {
+
+ UncachedKeyRing modifiedA, modifiedB; {
+ CanonicalizedSecretKeyRing secretRing =
+ new CanonicalizedSecretKeyRing(ringA.getEncoded(), false, 0);
+
+ parcel.reset();
+ parcel.mAddUserIds.add("flim");
+ modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+
+ parcel.reset();
+ parcel.mAddUserIds.add("flam");
+ modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+ }
+
+ { // merge A into base
+ UncachedKeyRing merged = mergeWithChecks(ringA, modifiedA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained two packets", 2, onlyB.size());
+ Assert.assertTrue("merged keyring must contain new user id",
+ merged.getPublicKey().getUnorderedUserIds().contains("flim"));
+ }
+
+ { // merge A into B
+ UncachedKeyRing merged = mergeWithChecks(modifiedA, modifiedB, ringA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained four packets", 4, onlyB.size());
+ Assert.assertTrue("merged keyring must contain first new user id",
+ merged.getPublicKey().getUnorderedUserIds().contains("flim"));
+ Assert.assertTrue("merged keyring must contain second new user id",
+ merged.getPublicKey().getUnorderedUserIds().contains("flam"));
+
+ }
+
+ }
+
+ @Test
+ public void testAddedSubkeyId() throws Exception {
+
+ UncachedKeyRing modifiedA, modifiedB;
+ long subKeyIdA, subKeyIdB;
+ {
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ringA.getEncoded(), false, 0);
+
+ parcel.reset();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+ modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+ modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+
+ subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
+ subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
+
+ }
+
+ {
+ UncachedKeyRing merged = mergeWithChecks(ringA, modifiedA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained two packets", 2, onlyB.size());
+
+ long mergedKeyId = KeyringTestingHelper.getSubkeyId(merged, 2);
+ Assert.assertEquals("merged keyring must contain the new subkey", subKeyIdA, mergedKeyId);
+ }
+
+ {
+ UncachedKeyRing merged = mergeWithChecks(modifiedA, modifiedB, ringA);
+
+ Assert.assertEquals("merged keyring must have lost no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring must have gained four packets", 4, onlyB.size());
+
+ Iterator<UncachedPublicKey> it = merged.getPublicKeys();
+ it.next(); it.next();
+ Assert.assertEquals("merged keyring must contain the new subkey",
+ subKeyIdA, it.next().getKeyId());
+ Assert.assertEquals("merged keyring must contain both new subkeys",
+ subKeyIdB, it.next().getKeyId());
+ }
+
+ }
+
+ @Test
+ public void testAddedKeySignature() throws Exception {
+
+ final UncachedKeyRing modified; {
+ parcel.reset();
+ parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1));
+ CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
+ ringA.getEncoded(), false, 0);
+ modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing();
+ }
+
+ {
+ UncachedKeyRing merged = ringA.merge(modified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertFalse(
+ "merging keyring with extra signatures into its base should yield that same keyring",
+ KeyringTestingHelper.diffKeyrings(merged.getEncoded(), modified.getEncoded(), onlyA, onlyB)
+ );
+ }
+
+ }
+
+ @Test
+ public void testAddedUserIdSignature() throws Exception {
+
+ final UncachedKeyRing pubRing = ringA.extractPublicKeyRing();
+
+ final UncachedKeyRing modified; {
+ CanonicalizedPublicKeyRing publicRing = new CanonicalizedPublicKeyRing(
+ pubRing.getEncoded(), 0);
+
+ CanonicalizedSecretKey secretKey = new CanonicalizedSecretKeyRing(
+ ringB.getEncoded(), false, 0).getSecretKey();
+ secretKey.unlock("");
+ // sign all user ids
+ modified = secretKey.certifyUserIds(publicRing, publicRing.getPublicKey().getUnorderedUserIds());
+ }
+
+ {
+ UncachedKeyRing merged = ringA.merge(modified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertArrayEquals("foreign signatures should not be merged into secret key",
+ ringA.getEncoded(), merged.getEncoded()
+ );
+ }
+
+ {
+ byte[] sig = KeyringTestingHelper.getNth(
+ modified.getPublicKey().getSignaturesForId("twi"), 1).getEncoded();
+
+ // inject the (foreign!) signature into subkey signature position
+ UncachedKeyRing moreModified = KeyringTestingHelper.injectPacket(modified, sig, 1);
+
+ UncachedKeyRing merged = ringA.merge(moreModified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertArrayEquals("foreign signatures should not be merged into secret key",
+ ringA.getEncoded(), merged.getEncoded()
+ );
+
+ merged = pubRing.merge(moreModified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertTrue(
+ "merged keyring should contain new signature",
+ KeyringTestingHelper.diffKeyrings(pubRing.getEncoded(), merged.getEncoded(), onlyA, onlyB)
+ );
+ Assert.assertEquals("merged keyring should be missing no packets", 0, onlyA.size());
+ Assert.assertEquals("merged keyring should contain exactly two more packets", 2, onlyB.size());
+ Assert.assertEquals("first added packet should be a signature",
+ PacketTags.SIGNATURE, onlyB.get(0).tag);
+ Assert.assertEquals("first added packet should be in the position we injected it at",
+ 1, onlyB.get(0).position);
+ Assert.assertEquals("second added packet should be a signature",
+ PacketTags.SIGNATURE, onlyB.get(1).tag);
+
+ }
+
+ {
+ UncachedKeyRing merged = pubRing.merge(modified, log, 0);
+ Assert.assertNotNull("merge must succeed", merged);
+ Assert.assertFalse(
+ "merging keyring with extra signatures into its base should yield that same keyring",
+ KeyringTestingHelper.diffKeyrings(merged.getEncoded(), modified.getEncoded(), onlyA, onlyB)
+ );
+ }
+ }
+
+ private UncachedKeyRing mergeWithChecks(UncachedKeyRing a, UncachedKeyRing b)
+ throws Exception {
+ return mergeWithChecks(a, b, a);
+ }
+
+ private UncachedKeyRing mergeWithChecks(UncachedKeyRing a, UncachedKeyRing b,
+ UncachedKeyRing base)
+ throws Exception {
+
+ Assert.assertTrue("merging keyring must be secret type", a.isSecret());
+ Assert.assertTrue("merged keyring must be secret type", b.isSecret());
+
+ final UncachedKeyRing resultA;
+ UncachedKeyRing resultB;
+
+ { // sec + sec
+ resultA = a.merge(b, log, 0);
+ Assert.assertNotNull("merge must succeed as sec(a)+sec(b)", resultA);
+
+ resultB = b.merge(a, log, 0);
+ Assert.assertNotNull("merge must succeed as sec(b)+sec(a)", resultB);
+
+ // check commutativity, if requested
+ Assert.assertFalse("result of merge must be commutative",
+ KeyringTestingHelper.diffKeyrings(
+ resultA.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+ }
+
+ final UncachedKeyRing pubA = a.extractPublicKeyRing();
+ final UncachedKeyRing pubB = b.extractPublicKeyRing();
+
+ { // sec + pub, pub + sec, and pub + pub
+
+ try {
+ resultB = a.merge(pubB, log, 0);
+ Assert.assertNotNull("merge must succeed as sec(a)+pub(b)", resultA);
+
+ Assert.assertFalse("result of sec(a)+pub(b) must be same as sec(a)+sec(b)",
+ KeyringTestingHelper.diffKeyrings(
+ resultA.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+ } catch (RuntimeException e) {
+ System.out.println("special case, dummy key generation not in yet");
+ }
+
+ final UncachedKeyRing pubResult = resultA.extractPublicKeyRing();
+
+ resultB = pubA.merge(b, log, 0);
+ Assert.assertNotNull("merge must succeed as pub(a)+sec(b)", resultA);
+
+ Assert.assertFalse("result of pub(a)+sec(b) must be same as pub(sec(a)+sec(b))",
+ KeyringTestingHelper.diffKeyrings(
+ pubResult.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+
+ resultB = pubA.merge(pubB, log, 0);
+ Assert.assertNotNull("merge must succeed as pub(a)+pub(b)", resultA);
+
+ Assert.assertFalse("result of pub(a)+pub(b) must be same as pub(sec(a)+sec(b))",
+ KeyringTestingHelper.diffKeyrings(
+ pubResult.getEncoded(), resultB.getEncoded(), onlyA, onlyB)
+ );
+
+ }
+
+ if (base != null) {
+ // set up onlyA and onlyB to be a diff to the base
+ Assert.assertTrue("merged keyring must differ from base",
+ KeyringTestingHelper.diffKeyrings(
+ base.getEncoded(), resultA.getEncoded(), onlyA, onlyB)
+ );
+ }
+
+ return resultA;
+
+ }
+
+}
diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java
new file mode 100644
index 000000000..15aaa4c5d
--- /dev/null
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/tests/UncachedKeyringTest.java
@@ -0,0 +1,125 @@
+package org.sufficientlysecure.keychain.tests;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowLog;
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
+public class UncachedKeyringTest {
+
+ static UncachedKeyRing staticRing, staticPubRing;
+ UncachedKeyRing ring, pubRing;
+ ArrayList<RawPacket> onlyA = new ArrayList<RawPacket>();
+ ArrayList<RawPacket> onlyB = new ArrayList<RawPacket>();
+ PgpKeyOperation op;
+ SaveKeyringParcel parcel;
+
+ @BeforeClass
+ public static void setUpOnce() throws Exception {
+ ShadowLog.stream = System.out;
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Constants.choice.algorithm.rsa, 1024, KeyFlags.ENCRYPT_COMMS, null));
+
+ parcel.mAddUserIds.add("twi");
+ parcel.mAddUserIds.add("pink");
+ // passphrase is tested in PgpKeyOperationTest, just use empty here
+ parcel.mNewPassphrase = "";
+ PgpKeyOperation op = new PgpKeyOperation(null);
+
+ EditKeyResult result = op.createSecretKeyRing(parcel);
+ staticRing = result.getRing();
+ staticPubRing = staticRing.extractPublicKeyRing();
+
+ Assert.assertNotNull("initial test key creation must succeed", staticRing);
+
+ // we sleep here for a second, to make sure all new certificates have different timestamps
+ Thread.sleep(1000);
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ // show Log.x messages in system.out
+ ShadowLog.stream = System.out;
+ ring = staticRing;
+ pubRing = staticPubRing;
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void testPublicKeyItRemove() throws Exception {
+ Iterator<UncachedPublicKey> it = ring.getPublicKeys();
+ it.remove();
+ }
+
+ @Test(expected = PgpGeneralException.class)
+ public void testDecodeFromEmpty() throws Exception {
+ UncachedKeyRing.decodeFromData(new byte[0]);
+ }
+
+ @Test
+ public void testArmorIdentity() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ring.encodeArmored(out, "OpenKeychain");
+
+ Assert.assertArrayEquals("armor encoded and decoded ring should be identical to original",
+ ring.getEncoded(),
+ UncachedKeyRing.decodeFromData(out.toByteArray()).getEncoded());
+ }
+
+ @Test(expected = PgpGeneralException.class)
+ public void testDecodeEncodeMulti() throws Exception {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ // encode secret and public ring in here
+ ring.encodeArmored(out, "OpenKeychain");
+ pubRing.encodeArmored(out, "OpenKeychain");
+
+ Iterator<UncachedKeyRing> it =
+ UncachedKeyRing.fromStream(new ByteArrayInputStream(out.toByteArray()));
+ Assert.assertTrue("there should be two rings in the stream", it.hasNext());
+ Assert.assertArrayEquals("first ring should be the first we put in",
+ ring.getEncoded(), it.next().getEncoded());
+ Assert.assertTrue("there should be two rings in the stream", it.hasNext());
+ Assert.assertArrayEquals("second ring should be the second we put in",
+ pubRing.getEncoded(), it.next().getEncoded());
+ Assert.assertFalse("there should be two rings in the stream", it.hasNext());
+
+ // this should fail with PgpGeneralException, since it expects exactly one ring
+ UncachedKeyRing.decodeFromData(out.toByteArray());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testPublicExtractPublic() throws Exception {
+ // can't do this, either!
+ pubRing.extractPublicKeyRing();
+ }
+
+}
diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle
index aa1932c56..265411595 100644
--- a/OpenKeychain/build.gradle
+++ b/OpenKeychain/build.gradle
@@ -15,7 +15,6 @@ dependencies {
compile project(':extern:spongycastle:pg')
compile project(':extern:spongycastle:pkix')
compile project(':extern:spongycastle:prov')
- compile project(':extern:AppMsg:library')
compile project(':extern:SuperToasts:supertoasts')
compile project(':extern:minidns')
compile project(':extern:KeybaseLib:Lib')
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml
index af09019e8..7af9d895f 100644
--- a/OpenKeychain/src/main/AndroidManifest.xml
+++ b/OpenKeychain/src/main/AndroidManifest.xml
@@ -31,7 +31,7 @@
For OI Filemanager it makes no difference, gpg files can't be associated
-->
- <!-- Specified in buid.gradle -->
+ <!-- Specified in build.gradle -->
<!--<uses-sdk-->
<!--android:minSdkVersion="9"-->
<!--android:targetSdkVersion="19" />-->
@@ -53,10 +53,10 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
- <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
- <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
- <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
- <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+ <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+ <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
@@ -84,22 +84,23 @@
android:name=".ui.FirstTimeActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name"
- android:windowSoftInputMode="stateHidden" />
+ android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".ui.CreateKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_create_key"
- android:windowSoftInputMode="stateHidden" />
+ android:label="@string/title_create_key">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".ui.KeyListActivity" />
+ </activity>
<activity
- android:name=".ui.EditKeyActivityOld"
+ android:name=".ui.EditKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_edit_key"
- android:windowSoftInputMode="stateHidden" />
+ android:label="@string/title_edit_key" />
<activity
- android:name=".ui.EditKeyActivity"
+ android:name=".ui.QrCodeActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_edit_key"
- android:windowSoftInputMode="stateHidden" />
+ android:label="@string/share_qr_code_dialog_title" />
<activity
android:name=".ui.ViewKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@@ -222,7 +223,19 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
- <!-- Workaround to match files in pathes with dots in them, like /cdcard/my.folder/test.gpg -->
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
+ <data android:pathPattern=".*\\.asc" />
+ <data android:pathPattern=".*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
<data android:pathPattern=".*\\.gpg" />
<data android:pathPattern=".*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\.gpg" />
@@ -233,7 +246,34 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
+ <!--
+ Some apps will only respect these file associations
+ if the mimeType is not set, and other apps will only respect them if mimeType is set
+ to */*. Therefore we have two whole copies of the same thing, besides setting the mimeType.
+ -->
<intent-filter android:label="@string/intent_decrypt_file">
<action android:name="android.intent.action.VIEW" />
@@ -243,7 +283,10 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
+
<data android:mimeType="*/*" />
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
<data android:pathPattern=".*\\.asc" />
<data android:pathPattern=".*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\.asc" />
@@ -254,6 +297,39 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
+ <data android:pathPattern=".*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
</activity>
<activity
@@ -323,6 +399,9 @@
<!-- mime type as defined in http://tools.ietf.org/html/rfc3156 -->
<data android:mimeType="application/pgp-keys" />
+ <!-- also link to text/plain, AOSP mail and K-9 mail only give mimeType text/plain
+ when the key file has been manually attached -->
+ <data android:mimeType="text/plain" />
</intent-filter>
<!-- NFC: Handle NFC tags detected from outside our application -->
<intent-filter>
@@ -342,6 +421,19 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
+ <data android:pathPattern=".*\\.asc" />
+ <data android:pathPattern=".*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
<data android:pathPattern=".*\\.gpg" />
<data android:pathPattern=".*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\.gpg" />
@@ -352,8 +444,34 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
- <!-- VIEW with file endings: *.asc -->
+ <!--
+ Some apps will only respect these file associations
+ if the mimeType is not set, and other apps will only respect them if mimeType is set
+ to */*. Therefore we have two whole copies of the same thing, besides setting the mimeType.
+ -->
<intent-filter android:label="@string/intent_import_key">
<action android:name="android.intent.action.VIEW" />
@@ -363,7 +481,10 @@
<data android:host="*" />
<data android:scheme="file" />
<data android:scheme="content" />
+
<data android:mimeType="*/*" />
+
+ <!-- GnuPG ASCII data, mostly keys, but sometimes signatures and encrypted data -->
<data android:pathPattern=".*\\.asc" />
<data android:pathPattern=".*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\.asc" />
@@ -374,7 +495,41 @@
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.asc" />
+ <!-- GnuPG binary encrypted/signed data, binary format -->
+ <data android:pathPattern=".*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.gpg" />
+ <!-- PGP encrypted data, binary format -->
+ <data android:pathPattern=".*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.pgp" />
+ <!-- on some mail clients, PGP attachments show up as *.bin -->
+ <data android:pathPattern=".*\\.bin" />
+ <data android:pathPattern=".*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
+ <data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.bin" />
</intent-filter>
+
<!-- Keychain's own Actions -->
<!-- IMPORT_KEY with files TODO: does this work? -->
<intent-filter android:label="@string/intent_import_key">
@@ -469,24 +624,24 @@
<service android:name=".service.DummyAccountService">
<intent-filter>
- <action android:name="android.accounts.AccountAuthenticator"/>
+ <action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
- android:name="android.accounts.AccountAuthenticator"
- android:resource="@xml/account_desc"/>
+ android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/account_desc" />
</service>
<service android:name=".service.ContactSyncAdapterService">
<intent-filter>
- <action android:name="android.content.SyncAdapter"/>
+ <action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
- android:name="android.content.SyncAdapter"
- android:resource="@xml/sync_adapter_desc"/>
+ android:name="android.content.SyncAdapter"
+ android:resource="@xml/sync_adapter_desc" />
<meta-data
- android:name="android.provider.CONTACTS_STRUCTURE"
- android:resource="@xml/custom_pgp_contacts_structure"/>
+ android:name="android.provider.CONTACTS_STRUCTURE"
+ android:resource="@xml/custom_pgp_contacts_structure" />
</service>
</application>
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index 72df3e4b6..a060092a3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -129,7 +129,7 @@ public class Preferences {
editor.commit();
}
- public boolean getFirstTime() {
+ public boolean isFirstTime() {
return mSharedPreferences.getBoolean(Constants.Pref.FIRST_TIME, true);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java
new file mode 100644
index 000000000..08b8afae7
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FileImportCache.java
@@ -0,0 +1,97 @@
+/*
+ * 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.keyimport;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Parcel;
+
+import org.sufficientlysecure.keychain.KeychainApplication;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * When sending large data (over 1MB) through Androids Binder IPC you get
+ * JavaBinder E !!! FAILED BINDER TRANSACTION !!!
+ * <p/>
+ * To overcome this problem, we cache large Parcelables into a file in our private cache directory
+ * instead of sending them through IPC.
+ */
+public class FileImportCache {
+
+ private Context mContext;
+
+ private static final String FILENAME = "key_import.pcl";
+ private static final String BUNDLE_DATA = "data";
+
+ public FileImportCache(Context context) {
+ this.mContext = context;
+ }
+
+ public void writeCache(ArrayList<ParcelableKeyRing> selectedEntries) throws IOException {
+ Bundle in = new Bundle();
+ in.putParcelableArrayList(BUNDLE_DATA, selectedEntries);
+ File cacheDir = mContext.getCacheDir();
+ if (cacheDir == null) {
+ // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
+ throw new IOException("cache dir is null!");
+ }
+ File tempFile = new File(mContext.getCacheDir(), FILENAME);
+
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ Parcel p = Parcel.obtain(); // creating empty parcel object
+ in.writeToParcel(p, 0); // saving bundle as parcel
+ fos.write(p.marshall()); // writing parcel to file
+ fos.flush();
+ fos.close();
+ }
+
+ public List<ParcelableKeyRing> readCache() throws IOException {
+ Parcel parcel = Parcel.obtain(); // creating empty parcel object
+ Bundle out;
+ File cacheDir = mContext.getCacheDir();
+ if (cacheDir == null) {
+ // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
+ throw new IOException("cache dir is null!");
+ }
+
+ File tempFile = new File(cacheDir, FILENAME);
+ try {
+ FileInputStream fis = new FileInputStream(tempFile);
+ byte[] array = new byte[(int) fis.getChannel().size()];
+ fis.read(array, 0, array.length);
+ fis.close();
+
+ parcel.unmarshall(array, 0, array.length);
+ parcel.setDataPosition(0);
+ out = parcel.readBundle(KeychainApplication.class.getClassLoader());
+ out.putAll(out);
+
+ return out.getParcelableArrayList(BUNDLE_DATA);
+ } finally {
+ parcel.recycle();
+ // delete temp file
+ tempFile.delete();
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
index 44679ba18..41f1e6997 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
@@ -22,6 +22,7 @@ import de.measite.minidns.Client;
import de.measite.minidns.Question;
import de.measite.minidns.Record;
import de.measite.minidns.record.SRV;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.helper.TlsHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@@ -102,8 +103,9 @@ public class HkpKeyserver extends Keyserver {
*/
public static final Pattern PUB_KEY_LINE = Pattern
.compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line
- + "((uid:([^:]*):([0-9]+):([0-9]*):([rde]*)[ \n\r]*)+)", // one or more uid lines
- Pattern.CASE_INSENSITIVE);
+ + "((uid:([^:]*):([0-9]+):([0-9]*):([rde]*)[ \n\r]*)+)", // one or more uid lines
+ Pattern.CASE_INSENSITIVE
+ );
/**
* uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags%
@@ -215,10 +217,18 @@ public class HkpKeyserver extends Keyserver {
throw new HttpError(response, data);
}
} catch (IOException e) {
- throw new QueryFailedException("querying server(s) for '" + mHost + "' failed");
+ throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!");
}
}
+ /**
+ * Results are sorted by creation date of key!
+ *
+ * @param query
+ * @return
+ * @throws QueryFailedException
+ * @throws QueryNeedsRepairException
+ */
@Override
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
QueryNeedsRepairException {
@@ -240,18 +250,26 @@ public class HkpKeyserver extends Keyserver {
try {
data = query(request);
} catch (HttpError e) {
- if (e.getCode() == 404) {
- return results;
- } else {
+ if (e.getData() != null) {
+ Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.US));
+
if (e.getData().toLowerCase(Locale.US).contains("no keys found")) {
+ // NOTE: This is also a 404 error for some keyservers!
return results;
} else if (e.getData().toLowerCase(Locale.US).contains("too many")) {
throw new TooManyResponsesException();
} else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) {
throw new QueryTooShortException();
+ } else if (e.getCode() == 404) {
+ // NOTE: handle this 404 at last, maybe it was a "no keys found" error
+ throw new QueryFailedException("Keyserver '" + mHost + "' not found. Error 404");
+ } else {
+ // NOTE: some keyserver do not provide a more detailed error response
+ throw new QueryTooShortOrTooManyResponsesException();
}
}
- throw new QueryFailedException("querying server(s) for '" + mHost + "' failed");
+
+ throw new QueryFailedException("Querying server(s) for '" + mHost + "' failed.");
}
final Matcher matcher = PUB_KEY_LINE.matcher(data);
@@ -267,9 +285,9 @@ public class HkpKeyserver extends Keyserver {
// group 1 contains the full fingerprint (v4) or the long key id if available
// see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr
- String fingerprintOrKeyId = matcher.group(1);
+ String fingerprintOrKeyId = matcher.group(1).toLowerCase(Locale.US);
if (fingerprintOrKeyId.length() > 16) {
- entry.setFingerprintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
+ entry.setFingerprintHex(fingerprintOrKeyId);
entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
- 16, fingerprintOrKeyId.length()));
} else {
@@ -291,7 +309,7 @@ public class HkpKeyserver extends Keyserver {
while (uidMatcher.find()) {
String tmp = uidMatcher.group(1).trim();
if (tmp.contains("%")) {
- if(tmp.contains("%%")) {
+ if (tmp.contains("%%")) {
// This is a fix for issue #683
// The server encodes a percent sign as %%, so it is swapped out with its
// urlencoded counterpart to prevent errors
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
index 842e7d922..b726529f8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java
@@ -44,6 +44,13 @@ public abstract class Keyserver {
private static final long serialVersionUID = 2703768928624654514L;
}
+ /**
+ * query too short _or_ too many responses
+ */
+ public static class QueryTooShortOrTooManyResponsesException extends QueryNeedsRepairException {
+ private static final long serialVersionUID = 2703768928624654514L;
+ }
+
public static class AddKeyException extends Exception {
private static final long serialVersionUID = -507574859137295530L;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index a054255dc..ee0dfefa4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -16,13 +16,11 @@ import java.io.OutputStream;
* getter method.
*
*/
-public abstract class WrappedKeyRing extends KeyRing {
+public abstract class CanonicalizedKeyRing extends KeyRing {
- private final boolean mHasAnySecret;
private final int mVerified;
- WrappedKeyRing(boolean hasAnySecret, int verified) {
- mHasAnySecret = hasAnySecret;
+ CanonicalizedKeyRing(int verified) {
mVerified = verified;
}
@@ -30,10 +28,6 @@ public abstract class WrappedKeyRing extends KeyRing {
return getRing().getPublicKey().getKeyID();
}
- public boolean hasAnySecret() {
- return mHasAnySecret;
- }
-
public int getVerified() {
return mVerified;
}
@@ -56,7 +50,7 @@ public abstract class WrappedKeyRing extends KeyRing {
}
public long getEncryptId() throws PgpGeneralException {
- for(WrappedPublicKey key : publicKeyIterator()) {
+ for(CanonicalizedPublicKey key : publicKeyIterator()) {
if(key.canEncrypt()) {
return key.getKeyId();
}
@@ -74,7 +68,7 @@ public abstract class WrappedKeyRing extends KeyRing {
}
public long getSignId() throws PgpGeneralException {
- for(WrappedPublicKey key : publicKeyIterator()) {
+ for(CanonicalizedPublicKey key : publicKeyIterator()) {
if(key.canSign()) {
return key.getKeyId();
}
@@ -103,14 +97,14 @@ public abstract class WrappedKeyRing extends KeyRing {
abstract PGPKeyRing getRing();
- abstract public IterableIterator<WrappedPublicKey> publicKeyIterator();
+ abstract public IterableIterator<CanonicalizedPublicKey> publicKeyIterator();
- public WrappedPublicKey getPublicKey() {
- return new WrappedPublicKey(this, getRing().getPublicKey());
+ public CanonicalizedPublicKey getPublicKey() {
+ return new CanonicalizedPublicKey(this, getRing().getPublicKey());
}
- public WrappedPublicKey getPublicKey(long id) {
- return new WrappedPublicKey(this, getRing().getPublicKey(id));
+ public CanonicalizedPublicKey getPublicKey(long id) {
+ return new CanonicalizedPublicKey(this, getRing().getPublicKey(id));
}
public byte[] getEncoded() throws IOException {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
index 69a4fbdee..981caad49 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
@@ -14,12 +14,12 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
* stored in the database.
*
*/
-public class WrappedPublicKey extends UncachedPublicKey {
+public class CanonicalizedPublicKey extends UncachedPublicKey {
// this is the parent key ring
final KeyRing mRing;
- WrappedPublicKey(KeyRing ring, PGPPublicKey key) {
+ CanonicalizedPublicKey(KeyRing ring, PGPPublicKey key) {
super(key);
mRing = ring;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index 57d84072a..70288dceb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -1,42 +1,45 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
import java.util.Iterator;
-public class WrappedPublicKeyRing extends WrappedKeyRing {
+public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
private PGPPublicKeyRing mRing;
- private final byte[] mPubKey;
- public WrappedPublicKeyRing(byte[] blob, boolean hasAnySecret, int verified) {
- super(hasAnySecret, verified);
- mPubKey = blob;
+ CanonicalizedPublicKeyRing(PGPPublicKeyRing ring, int verified) {
+ super(verified);
+ mRing = ring;
}
- PGPPublicKeyRing getRing() {
+ public CanonicalizedPublicKeyRing(byte[] blob, int verified) {
+ super(verified);
if(mRing == null) {
- PGPObjectFactory factory = new PGPObjectFactory(mPubKey);
- PGPKeyRing keyRing = null;
+ // get first object in block
+ PGPObjectFactory factory = new PGPObjectFactory(blob);
try {
- if ((keyRing = (PGPKeyRing) factory.nextObject()) == null) {
- Log.e(Constants.TAG, "No keys given!");
+ Object obj = factory.nextObject();
+ if (! (obj instanceof PGPPublicKeyRing)) {
+ throw new RuntimeException("Error constructing CanonicalizedPublicKeyRing, should never happen!");
+ }
+ mRing = (PGPPublicKeyRing) obj;
+ if (factory.nextObject() != null) {
+ throw new RuntimeException("Encountered trailing data after keyring, should never happen!");
}
} catch (IOException e) {
- Log.e(Constants.TAG, "Error while converting to PGPKeyRing!", e);
+ throw new RuntimeException("IO Error constructing CanonicalizedPublicKeyRing, should never happen!");
}
-
- mRing = (PGPPublicKeyRing) keyRing;
}
+ }
+
+ PGPPublicKeyRing getRing() {
return mRing;
}
@@ -45,10 +48,10 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
}
/** Getter that returns the subkey that should be used for signing. */
- WrappedPublicKey getEncryptionSubKey() throws PgpGeneralException {
+ CanonicalizedPublicKey getEncryptionSubKey() throws PgpGeneralException {
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
if(key != null) {
- WrappedPublicKey cKey = new WrappedPublicKey(this, key);
+ CanonicalizedPublicKey cKey = new CanonicalizedPublicKey(this, key);
if(!cKey.canEncrypt()) {
throw new PgpGeneralException("key error");
}
@@ -57,18 +60,18 @@ public class WrappedPublicKeyRing extends WrappedKeyRing {
throw new PgpGeneralException("no encryption key available");
}
- public IterableIterator<WrappedPublicKey> publicKeyIterator() {
+ public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
@SuppressWarnings("unchecked")
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedPublicKey next() {
- return new WrappedPublicKey(WrappedPublicKeyRing.this, it.next());
+ public CanonicalizedPublicKey next() {
+ return new CanonicalizedPublicKey(CanonicalizedPublicKeyRing.this, it.next());
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index cc6313b32..ff82da07a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -43,7 +43,7 @@ import java.util.List;
* properly imported secret keys only.
*
*/
-public class WrappedSecretKey extends WrappedPublicKey {
+public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
private final PGPSecretKey mSecretKey;
private PGPPrivateKey mPrivateKey = null;
@@ -53,21 +53,13 @@ public class WrappedSecretKey extends WrappedPublicKey {
private static int PRIVATE_KEY_STATE_UNLOCKED = 1;
private static int PRIVATE_KEY_STATE_DIVERT_TO_CARD = 2;
- WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) {
+ CanonicalizedSecretKey(CanonicalizedSecretKeyRing ring, PGPSecretKey key) {
super(ring, key.getPublicKey());
mSecretKey = key;
}
- public WrappedSecretKeyRing getRing() {
- return (WrappedSecretKeyRing) mRing;
- }
-
- /** Returns the wrapped PGPSecretKeyRing.
- * This function is for compatibility only, should not be used anymore and will be removed
- */
- @Deprecated
- public PGPSecretKey getKeyExternal() {
- return mSecretKey;
+ public CanonicalizedSecretKeyRing getRing() {
+ return (CanonicalizedSecretKeyRing) mRing;
}
/**
@@ -189,7 +181,7 @@ public class WrappedSecretKey extends WrappedPublicKey {
* @param userIds User IDs to certify, must not be null or empty
* @return A keyring with added certifications
*/
- public UncachedKeyRing certifyUserIds(WrappedPublicKeyRing publicKeyRing, List<String> userIds)
+ public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
index 5cb24cf88..7d3e26000 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
@@ -1,10 +1,12 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.bcpg.S2K;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
@@ -15,15 +17,21 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import java.io.IOException;
+import java.util.HashSet;
import java.util.Iterator;
-public class WrappedSecretKeyRing extends WrappedKeyRing {
+public class CanonicalizedSecretKeyRing extends CanonicalizedKeyRing {
private PGPSecretKeyRing mRing;
- public WrappedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
+ CanonicalizedSecretKeyRing(PGPSecretKeyRing ring, int verified) {
+ super(verified);
+ mRing = ring;
+ }
+
+ public CanonicalizedSecretKeyRing(byte[] blob, boolean isRevoked, int verified)
{
- super(isRevoked, verified);
+ super(verified);
PGPObjectFactory factory = new PGPObjectFactory(blob);
PGPKeyRing keyRing = null;
try {
@@ -41,19 +49,32 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
return mRing;
}
- public WrappedSecretKey getSecretKey() {
- return new WrappedSecretKey(this, mRing.getSecretKey());
+ public CanonicalizedSecretKey getSecretKey() {
+ return new CanonicalizedSecretKey(this, mRing.getSecretKey());
+ }
+
+ public CanonicalizedSecretKey getSecretKey(long id) {
+ return new CanonicalizedSecretKey(this, mRing.getSecretKey(id));
}
- public WrappedSecretKey getSecretKey(long id) {
- return new WrappedSecretKey(this, mRing.getSecretKey(id));
+ public HashSet<Long> getAvailableSubkeys() {
+ HashSet<Long> result = new HashSet<Long>();
+ // then, mark exactly the keys we have available
+ for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(getRing().getSecretKeys())) {
+ S2K s2k = sub.getS2K();
+ // add key, except if the private key has been stripped (GNU extension)
+ if(s2k == null || (s2k.getProtectionMode() != S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY)) {
+ result.add(sub.getKeyID());
+ }
+ }
+ return result;
}
/** Getter that returns the subkey that should be used for signing. */
- WrappedSecretKey getSigningSubKey() throws PgpGeneralException {
+ CanonicalizedSecretKey getSigningSubKey() throws PgpGeneralException {
PGPSecretKey key = mRing.getSecretKey(getSignId());
if(key != null) {
- WrappedSecretKey cKey = new WrappedSecretKey(this, key);
+ CanonicalizedSecretKey cKey = new CanonicalizedSecretKey(this, key);
if(!cKey.canSign()) {
throw new PgpGeneralException("key error");
}
@@ -88,17 +109,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
}
}
- public IterableIterator<WrappedSecretKey> secretKeyIterator() {
+ public IterableIterator<CanonicalizedSecretKey> secretKeyIterator() {
final Iterator<PGPSecretKey> it = mRing.getSecretKeys();
- return new IterableIterator<WrappedSecretKey>(new Iterator<WrappedSecretKey>() {
+ return new IterableIterator<CanonicalizedSecretKey>(new Iterator<CanonicalizedSecretKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedSecretKey next() {
- return new WrappedSecretKey(WrappedSecretKeyRing.this, it.next());
+ public CanonicalizedSecretKey next() {
+ return new CanonicalizedSecretKey(CanonicalizedSecretKeyRing.this, it.next());
}
@Override
@@ -108,17 +129,17 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
});
}
- public IterableIterator<WrappedPublicKey> publicKeyIterator() {
+ public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
- return new IterableIterator<WrappedPublicKey>(new Iterator<WrappedPublicKey>() {
+ return new IterableIterator<CanonicalizedPublicKey>(new Iterator<CanonicalizedPublicKey>() {
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
- public WrappedPublicKey next() {
- return new WrappedPublicKey(WrappedSecretKeyRing.this, it.next());
+ public CanonicalizedPublicKey next() {
+ return new CanonicalizedPublicKey(CanonicalizedSecretKeyRing.this, it.next());
}
@Override
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 129ffba3e..ebc49ab05 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -12,7 +12,7 @@ import java.util.regex.Pattern;
* keyring should in all cases agree on the output of all methods described
* here.
*
- * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see CanonicalizedKeyRing
* @see org.sufficientlysecure.keychain.provider.CachedPublicKeyRing
*
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index db9e2c6c6..7f2d971ed 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -231,7 +231,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
- WrappedSecretKey secretEncryptionKey = null;
+ CanonicalizedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@@ -243,10 +243,10 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- WrappedSecretKeyRing secretKeyRing;
+ CanonicalizedSecretKeyRing secretKeyRing;
try {
// get actual keyring object based on master key id
- secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(
+ secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(encData.getKeyID())
);
} catch (ProviderHelper.NotFoundException e) {
@@ -365,8 +365,8 @@ public class PgpDecryptVerify {
Object dataChunk = plainFact.nextObject();
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
int signatureIndex = -1;
- WrappedPublicKeyRing signingRing = null;
- WrappedPublicKey signingKey = null;
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@@ -390,7 +390,7 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
try {
long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
signingKey = signingRing.getPublicKey(sigKeyId);
@@ -566,8 +566,8 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
- WrappedPublicKeyRing signingRing = null;
- WrappedPublicKey signingKey = null;
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
int signatureIndex = -1;
// go through all signatures
@@ -575,7 +575,7 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
try {
long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getWrappedPublicKeyRing(
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
);
signingKey = signingRing.getPublicKey(sigKeyId);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index 6fc55cfb8..846b00ef2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -34,7 +34,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
-import org.sufficientlysecure.keychain.service.OperationResults.ImportResult;
+import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -93,7 +93,7 @@ public class PgpImportExport {
}
}
- public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) {
+ public boolean uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
@@ -123,14 +123,14 @@ public class PgpImportExport {
}
/** Imports keys from given data. If keyIds is given only those are imported */
- public ImportResult importKeyRings(List<ParcelableKeyRing> entries) {
+ public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries) {
updateProgress(R.string.progress_importing, 0, 100);
// If there aren't even any keys, do nothing here.
if (entries == null || entries.size() == 0) {
- return new ImportResult(
- ImportResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
+ return new ImportKeyResult(
+ ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
}
int newKeys = 0, oldKeys = 0, badKeys = 0;
@@ -185,26 +185,26 @@ public class PgpImportExport {
int resultType = 0;
// special return case: no new keys at all
if (badKeys == 0 && newKeys == 0 && oldKeys == 0) {
- resultType = ImportResult.RESULT_FAIL_NOTHING;
+ resultType = ImportKeyResult.RESULT_FAIL_NOTHING;
} else {
if (newKeys > 0) {
- resultType |= ImportResult.RESULT_OK_NEWKEYS;
+ resultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
}
if (oldKeys > 0) {
- resultType |= ImportResult.RESULT_OK_UPDATED;
+ resultType |= ImportKeyResult.RESULT_OK_UPDATED;
}
if (badKeys > 0) {
- resultType |= ImportResult.RESULT_WITH_ERRORS;
+ resultType |= ImportKeyResult.RESULT_WITH_ERRORS;
if (newKeys == 0 && oldKeys == 0) {
- resultType |= ImportResult.RESULT_ERROR;
+ resultType |= ImportKeyResult.RESULT_ERROR;
}
}
if (log.containsWarnings()) {
- resultType |= ImportResult.RESULT_WITH_WARNINGS;
+ resultType |= ImportKeyResult.RESULT_WITH_WARNINGS;
}
}
- return new ImportResult(resultType, log, newKeys, oldKeys, badKeys);
+ return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys);
}
@@ -235,7 +235,7 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing(
+ CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(pubKeyMasterId)
);
@@ -263,8 +263,8 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
- WrappedSecretKeyRing secretKeyRing =
- mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
+ CanonicalizedSecretKeyRing secretKeyRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(secretKeyMasterId);
secretKeyRing.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 00f73a5b0..861f93446 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -46,15 +46,17 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Primes;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.IOException;
import java.math.BigInteger;
@@ -67,6 +69,7 @@ import java.security.SignatureException;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
+import java.util.Stack;
/**
* This class is the single place where ALL operations that actually modify a PGP public or secret
@@ -78,7 +81,7 @@ import java.util.Iterator;
* This indicator may be null.
*/
public class PgpKeyOperation {
- private Progressable mProgress;
+ private Stack<Progressable> mProgress;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
@@ -92,13 +95,34 @@ public class PgpKeyOperation {
public PgpKeyOperation(Progressable progress) {
super();
- this.mProgress = progress;
+ if (progress != null) {
+ mProgress = new Stack<Progressable>();
+ mProgress.push(progress);
+ }
}
- void updateProgress(int message, int current, int total) {
- if (mProgress != null) {
- mProgress.setProgress(message, current, total);
+ private void subProgressPush(int from, int to) {
+ if (mProgress == null) {
+ return;
+ }
+ mProgress.push(new ProgressScaler(mProgress.peek(), from, to, 100));
+ }
+ private void subProgressPop() {
+ if (mProgress == null) {
+ return;
}
+ if (mProgress.size() == 1) {
+ throw new RuntimeException("Tried to pop progressable without prior push! "
+ + "This is a programming error, please file a bug report.");
+ }
+ mProgress.pop();
+ }
+
+ private void progress(int message, int current) {
+ if (mProgress == null) {
+ return;
+ }
+ mProgress.peek().setProgress(message, current, 100);
}
/** Creates new secret key. */
@@ -115,6 +139,7 @@ public class PgpKeyOperation {
switch (algorithmChoice) {
case Constants.choice.algorithm.dsa: {
+ progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
algorithm = PGPPublicKey.DSA;
@@ -122,6 +147,7 @@ public class PgpKeyOperation {
}
case Constants.choice.algorithm.elgamal: {
+ progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
BigInteger g = new BigInteger("2");
@@ -134,6 +160,7 @@ public class PgpKeyOperation {
}
case Constants.choice.algorithm.rsa: {
+ progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
keyGen.initialize(keySize, new SecureRandom());
@@ -163,43 +190,49 @@ public class PgpKeyOperation {
}
}
- public UncachedKeyRing createSecretKeyRing(SaveKeyringParcel saveParcel, OperationLog log,
- int indent) {
+ public EditKeyResult createSecretKeyRing(SaveKeyringParcel saveParcel) {
+
+ OperationLog log = new OperationLog();
+ int indent = 0;
try {
log.add(LogLevel.START, LogType.MSG_CR, indent);
+ progress(R.string.progress_building_key, 0);
indent += 1;
- updateProgress(R.string.progress_building_key, 0, 100);
if (saveParcel.mAddSubKeys.isEmpty()) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_MASTER, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
if (saveParcel.mAddUserIds.isEmpty()) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_USER_ID, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
SubkeyAdd add = saveParcel.mAddSubKeys.remove(0);
if ((add.mFlags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CERTIFY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
if (add.mAlgorithm == Constants.choice.algorithm.elgamal) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ subProgressPush(10, 30);
PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ subProgressPop();
// return null if this failed (an error will already have been logged by createKey)
if (keyPair == null) {
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ progress(R.string.progress_building_master_key, 40);
+
// define hashing and signing algos
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
.build().get(HashAlgorithmTags.SHA1);
@@ -213,15 +246,16 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR = new PGPSecretKeyRing(
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
- return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log, indent);
+ subProgressPush(50, 100);
+ return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log);
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
Log.e(Constants.TAG, "pgp error encoding key", e);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (IOException e) {
Log.e(Constants.TAG, "io error encoding key", e);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -237,8 +271,11 @@ public class PgpKeyOperation {
* are changed by adding new certificates, which implicitly override older certificates.
*
*/
- public UncachedKeyRing modifySecretKeyRing(WrappedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
- String passphrase, OperationLog log, int indent) {
+ public EditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
+ String passphrase) {
+
+ OperationLog log = new OperationLog();
+ int indent = 0;
/*
* 1. Unlock private key
@@ -251,14 +288,15 @@ public class PgpKeyOperation {
* 6. If requested, change passphrase
*/
- log.add(LogLevel.START, LogType.MSG_MF, indent);
+ log.add(LogLevel.START, LogType.MSG_MF, indent,
+ PgpKeyHelper.convertKeyIdToHex(wsKR.getMasterKeyId()));
indent += 1;
- updateProgress(R.string.progress_building_key, 0, 100);
+ progress(R.string.progress_building_key, 0);
// Make sure this is called with a proper SaveKeyringParcel
if (saveParcel.mMasterKeyId == null || saveParcel.mMasterKeyId != wsKR.getMasterKeyId()) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_KEYID, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// We work on bouncycastle object level here
@@ -270,27 +308,30 @@ public class PgpKeyOperation {
|| !Arrays.equals(saveParcel.mFingerprint,
masterSecretKey.getPublicKey().getFingerprint())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_FINGERPRINT, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// read masterKeyFlags, and use the same as before.
// since this is the master key, this contains at least CERTIFY_OTHER
int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER;
- return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log, indent);
+ return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log);
}
- private UncachedKeyRing internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
+ private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
int masterKeyFlags,
SaveKeyringParcel saveParcel, String passphrase,
- OperationLog log, int indent) {
+ OperationLog log) {
+
+ int indent = 1;
- updateProgress(R.string.progress_certifying_master_key, 20, 100);
+ progress(R.string.progress_modify, 0);
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
// 1. Unlock private key
+ progress(R.string.progress_modify_unlock, 10);
log.add(LogLevel.DEBUG, LogType.MSG_MF_UNLOCK, indent);
PGPPrivateKey masterPrivateKey;
{
@@ -300,7 +341,7 @@ public class PgpKeyOperation {
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -310,9 +351,18 @@ public class PgpKeyOperation {
PGPPublicKey modifiedPublicKey = masterPublicKey;
// 2a. Add certificates for new user ids
- for (String userId : saveParcel.mAddUserIds) {
+ subProgressPush(15, 25);
+ for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddUserIds.size()));
+ String userId = saveParcel.mAddUserIds.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent);
+ if (userId.equals("")) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
// this operation supersedes all previous binding and revocation certificates,
// so remove those to retain assertions from canonicalization for later operations
@SuppressWarnings("unchecked")
@@ -322,7 +372,7 @@ public class PgpKeyOperation {
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
// foreign certificate?! error error error
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
|| cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
@@ -343,19 +393,27 @@ public class PgpKeyOperation {
masterPublicKey, userId, isPrimary, masterKeyFlags);
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
+ subProgressPop();
// 2b. Add revocations for revoked user ids
- for (String userId : saveParcel.mRevokeUserIds) {
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent);
+ subProgressPush(25, 40);
+ for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mRevokeUserIds.size()));
+ String userId = saveParcel.mRevokeUserIds.get(i);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
+
// a duplicate revocation will be removed during canonicalization, so no need to
// take care of that here.
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
masterPublicKey, userId);
modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
}
+ subProgressPop();
// 3. If primary user id changed, generate new certificates for both old and new
if (saveParcel.mChangePrimaryUserId != null) {
+ progress(R.string.progress_modify_primaryuid, 40);
// keep track if we actually changed one
boolean ok = false;
@@ -373,7 +431,7 @@ public class PgpKeyOperation {
if (cert.getKeyID() != masterPublicKey.getKeyID()) {
// foreign certificate?! error error error
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// we know from canonicalization that if there is any revocation here, it
// is valid and not superseded by a newer certification.
@@ -394,7 +452,7 @@ public class PgpKeyOperation {
if (currentCert == null) {
// no certificate found?! error error error
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// we definitely should not update certifications of revoked keys, so just leave it.
@@ -402,13 +460,14 @@ public class PgpKeyOperation {
// revoked user ids cannot be primary!
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
continue;
}
// if this is~ the/a primary user id
- if (currentCert.hasSubpackets() && currentCert.getHashedSubPackets().isPrimaryUserID()) {
+ if (currentCert.getHashedSubPackets() != null
+ && currentCert.getHashedSubPackets().isPrimaryUserID()) {
// if it's the one we want, just leave it as is
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
ok = true;
@@ -448,7 +507,7 @@ public class PgpKeyOperation {
if (!ok) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
}
@@ -460,21 +519,25 @@ public class PgpKeyOperation {
}
// 4a. For each subkey change, generate new subkey binding certificate
- for (SaveKeyringParcel.SubkeyChange change : saveParcel.mChangeSubKeys) {
+ subProgressPush(50, 60);
+ for (int i = 0; i < saveParcel.mChangeSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeychange, (i-1) * (100 / saveParcel.mChangeSubKeys.size()));
+ SaveKeyringParcel.SubkeyChange change = saveParcel.mChangeSubKeys.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
// TODO allow changes in master key? this implies generating new user id certs...
if (change.mKeyId == masterPublicKey.getKeyID()) {
Log.e(Constants.TAG, "changing the master key not supported");
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey = sKey.getPublicKey();
@@ -482,7 +545,7 @@ public class PgpKeyOperation {
if (change.mExpiry != null && new Date(change.mExpiry*1000).before(new Date())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// keep old flags, or replace with new ones
@@ -514,16 +577,22 @@ public class PgpKeyOperation {
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
+ subProgressPop();
// 4b. For each subkey revocation, generate new subkey revocation certificate
- for (long revocation : saveParcel.mRevokeSubKeys) {
+ subProgressPush(60, 70);
+ for (int i = 0; i < saveParcel.mRevokeSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeyrevoke, (i-1) * (100 / saveParcel.mRevokeSubKeys.size()));
+ long revocation = saveParcel.mRevokeSubKeys.get(i);
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_REVOKE,
indent, PgpKeyHelper.convertKeyIdToHex(revocation));
+
PGPSecretKey sKey = sKR.getSecretKey(revocation);
if (sKey == null) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
indent+1, PgpKeyHelper.convertKeyIdToHex(revocation));
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
PGPPublicKey pKey = sKey.getPublicKey();
@@ -533,21 +602,30 @@ public class PgpKeyOperation {
pKey = PGPPublicKey.addCertification(pKey, sig);
sKR = PGPSecretKeyRing.insertSecretKey(sKR, PGPSecretKey.replacePublicKey(sKey, pKey));
}
+ subProgressPop();
// 5. Generate and add new subkeys
- for (SaveKeyringParcel.SubkeyAdd add : saveParcel.mAddSubKeys) {
+ subProgressPush(70, 90);
+ for (int i = 0; i < saveParcel.mAddSubKeys.size(); i++) {
+
+ progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
+ SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(0);
+ log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) {
log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
-
// generate a new secret key (privkey only for now)
+ subProgressPush(
+ (i-1) * (100 / saveParcel.mAddSubKeys.size()),
+ i * (100 / saveParcel.mAddSubKeys.size())
+ );
PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ subProgressPop();
if(keyPair == null) {
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
// add subkey binding signature (making this a sub rather than master key)
@@ -577,9 +655,11 @@ public class PgpKeyOperation {
sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
}
+ subProgressPop();
// 6. If requested, change passphrase
if (saveParcel.mNewPassphrase != null) {
+ progress(R.string.progress_modify_passphrase, 90);
log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent);
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(HashAlgorithmTags.SHA1);
@@ -597,18 +677,19 @@ public class PgpKeyOperation {
// This one must only be thrown by
} catch (IOException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (PGPException e) {
Log.e(Constants.TAG, "encountered pgp error while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (SignatureException e) {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1);
- return null;
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ progress(R.string.progress_done, 100);
log.add(LogLevel.OK, LogType.MSG_MF_SUCCESS, indent);
- return new UncachedKeyRing(sKR);
+ return new EditKeyResult(OperationResultParcel.RESULT_OK, log, new UncachedKeyRing(sKR));
}
@@ -735,7 +816,7 @@ public class PgpKeyOperation {
int flags = 0;
//noinspection unchecked
for(PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
- if (!sig.hasSubpackets()) {
+ if (sig.getHashedSubPackets() == null) {
continue;
}
flags |= sig.getHashedSubPackets().getKeyFlags();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 434b2bf90..09c28e7c5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -278,11 +278,11 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
- WrappedSecretKey signingKey = null;
+ CanonicalizedSecretKey signingKey = null;
if (enableSignature) {
- WrappedSecretKeyRing signingKeyRing;
+ CanonicalizedSecretKeyRing signingKeyRing;
try {
- signingKeyRing = mProviderHelper.getWrappedSecretKeyRing(mSignatureMasterKeyId);
+ signingKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
@@ -337,9 +337,9 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
- WrappedPublicKeyRing keyRing = mProviderHelper.getWrappedPublicKeyRing(
+ CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
- WrappedPublicKey key = keyRing.getEncryptionSubKey();
+ CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
cPk.addMethod(key.getPubKeyEncryptionGenerator());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 308375a1d..d29f19d67 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -1,7 +1,6 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.S2K;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyFlags;
@@ -14,28 +13,25 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPUtil;
+import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
+import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
import java.util.Set;
import java.util.TreeSet;
-import java.util.Vector;
/** Wrapper around PGPKeyRing class, to be constructed from bytes.
*
@@ -49,7 +45,7 @@ import java.util.Vector;
* treated equally for most purposes in UI code. It is up to the programmer to
* take care of the differences.
*
- * @see org.sufficientlysecure.keychain.pgp.WrappedKeyRing
+ * @see CanonicalizedKeyRing
* @see org.sufficientlysecure.keychain.pgp.UncachedPublicKey
* @see org.sufficientlysecure.keychain.pgp.UncachedSecretKey
*
@@ -59,18 +55,10 @@ public class UncachedKeyRing {
final PGPKeyRing mRing;
final boolean mIsSecret;
- final boolean mIsCanonicalized;
UncachedKeyRing(PGPKeyRing ring) {
mRing = ring;
mIsSecret = ring instanceof PGPSecretKeyRing;
- mIsCanonicalized = false;
- }
-
- private UncachedKeyRing(PGPKeyRing ring, boolean canonicalized) {
- mRing = ring;
- mIsSecret = ring instanceof PGPSecretKeyRing;
- mIsCanonicalized = canonicalized;
}
public long getMasterKeyId() {
@@ -89,7 +77,7 @@ public class UncachedKeyRing {
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
return new Iterator<UncachedPublicKey>() {
public void remove() {
- it.remove();
+ throw new UnsupportedOperationException();
}
public UncachedPublicKey next() {
return new UncachedPublicKey(it.next());
@@ -105,10 +93,6 @@ public class UncachedKeyRing {
return mIsSecret;
}
- public boolean isCanonicalized() {
- return mIsCanonicalized;
- }
-
public byte[] getEncoded() throws IOException {
return mRing.getEncoded();
}
@@ -119,43 +103,86 @@ public class UncachedKeyRing {
public static UncachedKeyRing decodeFromData(byte[] data)
throws PgpGeneralException, IOException {
- BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(data));
- if (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // get first object in block
- Object obj;
- if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
- return new UncachedKeyRing((PGPKeyRing) obj);
- } else {
- throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
- }
- } else {
+
+ Iterator<UncachedKeyRing> parsed = fromStream(new ByteArrayInputStream(data));
+
+ if ( ! parsed.hasNext()) {
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
+
+ UncachedKeyRing ring = parsed.next();
+
+ if (parsed.hasNext()) {
+ throw new PgpGeneralException("Expected single keyring in stream, found at least two");
+ }
+
+ return ring;
+
}
- public static List<UncachedKeyRing> fromStream(InputStream stream)
- throws PgpGeneralException, IOException {
+ public static Iterator<UncachedKeyRing> fromStream(final InputStream stream) throws IOException {
- PGPObjectFactory objectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+ return new Iterator<UncachedKeyRing>() {
- List<UncachedKeyRing> result = new Vector<UncachedKeyRing>();
+ UncachedKeyRing mNext = null;
+ PGPObjectFactory mObjectFactory = null;
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ private void cacheNext() {
+ if (mNext != null) {
+ return;
+ }
+
+ try {
+ while(stream.available() > 0) {
+ // if there are no objects left from the last factory, create a new one
+ if (mObjectFactory == null) {
+ mObjectFactory = new PGPObjectFactory(PGPUtil.getDecoderStream(stream));
+ }
+
+ // go through all objects in this block
+ Object obj;
+ while ((obj = mObjectFactory.nextObject()) != null) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+ if (!(obj instanceof PGPKeyRing)) {
+ Log.i(Constants.TAG,
+ "Skipping object of bad type " + obj.getClass().getName() + " in stream");
+ // skip object
+ continue;
+ }
+ mNext = new UncachedKeyRing((PGPKeyRing) obj);
+ return;
+ }
+ // if we are past the while loop, that means the objectFactory had no next
+ mObjectFactory = null;
+ }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException while processing stream", e);
+ }
- if (obj instanceof PGPKeyRing) {
- result.add(new UncachedKeyRing((PGPKeyRing) obj));
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
}
- }
- return result;
+
+ @Override
+ public boolean hasNext() {
+ cacheNext();
+ return mNext != null;
+ }
+
+ @Override
+ public UncachedKeyRing next() {
+ try {
+ cacheNext();
+ return mNext;
+ } finally {
+ mNext = null;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+
}
public void encodeArmored(OutputStream out, String version) throws IOException {
@@ -165,27 +192,6 @@ public class UncachedKeyRing {
aos.close();
}
- public HashSet<Long> getAvailableSubkeys() {
- if(!isSecret()) {
- throw new RuntimeException("Tried to find available subkeys from non-secret keys. " +
- "This is a programming error and should never happen!");
- }
-
- HashSet<Long> result = new HashSet<Long>();
- // then, mark exactly the keys we have available
- for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
- ((PGPSecretKeyRing) mRing).getSecretKeys())) {
- S2K s2k = sub.getS2K();
- // add key, except if the private key has been stripped (GNU extension)
- if(s2k == null || (s2k.getProtectionMode() != S2K.GNU_PROTECTION_MODE_NO_PRIVATE_KEY)) {
- result.add(sub.getKeyID());
- } else {
- Log.d(Constants.TAG, "S2K GNU extension!, mode: " + s2k.getProtectionMode());
- }
- }
- return result;
- }
-
/** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be
* applied to public keyrings only.
*
@@ -195,7 +201,7 @@ public class UncachedKeyRing {
* - Remove all certificates flagged as "local"
* - Remove all certificates which are superseded by a newer one on the same target,
* including revocations with later re-certifications.
- * - Remove all certificates of unknown type:
+ * - Remove all certificates in other positions if not of known type:
* - key revocation signatures on the master key
* - subkey binding signatures for subkeys
* - certifications and certification revocations for user ids
@@ -210,7 +216,7 @@ public class UncachedKeyRing {
*
*/
@SuppressWarnings("ConstantConditions")
- public UncachedKeyRing canonicalize(OperationLog log, int indent) {
+ public CanonicalizedKeyRing canonicalize(OperationLog log, int indent) {
log.add(LogLevel.START, isSecret() ? LogType.MSG_KC_SECRET : LogType.MSG_KC_PUBLIC,
indent, PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()));
@@ -292,14 +298,14 @@ public class UncachedKeyRing {
revocation = zert;
// more revocations? at least one is superfluous, then.
} else if (revocation.getCreationTime().before(zert.getCreationTime())) {
+ log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, indent);
modified = PGPPublicKey.removeCertification(modified, revocation);
redundantCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, indent);
revocation = zert;
} else {
+ log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
redundantCerts += 1;
- log.add(LogLevel.INFO, LogType.MSG_KC_REVOKE_DUP, indent);
}
}
@@ -323,20 +329,21 @@ public class UncachedKeyRing {
indent, "0x" + Integer.toString(zert.getSignatureType(), 16));
modified = PGPPublicKey.removeCertification(modified, userId, zert);
badCerts += 1;
+ continue;
}
if (cert.getCreationTime().after(now)) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TIME, indent);
- modified = PGPPublicKey.removeCertification(modified, zert);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_TIME, indent);
+ modified = PGPPublicKey.removeCertification(modified, userId, zert);
badCerts += 1;
continue;
}
if (cert.isLocal()) {
// Creation date in the future? No way!
- log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_LOCAL, indent);
- modified = PGPPublicKey.removeCertification(modified, zert);
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_BAD_LOCAL, indent);
+ modified = PGPPublicKey.removeCertification(modified, userId, zert);
badCerts += 1;
continue;
}
@@ -379,35 +386,35 @@ public class UncachedKeyRing {
if (selfCert == null) {
selfCert = zert;
} else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
- modified = PGPPublicKey.removeCertification(modified, userId, selfCert);
- redundantCerts += 1;
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
indent, userId);
+ modified = PGPPublicKey.removeCertification(modified, userId, selfCert);
+ redundantCerts += 1;
selfCert = zert;
} else {
- modified = PGPPublicKey.removeCertification(modified, userId, zert);
- redundantCerts += 1;
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
indent, userId);
+ modified = PGPPublicKey.removeCertification(modified, userId, zert);
+ redundantCerts += 1;
}
// If there is a revocation certificate, and it's older than this, drop it
if (revocation != null
&& revocation.getCreationTime().before(selfCert.getCreationTime())) {
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD,
+ indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, revocation);
revocation = null;
redundantCerts += 1;
- log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD,
- indent, userId);
}
break;
case PGPSignature.CERTIFICATION_REVOCATION:
// If this is older than the (latest) self cert, drop it
if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) {
- modified = PGPPublicKey.removeCertification(modified, userId, zert);
- redundantCerts += 1;
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_OLD,
indent, userId);
+ modified = PGPPublicKey.removeCertification(modified, userId, zert);
+ redundantCerts += 1;
continue;
}
// first revocation? remember it.
@@ -415,16 +422,16 @@ public class UncachedKeyRing {
revocation = zert;
// more revocations? at least one is superfluous, then.
} else if (revocation.getCreationTime().before(cert.getCreationTime())) {
- modified = PGPPublicKey.removeCertification(modified, userId, revocation);
- redundantCerts += 1;
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP,
indent, userId);
+ modified = PGPPublicKey.removeCertification(modified, userId, revocation);
+ redundantCerts += 1;
revocation = zert;
} else {
- modified = PGPPublicKey.removeCertification(modified, userId, zert);
- redundantCerts += 1;
log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_REVOKE_DUP,
indent, userId);
+ modified = PGPPublicKey.removeCertification(modified, userId, zert);
+ redundantCerts += 1;
}
break;
@@ -434,9 +441,9 @@ public class UncachedKeyRing {
// If no valid certificate (if only a revocation) remains, drop it
if (selfCert == null && revocation == null) {
- modified = PGPPublicKey.removeCertification(modified, userId);
log.add(LogLevel.ERROR, LogType.MSG_KC_UID_REMOVE,
indent, userId);
+ modified = PGPPublicKey.removeCertification(modified, userId);
}
}
@@ -515,14 +522,17 @@ public class UncachedKeyRing {
continue;
}
- if (zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) {
+ // if this certificate says it allows signing for the key
+ if (zert.getHashedSubPackets() != null &&
+ zert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.KEY_FLAGS)) {
+
int flags = ((KeyFlags) zert.getHashedSubPackets()
.getSubpacket(SignatureSubpacketTags.KEY_FLAGS)).getFlags();
- // If this subkey is allowed to sign data,
if ((flags & PGPKeyFlags.CAN_SIGN) == PGPKeyFlags.CAN_SIGN) {
+ boolean ok = false;
+ // it MUST have an embedded primary key binding signature
try {
PGPSignatureList list = zert.getUnhashedSubPackets().getEmbeddedSignatures();
- boolean ok = false;
for (int i = 0; i < list.size(); i++) {
WrappedSignature subsig = new WrappedSignature(list.get(i));
if (subsig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
@@ -536,17 +546,19 @@ public class UncachedKeyRing {
}
}
}
- if (!ok) {
- log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
- badCerts += 1;
- continue;
- }
} catch (Exception e) {
log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_BAD_ERR, indent);
badCerts += 1;
continue;
}
+ // if it doesn't, get rid of this!
+ if (!ok) {
+ log.add(LogLevel.WARN, LogType.MSG_KC_SUB_PRIMARY_NONE, indent);
+ badCerts += 1;
+ continue;
+ }
}
+
}
// if we already have a cert, and this one is not newer: skip it
@@ -559,6 +571,8 @@ public class UncachedKeyRing {
selfCert = zert;
// if this is newer than a possibly existing revocation, drop that one
if (revocation != null && selfCert.getCreationTime().after(revocation.getCreationTime())) {
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_REVOKE_DUP, indent);
+ redundantCerts += 1;
revocation = null;
}
@@ -592,7 +606,7 @@ public class UncachedKeyRing {
// it is not properly bound? error!
if (selfCert == null) {
- ring = replacePublicKey(ring, modified);
+ ring = removeSubKey(ring, key);
log.add(LogLevel.ERROR, LogType.MSG_KC_SUB_NO_CERT,
indent, PgpKeyHelper.convertKeyIdToHex(key.getKeyID()));
@@ -625,7 +639,8 @@ public class UncachedKeyRing {
log.add(LogLevel.OK, LogType.MSG_KC_SUCCESS, indent);
}
- return new UncachedKeyRing(ring, true);
+ return isSecret() ? new CanonicalizedSecretKeyRing((PGPSecretKeyRing) ring, 1)
+ : new CanonicalizedPublicKeyRing((PGPPublicKeyRing) ring, 0);
}
/** This operation merges information from a different keyring, returning a combined
@@ -660,7 +675,7 @@ public class UncachedKeyRing {
return left.length - right.length;
}
// compare byte-by-byte
- for (int i = 0; i < left.length && i < right.length; i++) {
+ for (int i = 0; i < left.length; i++) {
if (left[i] != right[i]) {
return (left[i] & 0xff) - (right[i] & 0xff);
}
@@ -688,7 +703,14 @@ public class UncachedKeyRing {
final PGPPublicKey resultKey = result.getPublicKey(key.getKeyID());
if (resultKey == null) {
log.add(LogLevel.DEBUG, LogType.MSG_MG_NEW_SUBKEY, indent);
- result = replacePublicKey(result, key);
+ // special case: if both rings are secret, copy over the secret key
+ if (isSecret() && other.isSecret()) {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) candidate).getSecretKey(key.getKeyID());
+ result = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing) result, sKey);
+ } else {
+ // otherwise, just insert the public key
+ result = replacePublicKey(result, key);
+ }
continue;
}
@@ -696,17 +718,7 @@ public class UncachedKeyRing {
PGPPublicKey modified = resultKey;
// Iterate certifications
- for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getSignatures())) {
- int type = cert.getSignatureType();
- // Disregard certifications on user ids, we will deal with those later
- if (type == PGPSignature.NO_CERTIFICATION
- || type == PGPSignature.DEFAULT_CERTIFICATION
- || type == PGPSignature.CASUAL_CERTIFICATION
- || type == PGPSignature.POSITIVE_CERTIFICATION
- || type == PGPSignature.CERTIFICATION_REVOCATION) {
- continue;
- }
-
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(key.getKeySignatures())) {
// Don't merge foreign stuff into secret keys
if (cert.getKeyID() != masterKeyId && isSecret()) {
continue;
@@ -770,19 +782,20 @@ public class UncachedKeyRing {
}
- public UncachedKeyRing extractPublicKeyRing() {
+ public UncachedKeyRing extractPublicKeyRing() throws IOException {
if(!isSecret()) {
throw new RuntimeException("Tried to extract public keyring from non-secret keyring. " +
"This is a programming error and should never happen!");
}
- ArrayList<PGPPublicKey> keys = new ArrayList();
Iterator<PGPPublicKey> it = mRing.getPublicKeys();
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(2048);
while (it.hasNext()) {
- keys.add(it.next());
+ stream.write(it.next().getEncoded());
}
- return new UncachedKeyRing(new PGPPublicKeyRing(keys));
+ return new UncachedKeyRing(
+ new PGPPublicKeyRing(stream.toByteArray(), new JcaKeyFingerprintCalculator()));
}
/** This method replaces a public key in a keyring.
@@ -806,4 +819,20 @@ public class UncachedKeyRing {
return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
}
+ /** This method removes a subkey in a keyring.
+ *
+ * This method essentially wraps PGP*KeyRing.remove*Key, where the keyring may be of either
+ * the secret or public subclass.
+ *
+ * @return the resulting PGPKeyRing of the same type as the input
+ */
+ private static PGPKeyRing removeSubKey(PGPKeyRing ring, PGPPublicKey key) {
+ if (ring instanceof PGPPublicKeyRing) {
+ return PGPPublicKeyRing.removePublicKey((PGPPublicKeyRing) ring, key);
+ } else {
+ PGPSecretKey sKey = ((PGPSecretKeyRing) ring).getSecretKey(key.getKeyID());
+ return PGPSecretKeyRing.removeSecretKey((PGPSecretKeyRing) ring, sKey);
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
index df19930c4..07fb4fb9e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -63,13 +63,17 @@ public class WrappedSignature {
}
try {
PGPSignatureList list;
- list = mSig.getHashedSubPackets().getEmbeddedSignatures();
- for(int i = 0; i < list.size(); i++) {
- sigs.add(new WrappedSignature(list.get(i)));
+ if (mSig.getHashedSubPackets() != null) {
+ list = mSig.getHashedSubPackets().getEmbeddedSignatures();
+ for (int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
}
- list = mSig.getUnhashedSubPackets().getEmbeddedSignatures();
- for(int i = 0; i < list.size(); i++) {
- sigs.add(new WrappedSignature(list.get(i)));
+ if (mSig.getUnhashedSubPackets() != null) {
+ list = mSig.getUnhashedSubPackets().getEmbeddedSignatures();
+ for (int i = 0; i < list.size(); i++) {
+ sigs.add(new WrappedSignature(list.get(i)));
+ }
}
} catch (PGPException e) {
// no matter
@@ -97,6 +101,9 @@ public class WrappedSignature {
if(!isRevocation()) {
throw new PgpGeneralException("Not a revocation signature.");
}
+ if (mSig.getHashedSubPackets() == null) {
+ return null;
+ }
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
SignatureSubpacketTags.REVOCATION_REASON);
// For some reason, this is missing in SignatureSubpacketInputStream:146
@@ -106,7 +113,7 @@ public class WrappedSignature {
return ((RevocationReason) p).getRevocationDescription();
}
- public void init(WrappedPublicKey key) throws PgpGeneralException {
+ public void init(CanonicalizedPublicKey key) throws PgpGeneralException {
init(key.getPublicKey());
}
@@ -184,7 +191,7 @@ public class WrappedSignature {
public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
- public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
+ public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException {
return verifySignature(key.getPublicKey(), uid);
}
@@ -205,7 +212,7 @@ public class WrappedSignature {
}
public boolean isLocal() {
- if (!mSig.hasSubpackets()
+ if (mSig.getHashedSubPackets() == null
|| !mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPORTABLE)) {
return false;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
index 34de0024d..aa0207a6a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -1,5 +1,6 @@
package org.sufficientlysecure.keychain.provider;
+import android.database.Cursor;
import android.net.Uri;
import org.sufficientlysecure.keychain.Constants;
@@ -33,6 +34,7 @@ public class CachedPublicKeyRing extends KeyRing {
mUri = uri;
}
+ @Override
public long getMasterKeyId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
@@ -59,10 +61,21 @@ public class CachedPublicKeyRing extends KeyRing {
return getMasterKeyId();
}
+ public byte[] getFingerprint() throws PgpGeneralException {
+ try {
+ Object data = mProviderHelper.getGenericData(mUri,
+ KeychainContract.KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ return (byte[]) data;
+ } catch (ProviderHelper.NotFoundException e) {
+ throw new PgpGeneralException(e);
+ }
+ }
+
+ @Override
public String getPrimaryUserId() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.USER_ID,
ProviderHelper.FIELD_TYPE_STRING);
return (String) data;
} catch(ProviderHelper.NotFoundException e) {
@@ -74,10 +87,11 @@ public class CachedPublicKeyRing extends KeyRing {
return getPrimaryUserId();
}
+ @Override
public boolean isRevoked() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.IS_REVOKED,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
@@ -85,10 +99,11 @@ public class CachedPublicKeyRing extends KeyRing {
}
}
+ @Override
public boolean canCertify() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.CAN_CERTIFY,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
@@ -96,21 +111,32 @@ public class CachedPublicKeyRing extends KeyRing {
}
}
+ @Override
public long getEncryptId() throws PgpGeneralException {
try {
- Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
- ProviderHelper.FIELD_TYPE_INTEGER);
- return (Long) data;
- } catch(ProviderHelper.NotFoundException e) {
+ Cursor subkeys = getSubkeys();
+ if (subkeys != null) {
+ try {
+ while (subkeys.moveToNext()) {
+ if (subkeys.getInt(subkeys.getColumnIndexOrThrow(KeychainContract.Keys.CAN_ENCRYPT)) != 0) {
+ return subkeys.getLong(subkeys.getColumnIndexOrThrow(KeychainContract.Keys.KEY_ID));
+ }
+ }
+ } finally {
+ subkeys.close();
+ }
+ }
+ } catch(Exception e) {
throw new PgpGeneralException(e);
}
+ throw new PgpGeneralException("No encrypt key found");
}
+ @Override
public boolean hasEncrypt() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.HAS_ENCRYPT,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
@@ -118,21 +144,32 @@ public class CachedPublicKeyRing extends KeyRing {
}
}
+ @Override
public long getSignId() throws PgpGeneralException {
try {
- Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
- ProviderHelper.FIELD_TYPE_INTEGER);
- return (Long) data;
- } catch(ProviderHelper.NotFoundException e) {
+ Cursor subkeys = getSubkeys();
+ if (subkeys != null) {
+ try {
+ while (subkeys.moveToNext()) {
+ if (subkeys.getInt(subkeys.getColumnIndexOrThrow(KeychainContract.Keys.CAN_SIGN)) != 0) {
+ return subkeys.getLong(subkeys.getColumnIndexOrThrow(KeychainContract.Keys.KEY_ID));
+ }
+ }
+ } finally {
+ subkeys.close();
+ }
+ }
+ } catch(Exception e) {
throw new PgpGeneralException(e);
}
+ throw new PgpGeneralException("No sign key found");
}
+ @Override
public boolean hasSign() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.HAS_SIGN,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
@@ -140,10 +177,11 @@ public class CachedPublicKeyRing extends KeyRing {
}
}
+ @Override
public int getVerified() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.VERIFIED,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Integer) data;
} catch(ProviderHelper.NotFoundException e) {
@@ -154,12 +192,16 @@ public class CachedPublicKeyRing extends KeyRing {
public boolean hasAnySecret() throws PgpGeneralException {
try {
Object data = mProviderHelper.getGenericData(mUri,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.HAS_ANY_SECRET,
ProviderHelper.FIELD_TYPE_INTEGER);
return (Long) data > 0;
} catch(ProviderHelper.NotFoundException e) {
throw new PgpGeneralException(e);
}
+ }
+ private Cursor getSubkeys() throws PgpGeneralException {
+ Uri keysUri = KeychainContract.Keys.buildKeysUri(Long.toString(extractOrGetMasterKeyId()));
+ return mProviderHelper.getContentResolver().query(keysUri, null, null, null, null);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 2d524f5b0..aa85577e0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -28,10 +28,12 @@ import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.NullProgressable;
import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
@@ -39,8 +41,6 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
@@ -180,7 +180,7 @@ public class ProviderHelper {
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
}
- private LongSparseArray<WrappedPublicKey> getTrustedMasterKeys() {
+ private LongSparseArray<CanonicalizedPublicKey> getTrustedMasterKeys() {
Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] {
KeyRings.MASTER_KEY_ID,
// we pick from cache only information that is not easily available from keyrings
@@ -190,16 +190,15 @@ public class ProviderHelper {
}, KeyRings.HAS_ANY_SECRET + " = 1", null, null);
try {
- LongSparseArray<WrappedPublicKey> result = new LongSparseArray<WrappedPublicKey>();
+ LongSparseArray<CanonicalizedPublicKey> result = new LongSparseArray<CanonicalizedPublicKey>();
if (cursor != null && cursor.moveToFirst()) do {
long masterKeyId = cursor.getLong(0);
- boolean hasAnySecret = cursor.getInt(1) > 0;
int verified = cursor.getInt(2);
byte[] blob = cursor.getBlob(3);
if (blob != null) {
result.put(masterKeyId,
- new WrappedPublicKeyRing(blob, hasAnySecret, verified).getPublicKey());
+ new CanonicalizedPublicKeyRing(blob, verified).getPublicKey());
}
} while (cursor.moveToNext());
@@ -217,23 +216,23 @@ public class ProviderHelper {
return new CachedPublicKeyRing(this, queryUri);
}
- public WrappedPublicKeyRing getWrappedPublicKeyRing(long id) throws NotFoundException {
- return (WrappedPublicKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
+ public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(long id) throws NotFoundException {
+ return (CanonicalizedPublicKeyRing) getCanonicalizedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), false);
}
- public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri queryUri) throws NotFoundException {
- return (WrappedPublicKeyRing) getWrappedKeyRing(queryUri, false);
+ public CanonicalizedPublicKeyRing getCanonicalizedPublicKeyRing(Uri queryUri) throws NotFoundException {
+ return (CanonicalizedPublicKeyRing) getCanonicalizedKeyRing(queryUri, false);
}
- public WrappedSecretKeyRing getWrappedSecretKeyRing(long id) throws NotFoundException {
- return (WrappedSecretKeyRing) getWrappedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
+ public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(long id) throws NotFoundException {
+ return (CanonicalizedSecretKeyRing) getCanonicalizedKeyRing(KeyRings.buildUnifiedKeyRingUri(id), true);
}
- public WrappedSecretKeyRing getWrappedSecretKeyRing(Uri queryUri) throws NotFoundException {
- return (WrappedSecretKeyRing) getWrappedKeyRing(queryUri, true);
+ public CanonicalizedSecretKeyRing getCanonicalizedSecretKeyRing(Uri queryUri) throws NotFoundException {
+ return (CanonicalizedSecretKeyRing) getCanonicalizedKeyRing(queryUri, true);
}
- private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
+ private KeyRing getCanonicalizedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{
// we pick from cache only information that is not easily available from keyrings
@@ -252,8 +251,8 @@ public class ProviderHelper {
throw new NotFoundException("Secret key not available!");
}
return secret
- ? new WrappedSecretKeyRing(blob, true, verified)
- : new WrappedPublicKeyRing(blob, hasAnySecret, verified);
+ ? new CanonicalizedSecretKeyRing(blob, true, verified)
+ : new CanonicalizedPublicKeyRing(blob, verified);
} else {
throw new NotFoundException("Key not found!");
}
@@ -271,16 +270,8 @@ public class ProviderHelper {
* and need to be saved externally to be preserved past the operation.
*/
@SuppressWarnings("unchecked")
- private int internalSavePublicKeyRing(UncachedKeyRing keyRing,
- Progressable progress, boolean selfCertsAreTrusted) {
- if (keyRing.isSecret()) {
- log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET);
- return SaveKeyringResult.RESULT_ERROR;
- }
- if (!keyRing.isCanonicalized()) {
- log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET);
- return SaveKeyringResult.RESULT_ERROR;
- }
+ private int saveCanonicalizedPublicKeyRing(CanonicalizedPublicKeyRing keyRing,
+ Progressable progress, boolean selfCertsAreTrusted) {
// start with ok result
int result = SaveKeyringResult.SAVED_PUBLIC;
@@ -318,7 +309,7 @@ public class ProviderHelper {
{ // insert subkeys
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
int rank = 0;
- for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
+ for (CanonicalizedPublicKey key : keyRing.publicKeyIterator()) {
long keyId = key.getKeyId();
log(LogLevel.DEBUG, keyId == masterKeyId ? LogType.MSG_IP_MASTER : LogType.MSG_IP_SUBKEY,
PgpKeyHelper.convertKeyIdToHex(keyId)
@@ -401,7 +392,7 @@ public class ProviderHelper {
mIndent -= 1;
// get a list of owned secret keys, for verification filtering
- LongSparseArray<WrappedPublicKey> trustedKeys = getTrustedMasterKeys();
+ LongSparseArray<CanonicalizedPublicKey> trustedKeys = getTrustedMasterKeys();
// classify and order user ids. primary are moved to the front, revoked to the back,
// otherwise the order in the keyfile is preserved.
@@ -445,7 +436,7 @@ public class ProviderHelper {
// verify signatures from known private keys
if (trustedKeys.indexOfKey(certId) >= 0) {
- WrappedPublicKey trustedKey = trustedKeys.get(certId);
+ CanonicalizedPublicKey trustedKey = trustedKeys.get(certId);
cert.init(trustedKey);
if (cert.verifySignature(masterKey, userId)) {
item.trustedCerts.add(cert);
@@ -559,28 +550,13 @@ public class ProviderHelper {
/** Saves an UncachedKeyRing of the secret variant into the db.
* This method will fail if no corresponding public keyring is in the database!
*/
- private int internalSaveSecretKeyRing(UncachedKeyRing keyRing) {
-
- if (!keyRing.isSecret()) {
- log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC);
- return SaveKeyringResult.RESULT_ERROR;
- }
-
- if (!keyRing.isCanonicalized()) {
- log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC);
- return SaveKeyringResult.RESULT_ERROR;
- }
+ private int saveCanonicalizedSecretKeyRing(CanonicalizedSecretKeyRing keyRing) {
long masterKeyId = keyRing.getMasterKeyId();
log(LogLevel.START, LogType.MSG_IS, PgpKeyHelper.convertKeyIdToHex(masterKeyId));
mIndent += 1;
- try {
- // Canonicalize this key, to assert a number of assumptions made about it.
- keyRing = keyRing.canonicalize(mLog, mIndent);
- if (keyRing == null) {
- return SaveKeyringResult.RESULT_ERROR;
- }
+ try {
// IF this is successful, it's a secret key
int result = SaveKeyringResult.SAVED_SECRET;
@@ -615,8 +591,7 @@ public class ProviderHelper {
log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS);
mIndent += 1;
Set<Long> available = keyRing.getAvailableSubkeys();
- for (UncachedPublicKey sub :
- new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
+ for (UncachedPublicKey sub : keyRing.publicKeyIterator()) {
long id = sub.getKeyId();
if (available.contains(id)) {
int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?",
@@ -667,9 +642,16 @@ public class ProviderHelper {
log(LogLevel.START, LogType.MSG_IP, PgpKeyHelper.convertKeyIdToHex(masterKeyId));
mIndent += 1;
+ if (publicRing.isSecret()) {
+ log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ }
+
+ CanonicalizedPublicKeyRing canPublicRing;
+
// If there is an old keyring, merge it
try {
- UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncachedKeyRing();
+ UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing();
// Merge data from new public ring into the old one
publicRing = oldPublicRing.merge(publicRing, mLog, mIndent);
@@ -680,8 +662,8 @@ public class ProviderHelper {
}
// Canonicalize this keyring, to assert a number of assumptions made about it.
- publicRing = publicRing.canonicalize(mLog, mIndent);
- if (publicRing == null) {
+ canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
+ if (canPublicRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
@@ -695,39 +677,40 @@ public class ProviderHelper {
// Not an issue, just means we are dealing with a new keyring.
// Canonicalize this keyring, to assert a number of assumptions made about it.
- publicRing = publicRing.canonicalize(mLog, mIndent);
- if (publicRing == null) {
+ canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
+ if (canPublicRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
}
// If there is a secret key, merge new data (if any) and save the key for later
- UncachedKeyRing secretRing;
+ CanonicalizedSecretKeyRing canSecretRing;
try {
- secretRing = getWrappedSecretKeyRing(publicRing.getMasterKeyId()).getUncachedKeyRing();
+ UncachedKeyRing secretRing = getCanonicalizedSecretKeyRing(publicRing.getMasterKeyId()).getUncachedKeyRing();
// Merge data from new public ring into secret one
secretRing = secretRing.merge(publicRing, mLog, mIndent);
if (secretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
- secretRing = secretRing.canonicalize(mLog, mIndent);
- if (secretRing == null) {
+ // This has always been a secret key ring, this is a safe cast
+ canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
+ if (canSecretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
} catch (NotFoundException e) {
// No secret key available (this is what happens most of the time)
- secretRing = null;
+ canSecretRing = null;
}
- int result = internalSavePublicKeyRing(publicRing, progress, secretRing != null);
+ int result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, canSecretRing != null);
// Save the saved keyring (if any)
- if (secretRing != null) {
+ if (canSecretRing != null) {
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
- int secretResult = internalSaveSecretKeyRing(secretRing);
+ int secretResult = saveCanonicalizedSecretKeyRing(canSecretRing);
if ((secretResult & SaveKeyringResult.RESULT_ERROR) != SaveKeyringResult.RESULT_ERROR) {
result |= SaveKeyringResult.SAVED_SECRET;
}
@@ -751,9 +734,16 @@ public class ProviderHelper {
log(LogLevel.START, LogType.MSG_IS, PgpKeyHelper.convertKeyIdToHex(masterKeyId));
mIndent += 1;
+ if ( ! secretRing.isSecret()) {
+ log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ }
+
+ CanonicalizedSecretKeyRing canSecretRing;
+
// If there is an old secret key, merge it.
try {
- UncachedKeyRing oldSecretRing = getWrappedSecretKeyRing(masterKeyId).getUncachedKeyRing();
+ UncachedKeyRing oldSecretRing = getCanonicalizedSecretKeyRing(masterKeyId).getUncachedKeyRing();
// Merge data from new secret ring into old one
secretRing = secretRing.merge(oldSecretRing, mLog, mIndent);
@@ -764,8 +754,9 @@ public class ProviderHelper {
}
// Canonicalize this keyring, to assert a number of assumptions made about it.
- secretRing = secretRing.canonicalize(mLog, mIndent);
- if (secretRing == null) {
+ // This is a safe cast, because we made sure this is a secret ring above
+ canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
+ if (canSecretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
@@ -780,8 +771,9 @@ public class ProviderHelper {
// Not an issue, just means we are dealing with a new keyring
// Canonicalize this keyring, to assert a number of assumptions made about it.
- secretRing = secretRing.canonicalize(mLog, mIndent);
- if (secretRing == null) {
+ // This is a safe cast, because we made sure this is a secret ring above
+ canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
+ if (canSecretRing == null) {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
@@ -790,7 +782,7 @@ public class ProviderHelper {
// Merge new data into public keyring as well, if there is any
UncachedKeyRing publicRing;
try {
- UncachedKeyRing oldPublicRing = getWrappedPublicKeyRing(masterKeyId).getUncachedKeyRing();
+ UncachedKeyRing oldPublicRing = getCanonicalizedPublicKeyRing(masterKeyId).getUncachedKeyRing();
// Merge data from new secret ring into public one
publicRing = oldPublicRing.merge(secretRing, mLog, mIndent);
@@ -798,31 +790,26 @@ public class ProviderHelper {
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
- // If nothing changed, never mind
- if (Arrays.hashCode(publicRing.getEncoded())
- == Arrays.hashCode(oldPublicRing.getEncoded())) {
- publicRing = null;
- }
-
} catch (NotFoundException e) {
log(LogLevel.DEBUG, LogType.MSG_IS_PUBRING_GENERATE);
publicRing = secretRing.extractPublicKeyRing();
}
- if (publicRing != null) {
- publicRing = publicRing.canonicalize(mLog, mIndent);
- if (publicRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
- }
+ CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
+ if (canPublicRing == null) {
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ }
- int result = internalSavePublicKeyRing(publicRing, progress, true);
- if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
- }
+ int result;
+
+ result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, true);
+ if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) {
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
}
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
- int result = internalSaveSecretKeyRing(secretRing);
+ result = saveCanonicalizedSecretKeyRing(canSecretRing);
+
return new SaveKeyringResult(result, mLog);
} catch (IOException e) {
@@ -1037,4 +1024,8 @@ public class ProviderHelper {
}
}
}
+
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 64f5e1050..eae217e16 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -457,7 +457,7 @@ public class OpenPgpService extends RemoteService {
try {
// try to find key, throws NotFoundException if not in db!
- mProviderHelper.getWrappedPublicKeyRing(masterKeyId);
+ mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index 0b1d521ad..2ba792f9a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -32,10 +32,11 @@ import android.widget.Button;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
-import org.sufficientlysecure.keychain.ui.EditKeyActivityOld;
+import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
import org.sufficientlysecure.keychain.util.AlgorithmNames;
@@ -163,11 +164,11 @@ public class AccountSettingsFragment extends Fragment implements
}
private void createKey() {
- Intent intent = new Intent(getActivity(), EditKeyActivityOld.class);
- intent.setAction(EditKeyActivityOld.ACTION_CREATE_KEY);
- intent.putExtra(EditKeyActivityOld.EXTRA_GENERATE_DEFAULT_KEYS, true);
- // set default user id to account name
- intent.putExtra(EditKeyActivityOld.EXTRA_USER_IDS, mAccSettings.getAccountName());
+ String[] userId = KeyRing.splitUserId(mAccSettings.getAccountName());
+
+ Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
+ intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId[0]);
+ intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId[1]);
startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index 77b207bdc..426b86590 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -31,11 +31,15 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.keyimport.FileImportCache;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
+import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
@@ -44,15 +48,13 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -169,7 +171,7 @@ public class KeychainIntentService extends IntentService
// export
public static final String RESULT_EXPORT = "exported";
- public static final String RESULT = "result";
+ public static final String RESULT_IMPORT = "result";
Messenger mMessenger;
@@ -330,39 +332,37 @@ public class KeychainIntentService extends IntentService
/* Operation */
ProviderHelper providerHelper = new ProviderHelper(this);
- PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 50, 100));
- try {
- OperationLog log = new OperationLog();
- UncachedKeyRing ring;
- if (saveParcel.mMasterKeyId != null) {
- String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
- WrappedSecretKeyRing secRing =
- providerHelper.getWrappedSecretKeyRing(saveParcel.mMasterKeyId);
-
- ring = keyOperations.modifySecretKeyRing(secRing, saveParcel,
- passphrase, log, 0);
- } else {
- ring = keyOperations.createSecretKeyRing(saveParcel, log, 0);
- }
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100));
+ EditKeyResult result;
- providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 10, 95, 100));
+ if (saveParcel.mMasterKeyId != null) {
+ String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
+ CanonicalizedSecretKeyRing secRing =
+ providerHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
- // cache new passphrase
- if (saveParcel.mNewPassphrase != null) {
- PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(),
- saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback());
- }
- } catch (ProviderHelper.NotFoundException e) {
- sendErrorToHandler(e);
+ result = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
+ } else {
+ result = keyOperations.createSecretKeyRing(saveParcel);
+ }
+
+ UncachedKeyRing ring = result.getRing();
+
+ providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100));
+
+ // cache new passphrase
+ if (saveParcel.mNewPassphrase != null) {
+ PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(),
+ saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback());
}
setProgress(R.string.progress_done, 100, 100);
/* Output */
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
} catch (Exception e) {
sendErrorToHandler(e);
}
+
} else if (ACTION_DELETE_FILE_SECURELY.equals(action)) {
try {
/* Input */
@@ -386,13 +386,21 @@ public class KeychainIntentService extends IntentService
}
} else if (ACTION_IMPORT_KEYRING.equals(action)) {
try {
- List<ParcelableKeyRing> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
+ List<ParcelableKeyRing> entries;
+ if (data.containsKey(IMPORT_KEY_LIST)) {
+ // get entries from intent
+ entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
+ } else {
+ // get entries from cached file
+ FileImportCache cache = new FileImportCache(this);
+ entries = cache.readCache();
+ }
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
- OperationResults.ImportResult result = pgpImportExport.importKeyRings(entries);
+ ImportKeyResult result = pgpImportExport.importKeyRings(entries);
Bundle resultData = new Bundle();
- resultData.putParcelable(RESULT, result);
+ resultData.putParcelable(RESULT_IMPORT, result);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) {
@@ -466,7 +474,7 @@ public class KeychainIntentService extends IntentService
HkpKeyserver server = new HkpKeyserver(keyServer);
ProviderHelper providerHelper = new ProviderHelper(this);
- WrappedPublicKeyRing keyring = providerHelper.getWrappedPublicKeyRing(dataUri);
+ CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
@@ -515,6 +523,7 @@ public class KeychainIntentService extends IntentService
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
+ // This is not going through binder, nothing to fear of
importData.putParcelableArrayList(IMPORT_KEY_LIST, keyRings);
importIntent.putExtra(EXTRA_DATA, importData);
importIntent.putExtra(EXTRA_MESSENGER, mMessenger);
@@ -542,9 +551,9 @@ public class KeychainIntentService extends IntentService
}
ProviderHelper providerHelper = new ProviderHelper(this);
- WrappedPublicKeyRing publicRing = providerHelper.getWrappedPublicKeyRing(pubKeyId);
- WrappedSecretKeyRing secretKeyRing = providerHelper.getWrappedSecretKeyRing(masterKeyId);
- WrappedSecretKey certificationKey = secretKeyRing.getSecretKey();
+ CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId);
+ CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId);
+ CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey();
if(!certificationKey.unlock(signaturePassphrase)) {
throw new PgpGeneralException("Error extracting key (bad passphrase?)");
}
@@ -622,6 +631,12 @@ public class KeychainIntentService extends IntentService
}
}
+ private void sendMessageToHandler(Integer arg1, OperationResultParcel data) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(OperationResultParcel.EXTRA_RESULT, data);
+ sendMessageToHandler(arg1, null, bundle);
+ }
+
private void sendMessageToHandler(Integer arg1, Bundle data) {
sendMessageToHandler(arg1, null, data);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
index 755827482..0cdbe708e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java
@@ -25,12 +25,11 @@ import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
public class KeychainIntentServiceHandler extends Handler {
@@ -102,9 +101,9 @@ public class KeychainIntentServiceHandler extends Handler {
// show error from service
if (data.containsKey(DATA_ERROR)) {
- AppMsg.makeText(mActivity,
+ Notify.showNotify(mActivity,
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
- AppMsg.STYLE_ALERT).show();
+ Notify.Style.ERROR);
}
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
index 89d534df6..c27b3f6da 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
@@ -1,10 +1,20 @@
package org.sufficientlysecure.keychain.service;
+import android.app.Activity;
+import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.View;
+
+import com.github.johnpersano.supertoasts.SuperCardToast;
+import com.github.johnpersano.supertoasts.SuperToast;
+import com.github.johnpersano.supertoasts.util.OnClickWrapper;
+import com.github.johnpersano.supertoasts.util.Style;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
+import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -24,6 +34,9 @@ import java.util.List;
*
*/
public class OperationResultParcel implements Parcelable {
+
+ public static final String EXTRA_RESULT = "operation_result";
+
/** Holds the overall result, the number specifying varying degrees of success. The first bit
* is 0 on overall success, 1 on overall failure. All other bits may be used for more specific
* conditions. */
@@ -70,6 +83,7 @@ public class OperationResultParcel implements Parcelable {
mType = type;
mParameters = parameters;
mIndent = indent;
+ Log.v(Constants.TAG, "log: " + this.toString());
}
public LogEntryParcel(Parcel source) {
@@ -113,6 +127,68 @@ public class OperationResultParcel implements Parcelable {
}
}
+ public SuperCardToast createNotify(final Activity activity) {
+
+ int resultType = getResult();
+
+ String str;
+ int duration, color;
+
+ // Not an overall failure
+ if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) {
+
+ if (getLog().containsWarnings()) {
+ duration = 0;
+ color = Style.ORANGE;
+ } else {
+ duration = SuperToast.Duration.LONG;
+ color = Style.GREEN;
+ }
+
+ str = "operation succeeded!";
+ // str = activity.getString(R.string.import_error);
+
+ } else {
+
+ duration = 0;
+ color = Style.RED;
+
+ str = "operation failed";
+ // str = activity.getString(R.string.import_error);
+
+ }
+
+ boolean button = getLog() != null && !getLog().isEmpty();
+ SuperCardToast toast = new SuperCardToast(activity,
+ button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
+ Style.getStyle(color, SuperToast.Animations.POPUP));
+ toast.setText(str);
+ toast.setDuration(duration);
+ toast.setIndeterminate(duration == 0);
+ toast.setSwipeToDismiss(true);
+ // If we have a log and it's non-empty, show a View Log button
+ if (button) {
+ toast.setButtonIcon(R.drawable.ic_action_view_as_list,
+ activity.getResources().getString(R.string.view_log));
+ toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
+ toast.setTextColor(activity.getResources().getColor(R.color.black));
+ toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
+ new SuperToast.OnClickListener() {
+ @Override
+ public void onClick(View view, Parcelable token) {
+ Intent intent = new Intent(
+ activity, LogDisplayActivity.class);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResultParcel.this);
+ activity.startActivity(intent);
+ }
+ }
+ ));
+ }
+
+ return toast;
+
+ }
+
/** This is an enum of all possible log events.
*
* Element names should generally be prefixed with MSG_XX_ where XX is an
@@ -132,6 +208,8 @@ public class OperationResultParcel implements Parcelable {
*/
public static enum LogType {
+ INTERNAL_ERROR (R.string.internal_error),
+
// import public
MSG_IP(R.string.msg_ip),
MSG_IP_APPLY_BATCH (R.string.msg_ip_apply_batch),
@@ -279,6 +357,7 @@ public class OperationResultParcel implements Parcelable {
MSG_MF_UID_ADD (R.string.msg_mf_uid_add),
MSG_MF_UID_PRIMARY (R.string.msg_mf_uid_primary),
MSG_MF_UID_REVOKE (R.string.msg_mf_uid_revoke),
+ MSG_MF_UID_ERROR_EMPTY (R.string.msg_mf_uid_error_empty),
MSG_MF_UNLOCK_ERROR (R.string.msg_mf_unlock_error),
MSG_MF_UNLOCK (R.string.msg_mf_unlock),
;
@@ -329,12 +408,10 @@ public class OperationResultParcel implements Parcelable {
/// Simple convenience method
public void add(LogLevel level, LogType type, int indent, Object... parameters) {
- Log.d(Constants.TAG, type.toString());
mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, parameters));
}
public void add(LogLevel level, LogType type, int indent) {
- Log.d(Constants.TAG, type.toString());
mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
index fd3d4ed00..11829e7b8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
@@ -12,12 +12,13 @@ import com.github.johnpersano.supertoasts.util.OnClickWrapper;
import com.github.johnpersano.supertoasts.util.Style;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
public abstract class OperationResults {
- public static class ImportResult extends OperationResultParcel {
+ public static class ImportKeyResult extends OperationResultParcel {
public final int mNewKeys, mUpdatedKeys, mBadKeys;
@@ -47,15 +48,15 @@ public abstract class OperationResults {
return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING;
}
- public ImportResult(Parcel source) {
+ public ImportKeyResult(Parcel source) {
super(source);
mNewKeys = source.readInt();
mUpdatedKeys = source.readInt();
mBadKeys = source.readInt();
}
- public ImportResult(int result, OperationLog log,
- int newKeys, int updatedKeys, int badKeys) {
+ public ImportKeyResult(int result, OperationLog log,
+ int newKeys, int updatedKeys, int badKeys) {
super(result, log);
mNewKeys = newKeys;
mUpdatedKeys = updatedKeys;
@@ -70,17 +71,17 @@ public abstract class OperationResults {
dest.writeInt(mBadKeys);
}
- public static Creator<ImportResult> CREATOR = new Creator<ImportResult>() {
- public ImportResult createFromParcel(final Parcel source) {
- return new ImportResult(source);
+ public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() {
+ public ImportKeyResult createFromParcel(final Parcel source) {
+ return new ImportKeyResult(source);
}
- public ImportResult[] newArray(final int size) {
- return new ImportResult[size];
+ public ImportKeyResult[] newArray(final int size) {
+ return new ImportKeyResult[size];
}
};
- public void displayNotify(final Activity activity) {
+ public SuperCardToast createNotify(final Activity activity) {
int resultType = getResult();
@@ -88,11 +89,11 @@ public abstract class OperationResults {
int duration, color;
// Not an overall failure
- if ((resultType & ImportResult.RESULT_ERROR) == 0) {
+ if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) {
String withWarnings;
// Any warnings?
- if ((resultType & ImportResult.RESULT_WITH_WARNINGS) > 0) {
+ if ((resultType & ImportKeyResult.RESULT_WITH_WARNINGS) > 0) {
duration = 0;
color = Style.ORANGE;
withWarnings = activity.getResources().getString(R.string.import_with_warnings);
@@ -106,7 +107,7 @@ public abstract class OperationResults {
if (this.isOkBoth()) {
str = activity.getResources().getQuantityString(
R.plurals.import_keys_added_and_updated_1, mNewKeys, mNewKeys);
- str += activity.getResources().getQuantityString(
+ str += " "+ activity.getResources().getQuantityString(
R.plurals.import_keys_added_and_updated_2, mUpdatedKeys, mUpdatedKeys, withWarnings);
} else if (isOkUpdated()) {
str = activity.getResources().getQuantityString(
@@ -142,7 +143,7 @@ public abstract class OperationResults {
// If we have a log and it's non-empty, show a View Log button
if (button) {
toast.setButtonIcon(R.drawable.ic_action_view_as_list,
- activity.getResources().getString(R.string.import_view_log));
+ activity.getResources().getString(R.string.view_log));
toast.setButtonTextColor(activity.getResources().getColor(R.color.black));
toast.setTextColor(activity.getResources().getColor(R.color.black));
toast.setOnClickWrapper(new OnClickWrapper("supercardtoast",
@@ -151,18 +152,59 @@ public abstract class OperationResults {
public void onClick(View view, Parcelable token) {
Intent intent = new Intent(
activity, LogDisplayActivity.class);
- intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportResult.this);
+ intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ImportKeyResult.this);
activity.startActivity(intent);
}
}
));
}
- toast.show();
+ return toast;
+
+ }
+
+ }
+
+ public static class EditKeyResult extends OperationResultParcel {
+
+ private transient UncachedKeyRing mRing;
+ public final Long mRingMasterKeyId;
+
+ public EditKeyResult(int result, OperationLog log,
+ UncachedKeyRing ring) {
+ super(result, log);
+ mRing = ring;
+ mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : null;
+ }
+
+ public UncachedKeyRing getRing() {
+ return mRing;
+ }
+
+ public EditKeyResult(Parcel source) {
+ super(source);
+ mRingMasterKeyId = source.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeLong(mRingMasterKeyId);
}
+ public static Creator<EditKeyResult> CREATOR = new Creator<EditKeyResult>() {
+ public EditKeyResult createFromParcel(final Parcel source) {
+ return new EditKeyResult(source);
+ }
+
+ public EditKeyResult[] newArray(final int size) {
+ return new EditKeyResult[size];
+ }
+ };
+
}
+
public static class SaveKeyringResult extends OperationResultParcel {
public SaveKeyringResult(int result, OperationLog log) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 774b9a0df..97d92dbf7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -18,9 +18,9 @@
package org.sufficientlysecure.keychain.service;
import android.app.AlarmManager;
+import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
-import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -42,7 +42,7 @@ import org.spongycastle.bcpg.S2K;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -71,7 +71,7 @@ public class PassphraseCacheService extends Service {
public static final String EXTRA_KEY_ID = "key_id";
public static final String EXTRA_PASSPHRASE = "passphrase";
public static final String EXTRA_MESSENGER = "messenger";
- public static final String EXTRA_USERID = "userid";
+ public static final String EXTRA_USER_ID = "user_id";
private static final int REQUEST_ID = 0;
private static final long DEFAULT_TTL = 15;
@@ -103,7 +103,7 @@ public class PassphraseCacheService extends Service {
intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassphraseCacheTtl());
intent.putExtra(EXTRA_PASSPHRASE, passphrase);
intent.putExtra(EXTRA_KEY_ID, keyId);
- intent.putExtra(EXTRA_USERID, primaryUserId);
+ intent.putExtra(EXTRA_USER_ID, primaryUserId);
context.startService(intent);
}
@@ -185,7 +185,7 @@ public class PassphraseCacheService extends Service {
// try to get master key id which is used as an identifier for cached passphrases
try {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId);
- WrappedSecretKeyRing key = new ProviderHelper(this).getWrappedSecretKeyRing(
+ CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
// no passphrase needed? just add empty string and return it, then
if (!key.hasPassphrase()) {
@@ -209,18 +209,18 @@ public class PassphraseCacheService extends Service {
// get cached passphrase
CachedPassphrase cachedPassphrase = mPassphraseCache.get(keyId);
if (cachedPassphrase == null) {
- Log.d(Constants.TAG, "PassphraseCacheService Passphrase not (yet) cached, returning null");
+ Log.d(Constants.TAG, "PassphraseCacheService: Passphrase not (yet) cached, returning null");
// not really an error, just means the passphrase is not cached but not empty either
return null;
}
// set it again to reset the cache life cycle
- Log.d(Constants.TAG, "PassphraseCacheService Cache passphrase again when getting it!");
+ Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!");
addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID());
return cachedPassphrase.getPassphrase();
} catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "PassphraseCacheService Passphrase for unknown key was requested!");
+ Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!");
return null;
}
}
@@ -237,7 +237,7 @@ public class PassphraseCacheService extends Service {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- Log.d(Constants.TAG, "PassphraseCacheService Received broadcast...");
+ Log.d(Constants.TAG, "PassphraseCacheService: Received broadcast...");
if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
@@ -262,10 +262,8 @@ public class PassphraseCacheService extends Service {
private static PendingIntent buildIntent(Context context, long keyId) {
Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE);
intent.putExtra(EXTRA_KEY_ID, keyId);
- PendingIntent sender = PendingIntent.getBroadcast(context, REQUEST_ID, intent,
+ return PendingIntent.getBroadcast(context, REQUEST_ID, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
-
- return sender;
}
/**
@@ -284,11 +282,12 @@ public class PassphraseCacheService extends Service {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
- String primaryUserID = intent.getStringExtra(EXTRA_USERID);
+ String primaryUserID = intent.getStringExtra(EXTRA_USER_ID);
Log.d(Constants.TAG,
- "PassphraseCacheService Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: "
- + keyId + ", ttl: " + ttl + ", usrId: " + primaryUserID);
+ "PassphraseCacheService: Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: "
+ + keyId + ", ttl: " + ttl + ", usrId: " + primaryUserID
+ );
// add keyId, passphrase and primary user id to memory
mPassphraseCache.put(keyId, new CachedPassphrase(passphrase, primaryUserID));
@@ -300,8 +299,7 @@ public class PassphraseCacheService extends Service {
am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId));
}
- updateNotifications();
-
+ updateService();
} else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
@@ -315,21 +313,21 @@ public class PassphraseCacheService extends Service {
try {
messenger.send(msg);
} catch (RemoteException e) {
- Log.e(Constants.TAG, "PassphraseCacheService Sending message failed", e);
+ Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e);
}
} else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) {
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
// Stop all ttl alarms
- for(int i = 0; i < mPassphraseCache.size(); i++) {
+ for (int i = 0; i < mPassphraseCache.size(); i++) {
am.cancel(buildIntent(this, mPassphraseCache.keyAt(i)));
}
mPassphraseCache.clear();
- updateNotifications();
+ updateService();
} else {
- Log.e(Constants.TAG, "PassphraseCacheService Intent or Intent Action not supported!");
+ Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!");
}
}
@@ -348,79 +346,74 @@ public class PassphraseCacheService extends Service {
Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!");
- // stop whole service if no cached passphrases remaining
- if (mPassphraseCache.size() == 0) {
- Log.d(Constants.TAG, "PassphraseCacheServic No passphrases remaining in memory, stopping service!");
- stopSelf();
- }
-
- updateNotifications();
+ updateService();
}
- private void updateNotifications() {
- NotificationManager notificationManager =
- (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
+ private void updateService() {
+ if (mPassphraseCache.size() > 0) {
+ startForeground(NOTIFICATION_ID, getNotification());
+ } else {
+ // stop whole service if no cached passphrases remaining
+ Log.d(Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!");
+ stopForeground(true);
+ }
+ }
- if(mPassphraseCache.size() > 0) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+ private Notification getNotification() {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
- builder.setSmallIcon(R.drawable.ic_launcher)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ builder.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getString(R.string.app_name))
- .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys), mPassphraseCache.size()));
+ .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys),
+ mPassphraseCache.size()));
- NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
+ NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
- inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys));
+ inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys));
- // Moves events into the big view
- for (int i = 0; i < mPassphraseCache.size(); i++) {
- inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID());
- }
+ // Moves events into the big view
+ for (int i = 0; i < mPassphraseCache.size(); i++) {
+ inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID());
+ }
- // Moves the big view style object into the notification object.
- builder.setStyle(inboxStyle);
+ // Moves the big view style object into the notification object.
+ builder.setStyle(inboxStyle);
- // Add purging action
- Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
- intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
- builder.addAction(
+ // Add purging action
+ Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+ builder.addAction(
R.drawable.abc_ic_clear_normal,
getString(R.string.passp_cache_notif_clear),
PendingIntent.getService(
- getApplicationContext(),
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
+ getApplicationContext(),
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT
)
- );
-
- notificationManager.notify(NOTIFICATION_ID, builder.build());
- } else { // Fallback, since expandable notifications weren't available back then
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
-
- builder.setSmallIcon(R.drawable.ic_launcher)
- .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys, mPassphraseCache.size())))
+ );
+ } else {
+ // Fallback, since expandable notifications weren't available back then
+ builder.setSmallIcon(R.drawable.ic_launcher)
+ .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys,
+ mPassphraseCache.size())))
.setContentText(getString(R.string.passp_cache_notif_click_to_clear));
- Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
- intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+ Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
- builder.setContentIntent(
+ builder.setContentIntent(
PendingIntent.getService(
- getApplicationContext(),
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
+ getApplicationContext(),
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT
)
- );
-
- notificationManager.notify(NOTIFICATION_ID, builder.build());
- }
-
- } else {
- notificationManager.cancel(NOTIFICATION_ID);
+ );
}
+
+ return builder.build();
}
@Override
@@ -463,6 +456,7 @@ public class PassphraseCacheService extends Service {
public String getPrimaryUserID() {
return primaryUserID;
}
+
public String getPassphrase() {
return passphrase;
}
@@ -470,6 +464,7 @@ public class PassphraseCacheService extends Service {
public void setPrimaryUserID(String primaryUserID) {
this.primaryUserID = primaryUserID;
}
+
public void setPassphrase(String passphrase) {
this.passphrase = passphrase;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index 7ac229b91..467e0c14a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -37,12 +38,11 @@ import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
@@ -52,10 +52,12 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.ArrayList;
@@ -64,7 +66,8 @@ import java.util.ArrayList;
*/
public class CertifyKeyActivity extends ActionBarActivity implements
SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> {
- private View mSignButton;
+ private View mCertifyButton;
+ private ImageView mActionCertifyImage;
private CheckBox mUploadKeyCheckbox;
private Spinner mSelectKeyserverSpinner;
@@ -88,10 +91,19 @@ public class CertifyKeyActivity extends ActionBarActivity implements
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager()
.findFragmentById(R.id.sign_key_select_key_fragment);
+ mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
+ mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox);
+ mCertifyButton = findViewById(R.id.certify_key_certify_button);
+ mActionCertifyImage = (ImageView) findViewById(R.id.certify_key_action_certify_image);
+ mUserIds = (ListView) findViewById(R.id.view_key_user_ids);
+
+ // make certify image gray, like action icons
+ mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ PorterDuff.Mode.SRC_IN);
+
mSelectKeyFragment.setCallback(this);
mSelectKeyFragment.setFilterCertify(true);
- mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
.getKeyServers()
@@ -99,7 +111,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mSelectKeyserverSpinner.setAdapter(adapter);
- mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox);
if (!mUploadKeyCheckbox.isChecked()) {
mSelectKeyserverSpinner.setEnabled(false);
} else {
@@ -118,16 +129,17 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
});
- mSignButton = findViewById(R.id.sign_key_sign_button);
- mSignButton.setOnClickListener(new OnClickListener() {
+ mCertifyButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mPubKeyId != 0) {
if (mMasterKeyId == 0) {
mSelectKeyFragment.setError(getString(R.string.select_key_to_certify));
+ Notify.showNotify(CertifyKeyActivity.this, getString(R.string.select_key_to_certify),
+ Notify.Style.ERROR);
} else {
- initiateSigning();
+ initiateCertifying();
}
}
}
@@ -141,7 +153,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
Log.e(Constants.TAG, "uri: " + mDataUri);
- mUserIds = (ListView) findViewById(R.id.view_key_user_ids);
mUserIdsAdapter = new UserIdsAdapter(this, null, 0, true);
mUserIds.setAdapter(mUserIdsAdapter);
@@ -218,7 +229,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
/**
* handles the UI bits of the signing process on the UI thread
*/
- private void initiateSigning() {
+ private void initiateCertifying() {
// get the user's passphrase for this key (if required)
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
@@ -227,27 +238,27 @@ public class CertifyKeyActivity extends ActionBarActivity implements
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- startSigning();
+ startCertifying();
}
}
- });
+ }
+ );
// bail out; need to wait until the user has entered the passphrase before trying again
return;
} else {
- startSigning();
+ startCertifying();
}
}
/**
* kicks off the actual signing process on a background thread
*/
- private void startSigning() {
-
+ private void startCertifying() {
// Bail out if there is not at least one user id selected
ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds();
if (userIds.isEmpty()) {
- AppMsg.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!",
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(CertifyKeyActivity.this, "No identities selected!",
+ Notify.Style.ERROR);
return;
}
@@ -267,15 +278,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
// Message is received after signing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) {
+ getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- AppMsg.makeText(CertifyKeyActivity.this, R.string.key_certify_success,
- AppMsg.STYLE_INFO).show();
+ Notify.showNotify(CertifyKeyActivity.this, R.string.key_certify_success,
+ Notify.Style.INFO);
// check if we need to send the key to the server or not
if (mUploadKeyCheckbox.isChecked()) {
@@ -321,14 +332,16 @@ public class CertifyKeyActivity extends ActionBarActivity implements
// Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
+ getString(R.string.progress_uploading), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- AppMsg.makeText(CertifyKeyActivity.this, R.string.key_send_success,
- AppMsg.STYLE_INFO).show();
+ Intent intent = new Intent();
+ intent.putExtra(OperationResultParcel.EXTRA_RESULT, message.getData());
+ Notify.showNotify(CertifyKeyActivity.this, R.string.key_send_success,
+ Notify.Style.INFO);
setResult(RESULT_OK);
finish();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
index d2073d9a7..534ac5811 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java
@@ -17,171 +17,69 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBarActivity;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Patterns;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-import android.widget.Button;
-import android.widget.EditText;
-import org.spongycastle.bcpg.sig.KeyFlags;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ContactHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-
-import java.util.regex.Matcher;
public class CreateKeyActivity extends ActionBarActivity {
- AutoCompleteTextView nameEdit;
- AutoCompleteTextView emailEdit;
- EditText passphraseEdit;
- Button createButton;
+ public static final String EXTRA_NAME = "name";
+ public static final String EXTRA_EMAIL = "email";
+
+ public static final int FRAG_ACTION_START = 0;
+ public static final int FRAG_ACTION_TO_RIGHT = 1;
+ public static final int FRAG_ACTION_TO_LEFT = 2;
@Override
- protected void onCreate(Bundle savedInstanceState) {
+ public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_key_activity);
- nameEdit = (AutoCompleteTextView) findViewById(R.id.name);
- emailEdit = (AutoCompleteTextView) findViewById(R.id.email);
- passphraseEdit = (EditText) findViewById(R.id.passphrase);
- createButton = (Button) findViewById(R.id.create_key_button);
-
- emailEdit.setThreshold(1); // Start working from first character
- emailEdit.setAdapter(
- new ArrayAdapter<String>
- (this, android.R.layout.simple_spinner_dropdown_item,
- ContactHelper.getPossibleUserEmails(this)
- )
- );
- emailEdit.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
- }
-
- @Override
- public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- String email = editable.toString();
- if (email.length() > 0) {
- Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
- if (emailMatcher.matches()) {
- emailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_ok, 0);
- } else {
- emailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
- R.drawable.uid_mail_bad, 0);
- }
- } else {
- // remove drawable if email is empty
- emailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
- }
- }
- });
- nameEdit.setThreshold(1); // Start working from first character
- nameEdit.setAdapter(
- new ArrayAdapter<String>
- (this, android.R.layout.simple_spinner_dropdown_item,
- ContactHelper.getPossibleUserNames(this)
- )
- );
-
- createButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- createKeyCheck();
- }
- });
-
+ // pass extras into fragment
+ CreateKeyInputFragment frag =
+ CreateKeyInputFragment.newInstance(
+ getIntent().getStringExtra(EXTRA_NAME),
+ getIntent().getStringExtra(EXTRA_EMAIL)
+ );
+ loadFragment(null, frag, FRAG_ACTION_START);
}
- private void createKeyCheck() {
- if (isEditTextNotEmpty(this, nameEdit)
- && isEditTextNotEmpty(this, emailEdit)
- && isEditTextNotEmpty(this, passphraseEdit)) {
- createKey();
+ public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) {
+ // However, if we're being restored from a previous state,
+ // then we don't need to do anything and should return or else
+ // we could end up with overlapping fragments.
+ if (savedInstanceState != null) {
+ return;
}
- }
-
- private void createKey() {
- Intent intent = new Intent(this, KeychainIntentService.class);
- intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
-
- // Message is received after importing is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
- this,
- getString(R.string.progress_importing),
- ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- CreateKeyActivity.this.finish();
- }
- }
- };
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+ switch (action) {
+ case FRAG_ACTION_START:
+ transaction.setCustomAnimations(0, 0);
+ transaction.replace(R.id.create_key_fragment_container, fragment)
+ .commitAllowingStateLoss();
+ break;
+ case FRAG_ACTION_TO_LEFT:
+ getSupportFragmentManager().popBackStackImmediate();
+ break;
+ case FRAG_ACTION_TO_RIGHT:
+ transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left,
+ R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right);
+ transaction.addToBackStack(null);
+ transaction.replace(R.id.create_key_fragment_container, fragment)
+ .commitAllowingStateLoss();
+ break;
- // fill values for this action
- Bundle data = new Bundle();
-
- SaveKeyringParcel parcel = new SaveKeyringParcel();
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.CERTIFY_OTHER, null));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.SIGN_DATA, null));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null));
- String userId = nameEdit.getText().toString() + " <" + emailEdit.getText().toString() + ">";
- parcel.mAddUserIds.add(userId);
- parcel.mNewPassphrase = passphraseEdit.getText().toString();
-
- // get selected key entries
- data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- saveHandler.showProgressDialog(this);
-
- startService(intent);
- }
-
- /**
- * Checks if text of given EditText is not empty. If it is empty an error is
- * set and the EditText gets the focus.
- *
- * @param context
- * @param editText
- * @return true if EditText is not empty
- */
- private static boolean isEditTextNotEmpty(Context context, EditText editText) {
- boolean output = true;
- if (editText.getText().toString().length() == 0) {
- editText.setError("empty!");
- editText.requestFocus();
- output = false;
- } else {
- editText.setError(null);
}
-
- return output;
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
new file mode 100644
index 000000000..f9ed16cba
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -0,0 +1,249 @@
+/*
+ * 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 android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.util.Notify;
+
+public class CreateKeyFinalFragment extends Fragment {
+
+ CreateKeyActivity mCreateKeyActivity;
+
+ TextView mNameEdit;
+ TextView mEmailEdit;
+ CheckBox mUploadCheckbox;
+ View mBackButton;
+ View mCreateButton;
+
+ public static final String ARG_NAME = "name";
+ public static final String ARG_EMAIL = "email";
+ public static final String ARG_PASSPHRASE = "passphrase";
+
+ String mName;
+ String mEmail;
+ String mPassphrase;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateKeyFinalFragment newInstance(String name, String email, String passphrase) {
+ CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
+
+ Bundle args = new Bundle();
+ args.putString(ARG_NAME, name);
+ args.putString(ARG_EMAIL, email);
+ args.putString(ARG_PASSPHRASE, passphrase);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_key_final_fragment, container, false);
+
+ mNameEdit = (TextView) view.findViewById(R.id.name);
+ mEmailEdit = (TextView) view.findViewById(R.id.email);
+ mUploadCheckbox = (CheckBox) view.findViewById(R.id.create_key_upload);
+ mBackButton = view.findViewById(R.id.create_key_back_button);
+ mCreateButton = view.findViewById(R.id.create_key_create_button);
+
+ // get args
+ mName = getArguments().getString(ARG_NAME);
+ mEmail = getArguments().getString(ARG_EMAIL);
+ mPassphrase = getArguments().getString(ARG_PASSPHRASE);
+
+ // set values
+ mNameEdit.setText(mName);
+ mEmailEdit.setText(mEmail);
+
+ mCreateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKey();
+ }
+ });
+
+ mBackButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCreateKeyActivity.loadFragment(null, null, CreateKeyActivity.FRAG_ACTION_TO_LEFT);
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void createKey() {
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+ intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
+
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ getActivity(),
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+ final OperationResults.EditKeyResult result =
+ returnData.getParcelable(OperationResultParcel.EXTRA_RESULT);
+ if (result == null) {
+ return;
+ }
+
+ if (result.getResult() == OperationResultParcel.RESULT_OK) {
+ if (mUploadCheckbox.isChecked()) {
+ // result will be displayed after upload
+ uploadKey(result);
+ } else {
+ // TODO: return result
+ result.createNotify(getActivity());
+
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().finish();
+ }
+ } else {
+ // display result on error without finishing activity
+ result.createNotify(getActivity());
+ }
+ }
+ }
+ };
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ SaveKeyringParcel parcel = new SaveKeyringParcel();
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.CERTIFY_OTHER, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.SIGN_DATA, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(Constants.choice.algorithm.rsa, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null));
+ String userId = mName + " <" + mEmail + ">";
+ parcel.mAddUserIds.add(userId);
+ parcel.mNewPassphrase = mPassphrase;
+
+ // get selected key entries
+ data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, parcel);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ saveHandler.showProgressDialog(getActivity());
+
+ getActivity().startService(intent);
+ }
+
+ private void uploadKey(final OperationResults.EditKeyResult editKeyResult) {
+ // Send all information needed to service to upload key in other thread
+ final Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_UPLOAD_KEYRING);
+
+ // set data uri as path to keyring
+ Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
+ editKeyResult.mRingMasterKeyId);
+ intent.setData(blobUri);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ // upload to favorite keyserver
+ String keyserver = Preferences.getPreferences(getActivity()).getKeyServers()[0];
+ data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after uploading is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_uploading), ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // TODO: not supported by upload?
+// if (result.getResult() == OperationResultParcel.RESULT_OK) {
+ // TODO: return result
+ editKeyResult.createNotify(getActivity());
+
+ Notify.showNotify(getActivity(), R.string.key_send_success,
+ Notify.Style.INFO);
+
+ getActivity().setResult(Activity.RESULT_OK);
+ getActivity().finish();
+// } else {
+// // display result on error without finishing activity
+// editKeyResult.createNotify(getActivity());
+// }
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java
new file mode 100644
index 000000000..ef5a3b145
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyInputFragment.java
@@ -0,0 +1,214 @@
+/*
+ * 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 android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Patterns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.EditText;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ContactHelper;
+
+import java.util.regex.Matcher;
+
+public class CreateKeyInputFragment extends Fragment {
+
+ CreateKeyActivity mCreateKeyActivity;
+
+ AutoCompleteTextView mNameEdit;
+ AutoCompleteTextView mEmailEdit;
+ EditText mPassphraseEdit;
+ EditText mPassphraseEditAgain;
+ View mCreateButton;
+
+ public static final String ARG_NAME = "name";
+ public static final String ARG_EMAIL = "email";
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static CreateKeyInputFragment newInstance(String name, String email) {
+ CreateKeyInputFragment frag = new CreateKeyInputFragment();
+
+ Bundle args = new Bundle();
+ args.putString(ARG_NAME, name);
+ args.putString(ARG_EMAIL, email);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.create_key_input_fragment, container, false);
+
+ mNameEdit = (AutoCompleteTextView) view.findViewById(R.id.name);
+ mEmailEdit = (AutoCompleteTextView) view.findViewById(R.id.email);
+ mPassphraseEdit = (EditText) view.findViewById(R.id.passphrase);
+ mPassphraseEditAgain = (EditText) view.findViewById(R.id.passphrase_again);
+ mCreateButton = view.findViewById(R.id.create_key_button);
+
+ // initial values
+ String name = getArguments().getString(ARG_NAME);
+ String email = getArguments().getString(ARG_EMAIL);
+ mNameEdit.setText(name);
+ mEmailEdit.setText(email);
+
+ // focus non-empty edit fields
+ if (name != null && email != null) {
+ mPassphraseEdit.requestFocus();
+ } else if (name != null) {
+ mEmailEdit.requestFocus();
+ }
+
+ mEmailEdit.setThreshold(1); // Start working from first character
+ mEmailEdit.setAdapter(
+ new ArrayAdapter<String>
+ (getActivity(), android.R.layout.simple_spinner_dropdown_item,
+ ContactHelper.getPossibleUserEmails(getActivity())
+ )
+ );
+ mEmailEdit.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ String email = editable.toString();
+ if (email.length() > 0) {
+ Matcher emailMatcher = Patterns.EMAIL_ADDRESS.matcher(email);
+ if (emailMatcher.matches()) {
+ mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
+ R.drawable.uid_mail_ok, 0);
+ } else {
+ mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0,
+ R.drawable.uid_mail_bad, 0);
+ }
+ } else {
+ // remove drawable if email is empty
+ mEmailEdit.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
+ }
+ }
+ });
+
+ mNameEdit.setThreshold(1); // Start working from first character
+ mNameEdit.setAdapter(
+ new ArrayAdapter<String>
+ (getActivity(), android.R.layout.simple_spinner_dropdown_item,
+ ContactHelper.getPossibleUserNames(getActivity())
+ )
+ );
+
+ mCreateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKeyCheck();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mCreateKeyActivity = (CreateKeyActivity) getActivity();
+ }
+
+ private void createKeyCheck() {
+ if (isEditTextNotEmpty(getActivity(), mNameEdit)
+ && isEditTextNotEmpty(getActivity(), mEmailEdit)
+ && isEditTextNotEmpty(getActivity(), mPassphraseEdit)
+ && areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) {
+
+ CreateKeyFinalFragment frag =
+ CreateKeyFinalFragment.newInstance(
+ mNameEdit.getText().toString(),
+ mEmailEdit.getText().toString(),
+ mPassphraseEdit.getText().toString()
+ );
+
+ hideKeyboard();
+ mCreateKeyActivity.loadFragment(null, frag, CreateKeyActivity.FRAG_ACTION_TO_RIGHT);
+ }
+ }
+
+ private void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ //check if no view has focus:
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+
+ /**
+ * Checks if text of given EditText is not empty. If it is empty an error is
+ * set and the EditText gets the focus.
+ *
+ * @param context
+ * @param editText
+ * @return true if EditText is not empty
+ */
+ private static boolean isEditTextNotEmpty(Context context, EditText editText) {
+ boolean output = true;
+ if (editText.getText().toString().length() == 0) {
+ editText.setError(context.getString(R.string.create_key_empty));
+ editText.requestFocus();
+ output = false;
+ } else {
+ editText.setError(null);
+ }
+
+ return output;
+ }
+
+ private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) {
+ boolean output = true;
+ if (!editText1.getText().toString().equals(editText2.getText().toString())) {
+ editText2.setError(context.getString(R.string.create_key_passphrases_not_equal));
+ editText2.requestFocus();
+ output = false;
+ } else {
+ editText2.setError(null);
+ }
+
+ return output;
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
index c12b5b7be..56dfdbd95 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -34,8 +34,6 @@ import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageButton;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
@@ -129,7 +127,6 @@ public class DecryptFileFragment extends DecryptFragment {
}
if (mInputFilename.equals("")) {
- //AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
return;
}
@@ -137,11 +134,8 @@ public class DecryptFileFragment extends DecryptFragment {
if (mInputUri == null && mInputFilename.startsWith("file")) {
File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) {
- AppMsg.makeText(
- getActivity(),
- getString(R.string.error_message,
- getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
- .show();
+ Notify.showNotify(getActivity(), getString(R.string.error_message,
+ getString(R.string.error_file_not_found)), Notify.Style.ERROR);
return;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
index 6b8358538..d4235b82b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -132,7 +132,7 @@ public class DecryptFragment extends Fragment {
mResultText.setText(R.string.decrypt_result_decrypted_and_signature_certified);
}
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_green));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_green_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
mSignatureLayout.setVisibility(View.VISIBLE);
mLookupKey.setVisibility(View.GONE);
@@ -146,7 +146,7 @@ public class DecryptFragment extends Fragment {
mResultText.setText(R.string.decrypt_result_decrypted_and_signature_uncertified);
}
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_orange));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
mSignatureLayout.setVisibility(View.VISIBLE);
mLookupKey.setVisibility(View.GONE);
@@ -160,7 +160,7 @@ public class DecryptFragment extends Fragment {
mResultText.setText(R.string.decrypt_result_decrypted_unknown_pub_key);
}
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_orange));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_orange_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mSignatureLayout.setVisibility(View.VISIBLE);
mLookupKey.setVisibility(View.VISIBLE);
@@ -170,7 +170,7 @@ public class DecryptFragment extends Fragment {
case OpenPgpSignatureResult.SIGNATURE_ERROR: {
mResultText.setText(R.string.decrypt_result_invalid_signature);
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_red));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mSignatureLayout.setVisibility(View.GONE);
mLookupKey.setVisibility(View.GONE);
@@ -180,7 +180,7 @@ public class DecryptFragment extends Fragment {
default: {
mResultText.setText(R.string.error);
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_red));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_red_light));
mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
mSignatureLayout.setVisibility(View.GONE);
mLookupKey.setVisibility(View.GONE);
@@ -192,7 +192,7 @@ public class DecryptFragment extends Fragment {
mLookupKey.setVisibility(View.GONE);
// successful decryption-only
- mResultLayout.setBackgroundColor(getResources().getColor(R.color.result_purple));
+ mResultLayout.setBackgroundColor(getResources().getColor(R.color.android_purple_light));
mResultText.setText(R.string.decrypt_result_decrypted);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
index 46462f924..cf7a0b4b8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
@@ -28,8 +28,6 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.EditText;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
@@ -38,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.regex.Matcher;
@@ -107,12 +106,10 @@ public class DecryptMessageFragment extends DecryptFragment {
mCiphertext = matcher.group(1);
decryptStart(null);
} else {
- AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
- .show();
+ Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR);
}
} else {
- AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_ALERT)
- .show();
+ Notify.showNotify(getActivity(), R.string.error_invalid_data, Notify.Style.ERROR);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index d80425c3c..6ddaec17f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -33,7 +33,7 @@ public class EditKeyActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setContentView(R.layout.edit_key_activity_new);
+ setContentView(R.layout.edit_key_activity);
Uri dataUri = getIntent().getData();
if (dataUri == null) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java
deleted file mode 100644
index 70ccb8800..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityOld.java
+++ /dev/null
@@ -1,744 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
- *
- * 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 android.content.Context;
-import android.content.DialogInterface;
-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.support.v4.app.ActivityCompat;
-import android.support.v7.app.ActionBarActivity;
-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;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import com.devspark.appmsg.AppMsg;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.helper.ExportHelper;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
-import org.sufficientlysecure.keychain.ui.widget.Editor;
-import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
-import org.sufficientlysecure.keychain.ui.widget.KeyEditor;
-import org.sufficientlysecure.keychain.ui.widget.SectionView;
-import org.sufficientlysecure.keychain.ui.widget.UserIdEditor;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.Vector;
-
-public class EditKeyActivityOld extends ActionBarActivity implements EditorListener {
-
- // Actions for internal use only:
- public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY";
- public static final String ACTION_EDIT_KEY = Constants.INTENT_PREFIX + "EDIT_KEY";
-
- // possible extra keys
- public static final String EXTRA_USER_IDS = "user_ids";
- public static final String EXTRA_NO_PASSPHRASE = "no_passphrase";
- public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generate_default_keys";
-
- // EDIT
- private Uri mDataUri;
-
- private SectionView mUserIdsView;
- private SectionView mKeysView;
-
- private String mCurrentPassphrase = null;
- private String mNewPassphrase = null;
- private String mSavedNewPassphrase = null;
- private boolean mIsPassphraseSet;
- private boolean mNeedsSaving;
- private boolean mIsBrandNewKeyring = false;
-
- private Button mChangePassphrase;
-
- private CheckBox mNoPassphrase;
-
- Vector<String> mUserIds;
- Vector<UncachedSecretKey> mKeys;
- Vector<Integer> mKeysUsages;
- boolean mMasterCanSign = true;
-
- ExportHelper mExportHelper;
-
- public boolean needsSaving() {
- mNeedsSaving = (mUserIdsView == null) ? false : mUserIdsView.needsSaving();
- mNeedsSaving |= (mKeysView == null) ? false : mKeysView.needsSaving();
- mNeedsSaving |= hasPassphraseChanged();
- mNeedsSaving |= mIsBrandNewKeyring;
- return mNeedsSaving;
- }
-
-
- public void somethingChanged() {
- ActivityCompat.invalidateOptionsMenu(this);
- }
-
- public void onDeleted(Editor e, boolean wasNewItem) {
- somethingChanged();
- }
-
- public void onEdited() {
- somethingChanged();
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mExportHelper = new ExportHelper(this);
-
- // Inflate a "Done"/"Cancel" custom action bar view
- ActionBarHelper.setTwoButtonView(getSupportActionBar(),
- R.string.btn_save, R.drawable.ic_action_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Save
- saveClicked();
- }
- }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Cancel
- cancelClicked();
- }
- }
- );
-
- mUserIds = new Vector<String>();
- mKeys = new Vector<UncachedSecretKey>();
- mKeysUsages = new Vector<Integer>();
-
- // Catch Intents opened from other apps
- Intent intent = getIntent();
- String action = intent.getAction();
- if (ACTION_CREATE_KEY.equals(action)) {
- handleActionCreateKey(intent);
- } else if (ACTION_EDIT_KEY.equals(action)) {
- handleActionEditKey(intent);
- }
- }
-
- /**
- * Handle intent action to create new key
- *
- * @param intent
- */
- private void handleActionCreateKey(Intent intent) {
- Bundle extras = intent.getExtras();
-
- mCurrentPassphrase = "";
- mIsBrandNewKeyring = true;
-
- if (extras != null) {
- // if userId is given, prefill the fields
- if (extras.containsKey(EXTRA_USER_IDS)) {
- Log.d(Constants.TAG, "UserIds are given!");
- mUserIds.add(extras.getString(EXTRA_USER_IDS));
- }
-
- // if no passphrase is given
- if (extras.containsKey(EXTRA_NO_PASSPHRASE)) {
- boolean noPassphrase = extras.getBoolean(EXTRA_NO_PASSPHRASE);
- if (noPassphrase) {
- // check "no passphrase" checkbox and remove button
- mNoPassphrase.setChecked(true);
- mChangePassphrase.setVisibility(View.GONE);
- }
- }
-
- // generate key
- if (extras.containsKey(EXTRA_GENERATE_DEFAULT_KEYS)) {
- /*
- boolean generateDefaultKeys = extras.getBoolean(EXTRA_GENERATE_DEFAULT_KEYS);
- if (generateDefaultKeys) {
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE,
- mCurrentPassphrase);
-
- serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after generating is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
- this, getResources().getQuantityString(R.plurals.progress_generating, 1),
- ProgressDialog.STYLE_HORIZONTAL, true,
-
- new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- // Stop key generation on cancel
- stopService(serviceIntent);
- EditKeyActivity.this.setResult(Activity.RESULT_CANCELED);
- EditKeyActivity.this.finish();
- }
- }) {
-
- @Override
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get new key from data bundle returned from service
- Bundle data = message.getDataAsStringList();
-
- ArrayList<UncachedSecretKey> newKeys =
- PgpConversionHelper.BytesToPGPSecretKeyList(data
- .getByteArray(KeychainIntentService.RESULT_NEW_KEY));
-
- ArrayList<Integer> keyUsageFlags = data.getIntegerArrayList(
- KeychainIntentService.RESULT_KEY_USAGES);
-
- if (newKeys.size() == keyUsageFlags.size()) {
- for (int i = 0; i < newKeys.size(); ++i) {
- mKeys.add(newKeys.get(i));
- mKeysUsages.add(keyUsageFlags.get(i));
- }
- }
-
- buildLayout(true);
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- serviceIntent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(serviceIntent);
- }
- */
- }
- } else {
- buildLayout(false);
- }
- }
-
- /**
- * Handle intent action to edit existing key
- *
- * @param intent
- */
- private void handleActionEditKey(Intent intent) {
- mDataUri = intent.getData();
- if (mDataUri == null) {
- Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
- finish();
- } else {
- Log.d(Constants.TAG, "uri: " + mDataUri);
-
- try {
- Uri secretUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
- WrappedSecretKeyRing keyRing = new ProviderHelper(this).getWrappedSecretKeyRing(secretUri);
-
- mMasterCanSign = keyRing.getSecretKey().canCertify();
- for (WrappedSecretKey key : keyRing.secretKeyIterator()) {
- // Turn into uncached instance
- mKeys.add(key.getUncached());
- mKeysUsages.add(key.getKeyUsage()); // get usage when view is created
- }
-
- boolean isSet = false;
- for (String userId : keyRing.getSecretKey().getUserIds()) {
- Log.d(Constants.TAG, "Added userId " + userId);
- if (!isSet) {
- isSet = true;
- String[] parts = KeyRing.splitUserId(userId);
- if (parts[0] != null) {
- setTitle(parts[0]);
- }
- }
- mUserIds.add(userId);
- }
-
- buildLayout(false);
-
- mCurrentPassphrase = "";
- mIsPassphraseSet = keyRing.hasPassphrase();
- if (!mIsPassphraseSet) {
- // check "no passphrase" checkbox and remove button
- mNoPassphrase.setChecked(true);
- mChangePassphrase.setVisibility(View.GONE);
- }
-
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "Keyring not found: " + e.getMessage(), e);
- Toast.makeText(this, R.string.error_no_secret_key_found, Toast.LENGTH_SHORT).show();
- finish();
- }
-
- }
- }
-
- /**
- * Shows the dialog to set a new passphrase
- */
- private void showSetPassphraseDialog() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == SetPassphraseDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
-
- // set new returned passphrase!
- mNewPassphrase = data
- .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
-
- updatePassphraseButtonText();
- somethingChanged();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- // set title based on isPassphraseSet()
- int title;
- if (isPassphraseSet()) {
- title = R.string.title_change_passphrase;
- } else {
- title = R.string.title_set_passphrase;
- }
-
- SetPassphraseDialogFragment setPassphraseDialog = SetPassphraseDialogFragment.newInstance(
- messenger, null, title);
-
- setPassphraseDialog.show(getSupportFragmentManager(), "setPassphraseDialog");
- }
-
- /**
- * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user
- * id and key.
- *
- * @param newKeys
- */
- private void buildLayout(boolean newKeys) {
- setContentView(R.layout.edit_key_activity);
-
- // find views
- mChangePassphrase = (Button) findViewById(R.id.edit_key_btn_change_passphrase);
- 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);
-
- LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container);
- if (mIsPassphraseSet) {
- mChangePassphrase.setText(getString(R.string.btn_change_passphrase));
- }
- mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
- mUserIdsView.setType(SectionView.TYPE_USER_ID);
- mUserIdsView.setCanBeEdited(mMasterCanSign);
- mUserIdsView.setUserIds(mUserIds);
- mUserIdsView.setEditorListener(this);
- container.addView(mUserIdsView);
- mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false);
- mKeysView.setType(SectionView.TYPE_KEY);
- mKeysView.setCanBeEdited(mMasterCanSign);
- mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
- mKeysView.setEditorListener(this);
- container.addView(mKeysView);
-
- updatePassphraseButtonText();
-
- mChangePassphrase.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- showSetPassphraseDialog();
- }
- });
-
- // disable passphrase when no passphrase checkbox is checked!
- mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() {
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (isChecked) {
- // remove passphrase
- mSavedNewPassphrase = mNewPassphrase;
- mNewPassphrase = "";
- mChangePassphrase.setVisibility(View.GONE);
- } else {
- mNewPassphrase = mSavedNewPassphrase;
- mChangePassphrase.setVisibility(View.VISIBLE);
- }
- somethingChanged();
- }
- });
- }
-
- private long getMasterKeyId() {
- if (mKeysView.getEditors().getChildCount() == 0) {
- return 0;
- }
- return ((KeyEditor) mKeysView.getEditors().getChildAt(0)).getValue().getKeyId();
- }
-
- public boolean isPassphraseSet() {
- if (mNoPassphrase.isChecked()) {
- return true;
- } else if ((mIsPassphraseSet)
- || (mNewPassphrase != null && !mNewPassphrase.equals(""))) {
- return true;
- } else {
- return false;
- }
- }
-
- public boolean hasPassphraseChanged() {
- if (mNoPassphrase != null) {
- if (mNoPassphrase.isChecked()) {
- return mIsPassphraseSet;
- } else {
- return (mNewPassphrase != null && !mNewPassphrase.equals(""));
- }
- } else {
- return false;
- }
- }
-
- private void saveClicked() {
- final long masterKeyId = getMasterKeyId();
- if (needsSaving()) { //make sure, as some versions don't support invalidateOptionsMenu
- try {
- if (!isPassphraseSet()) {
- throw new PgpGeneralException(this.getString(R.string.set_a_passphrase));
- }
-
- String passphrase;
- if (mIsPassphraseSet) {
- passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
- } else {
- passphrase = "";
- }
- if (passphrase == null) {
- PassphraseDialogFragment.show(this, masterKeyId,
- new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(
- EditKeyActivityOld.this, masterKeyId);
- checkEmptyIDsWanted();
- }
- }
- });
- } else {
- mCurrentPassphrase = passphrase;
- checkEmptyIDsWanted();
- }
- } catch (PgpGeneralException e) {
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- } else {
- AppMsg.makeText(this, R.string.error_change_something_first, AppMsg.STYLE_ALERT).show();
- }
- }
-
- private void checkEmptyIDsWanted() {
- try {
- ArrayList<String> userIDs = getUserIds(mUserIdsView);
- List<Boolean> newIDs = mUserIdsView.getNewIDFlags();
- ArrayList<String> originalIDs = mUserIdsView.getOriginalIDs();
- int curID = 0;
- for (String userID : userIDs) {
- if (userID.equals("") && (!userID.equals(originalIDs.get(curID)) || newIDs.get(curID))) {
- CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
- EditKeyActivityOld.this);
-
- alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
- alert.setTitle(R.string.warning);
- alert.setMessage(EditKeyActivityOld.this.getString(R.string.ask_empty_id_ok));
-
- alert.setPositiveButton(EditKeyActivityOld.this.getString(android.R.string.yes),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- finallySaveClicked();
- }
- }
- );
- alert.setNegativeButton(this.getString(android.R.string.no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- }
- }
- );
- alert.setCancelable(false);
- alert.show();
- return;
- }
- curID++;
- }
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), AppMsg.STYLE_ALERT).show();
- }
- finallySaveClicked();
- }
-
- private boolean[] toPrimitiveArray(final List<Boolean> booleanList) {
- final boolean[] primitives = new boolean[booleanList.size()];
- int index = 0;
- for (Boolean object : booleanList) {
- primitives[index++] = object;
- }
- return primitives;
- }
-
- private void finallySaveClicked() {
- /*
- try {
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
-
- OldSaveKeyringParcel saveParams = new OldSaveKeyringParcel();
- saveParams.userIds = getUserIds(mUserIdsView);
- saveParams.originalIDs = mUserIdsView.getOriginalIDs();
- saveParams.deletedIDs = mUserIdsView.getDeletedIDs();
- saveParams.newIDs = toPrimitiveArray(mUserIdsView.getNewIDFlags());
- saveParams.primaryIDChanged = mUserIdsView.primaryChanged();
- saveParams.moddedKeys = toPrimitiveArray(mKeysView.getNeedsSavingArray());
- saveParams.deletedKeys = mKeysView.getDeletedKeys();
- saveParams.keysExpiryDates = getKeysExpiryDates(mKeysView);
- saveParams.keysUsages = getKeysUsages(mKeysView);
- saveParams.mNewPassphrase = mNewPassphrase;
- saveParams.oldPassphrase = mCurrentPassphrase;
- saveParams.newKeys = toPrimitiveArray(mKeysView.getNewKeysArray());
- saveParams.keys = getKeys(mKeysView);
- saveParams.originalPrimaryID = mUserIdsView.getOriginalPrimaryID();
-
- // fill values for this action
- Bundle data = new Bundle();
- data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, mMasterCanSign);
- data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after saving is done in KeychainIntentService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard KeychainIntentServiceHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Intent data = new Intent();
-
- // return uri pointing to new created key
- Uri uri = KeyRings.buildGenericKeyRingUri(getMasterKeyId());
- data.setData(uri);
-
- setResult(RESULT_OK, data);
- finish();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- */
- }
-
- private void cancelClicked() {
- if (needsSaving()) { //ask if we want to save
- CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(
- EditKeyActivityOld.this);
-
- alert.setIcon(R.drawable.ic_dialog_alert_holo_light);
- alert.setTitle(R.string.warning);
- alert.setMessage(EditKeyActivityOld.this.getString(R.string.ask_save_changed_key));
-
- alert.setPositiveButton(EditKeyActivityOld.this.getString(android.R.string.yes),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- saveClicked();
- }
- });
- alert.setNegativeButton(this.getString(android.R.string.no),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.dismiss();
- setResult(RESULT_CANCELED);
- finish();
- }
- });
- alert.setCancelable(false);
- alert.show();
- } else {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
-
- /**
- * Returns user ids from the SectionView
- *
- * @param userIdsView
- * @return
- */
- private ArrayList<String> getUserIds(SectionView userIdsView) throws PgpGeneralException {
- ArrayList<String> userIds = new ArrayList<String>();
-
- ViewGroup userIdEditors = userIdsView.getEditors();
-
- boolean gotMainUserId = false;
- for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
- UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
- String userId;
- userId = editor.getValue();
-
- if (editor.isMainUserId()) {
- userIds.add(0, userId);
- gotMainUserId = true;
- } else {
- userIds.add(userId);
- }
- }
-
- if (userIds.size() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_a_user_id));
- }
-
- if (!gotMainUserId) {
- throw new PgpGeneralException(getString(R.string.error_main_user_id_must_not_be_empty));
- }
-
- return userIds;
- }
-
- /**
- * Returns keys from the SectionView
- *
- * @param keysView
- * @return
- */
- private ArrayList<UncachedSecretKey> getKeys(SectionView keysView) throws PgpGeneralException {
- ArrayList<UncachedSecretKey> keys = new ArrayList<UncachedSecretKey>();
-
- ViewGroup keyEditors = keysView.getEditors();
-
- if (keyEditors.getChildCount() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_master_key));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
- keys.add(editor.getValue());
- }
-
- return keys;
- }
-
- /**
- * Returns usage selections of keys from the SectionView
- *
- * @param keysView
- * @return
- */
- private ArrayList<Integer> getKeysUsages(SectionView keysView) throws PgpGeneralException {
- ArrayList<Integer> keysUsages = new ArrayList<Integer>();
-
- ViewGroup keyEditors = keysView.getEditors();
-
- if (keyEditors.getChildCount() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_master_key));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
- keysUsages.add(editor.getUsage());
- }
-
- return keysUsages;
- }
-
- private ArrayList<Calendar> getKeysExpiryDates(SectionView keysView) throws PgpGeneralException {
- ArrayList<Calendar> keysExpiryDates = new ArrayList<Calendar>();
-
- ViewGroup keyEditors = keysView.getEditors();
-
- if (keyEditors.getChildCount() == 0) {
- throw new PgpGeneralException(getString(R.string.error_key_needs_master_key));
- }
-
- for (int i = 0; i < keyEditors.getChildCount(); ++i) {
- KeyEditor editor = (KeyEditor) keyEditors.getChildAt(i);
- keysExpiryDates.add(editor.getExpiryDate());
- }
-
- return keysExpiryDates;
- }
-
- private void updatePassphraseButtonText() {
- mChangePassphrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
- : getString(R.string.btn_set_passphrase));
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index b41871a39..9083d1567 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -42,12 +42,13 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OperationResults;
+import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
@@ -167,8 +168,8 @@ public class EditKeyFragment extends LoaderFragment implements
try {
Uri secretUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
- WrappedSecretKeyRing keyRing =
- new ProviderHelper(getActivity()).getWrappedSecretKeyRing(secretUri);
+ CanonicalizedSecretKeyRing keyRing =
+ new ProviderHelper(getActivity()).getCanonicalizedSecretKeyRing(secretUri);
mSaveKeyringParcel = new SaveKeyringParcel(keyRing.getMasterKeyId(),
keyRing.getUncachedKeyRing().getFingerprint());
@@ -466,26 +467,30 @@ public class EditKeyFragment extends LoaderFragment implements
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- getActivity().finish();
- // TODO below
// get returned data bundle
Bundle returnData = message.getData();
if (returnData == null) {
return;
}
- final OperationResults.SaveKeyringResult result =
- returnData.getParcelable(KeychainIntentService.RESULT);
+ final OperationResults.EditKeyResult result =
+ returnData.getParcelable(EditKeyResult.EXTRA_RESULT);
if (result == null) {
return;
}
- // if good -> finish, return result to showkey and display there!
// if bad -> display here!
+ if (!result.success()) {
+ result.createNotify(getActivity()).show();
+ return;
+ }
-// result.displayNotify(ImportKeysActivity.this);
+ // if good -> finish, return result to showkey and display there!
+ Intent intent = new Intent();
+ intent.putExtra(EditKeyResult.EXTRA_RESULT, result);
+ getActivity().setResult(EditKeyActivity.RESULT_OK, intent);
+ getActivity().finish();
-// getActivity().finish();
}
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
index dc0510189..eb807792b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -31,11 +31,14 @@ import android.widget.Button;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.Vector;
@@ -195,22 +198,23 @@ public class EncryptAsymmetricFragment extends Fragment {
mMainUserIdRest.setText("");
} else {
// See if we can get a user_id from a unified query
- String[] userId;
try {
- userId = mProviderHelper.getCachedPublicKeyRing(
+ String[] userIdSplit = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(mSecretKeyId)).getSplitPrimaryUserIdWithFallback();
+
+ if (userIdSplit[0] != null) {
+ mMainUserId.setText(userIdSplit[0]);
+ } else {
+ mMainUserId.setText(R.string.user_id_no_name);
+ }
+ if (userIdSplit[1] != null) {
+ mMainUserIdRest.setText(userIdSplit[1]);
+ } else {
+ mMainUserIdRest.setText(getString(R.string.label_key_id) + ": "
+ + PgpKeyHelper.convertKeyIdToHex(mSecretKeyId));
+ }
} catch (PgpGeneralException e) {
- userId = null;
- }
- if (userId != null && userId[0] != null) {
- mMainUserId.setText(String.format("%#16x", Long.parseLong(userId[0])));
- } else {
- mMainUserId.setText(getResources().getString(R.string.user_id_no_name));
- }
- if (userId != null && userId[1] != null) {
- mMainUserIdRest.setText(userId[1]);
- } else {
- mMainUserIdRest.setText("");
+ Notify.showNotify(getActivity(), "Key not found! This is a bug!", Notify.Style.ERROR);
}
mSign.setChecked(true);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
index f5d89d186..345e38a0e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
@@ -37,8 +37,6 @@ import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Spinner;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper;
@@ -51,6 +49,7 @@ import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Choice;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.io.File;
@@ -58,7 +57,7 @@ public class EncryptFileFragment extends Fragment {
public static final String ARG_FILENAME = "filename";
public static final String ARG_ASCII_ARMOR = "ascii_armor";
- private static final int RESULT_CODE_FILE = 0x00007003;
+ private static final int REQUEST_CODE_FILE = 0x00007003;
private EncryptActivityInterface mEncryptInterface;
@@ -109,10 +108,10 @@ public class EncryptFileFragment extends Fragment {
mBrowse.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (Constants.KITKAT) {
- FileHelper.openDocument(EncryptFileFragment.this, mInputUri, "*/*", RESULT_CODE_FILE);
+ FileHelper.openDocument(EncryptFileFragment.this, mInputUri, "*/*", REQUEST_CODE_FILE);
} else {
FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*",
- RESULT_CODE_FILE);
+ REQUEST_CODE_FILE);
}
}
});
@@ -218,18 +217,18 @@ public class EncryptFileFragment extends Fragment {
}
if (mInputFilename.equals("")) {
- AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
return;
}
if (mInputUri == null && !mInputFilename.startsWith("content")) {
File file = new File(mInputFilename);
if (!file.exists() || !file.isFile()) {
- AppMsg.makeText(
+ Notify.showNotify(
getActivity(),
getString(R.string.error_message,
- getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
- .show();
+ getString(R.string.error_file_not_found)), Notify.Style.ERROR
+ );
return;
}
}
@@ -240,13 +239,13 @@ public class EncryptFileFragment extends Fragment {
boolean gotPassphrase = (mEncryptInterface.getPassphrase() != null
&& mEncryptInterface.getPassphrase().length() != 0);
if (!gotPassphrase) {
- AppMsg.makeText(getActivity(), R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
- .show();
+ Notify.showNotify(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
+ ;
return;
}
if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
- AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR);
return;
}
} else {
@@ -256,13 +255,13 @@ public class EncryptFileFragment extends Fragment {
&& mEncryptInterface.getEncryptionKeys().length > 0);
if (!gotEncryptionKeys) {
- AppMsg.makeText(getActivity(), R.string.select_encryption_key, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR);
return;
}
if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
- AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.select_encryption_or_signature_key,
+ Notify.Style.ERROR);
return;
}
@@ -345,8 +344,8 @@ public class EncryptFileFragment extends Fragment {
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- AppMsg.makeText(getActivity(), R.string.encrypt_sign_successful,
- AppMsg.STYLE_INFO).show();
+ Notify.showNotify(getActivity(), R.string.encrypt_sign_successful,
+ Notify.Style.INFO);
if (mDeleteAfter.isChecked()) {
// Create and show dialog to delete original file
@@ -390,7 +389,7 @@ public class EncryptFileFragment extends Fragment {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
- case RESULT_CODE_FILE: {
+ case REQUEST_CODE_FILE: {
if (resultCode == Activity.RESULT_OK && data != null) {
if (Constants.KITKAT) {
mInputUri = data.getData();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
index 8a6103b16..e1760b4ed 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
@@ -30,8 +30,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
@@ -41,6 +39,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
public class EncryptMessageFragment extends Fragment {
public static final String ARG_TEXT = "text";
@@ -126,13 +125,12 @@ public class EncryptMessageFragment extends Fragment {
boolean gotPassphrase = (mEncryptInterface.getPassphrase() != null
&& mEncryptInterface.getPassphrase().length() != 0);
if (!gotPassphrase) {
- AppMsg.makeText(getActivity(), R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
- .show();
+ Notify.showNotify(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
return;
}
if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
- AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR);
return;
}
@@ -143,8 +141,8 @@ public class EncryptMessageFragment extends Fragment {
&& mEncryptInterface.getEncryptionKeys().length > 0);
if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
- AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.select_encryption_or_signature_key,
+ Notify.Style.ERROR);
return;
}
@@ -226,9 +224,8 @@ public class EncryptMessageFragment extends Fragment {
if (toClipboard) {
ClipboardReflection.copyToClipboard(getActivity(), output);
- AppMsg.makeText(getActivity(),
- R.string.encrypt_sign_clipboard_successful, AppMsg.STYLE_INFO)
- .show();
+ Notify.showNotify(getActivity(),
+ R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
} else {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
index aa4120d2c..5f3f170a1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
@@ -22,15 +22,19 @@ import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.Window;
-import android.widget.Button;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.util.Log;
public class FirstTimeActivity extends ActionBarActivity {
- Button mCreateKey;
- Button mImportKey;
- Button mSkipSetup;
+ View mCreateKey;
+ View mImportKey;
+ View mSkipSetup;
+
+ public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -40,16 +44,14 @@ public class FirstTimeActivity extends ActionBarActivity {
setContentView(R.layout.first_time_activity);
- mCreateKey = (Button) findViewById(R.id.first_time_create_key);
- mImportKey = (Button) findViewById(R.id.first_time_import_key);
- mSkipSetup = (Button) findViewById(R.id.first_time_cancel);
+ mCreateKey = findViewById(R.id.first_time_create_key);
+ mImportKey = findViewById(R.id.first_time_import_key);
+ mSkipSetup = findViewById(R.id.first_time_cancel);
mSkipSetup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- Intent intent = new Intent(FirstTimeActivity.this, KeyListActivity.class);
- startActivity(intent);
- finish();
+ finishSetup();
}
});
@@ -58,8 +60,7 @@ public class FirstTimeActivity extends ActionBarActivity {
public void onClick(View v) {
Intent intent = new Intent(FirstTimeActivity.this, ImportKeysActivity.class);
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
- startActivity(intent);
- finish();
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
}
});
@@ -67,11 +68,30 @@ public class FirstTimeActivity extends ActionBarActivity {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstTimeActivity.this, CreateKeyActivity.class);
- startActivity(intent);
- finish();
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
}
});
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) {
+ if (resultCode == RESULT_OK) {
+ finishSetup();
+ }
+ } else {
+ Log.e(Constants.TAG, "No valid request code!");
+ }
+ }
+
+ private void finishSetup() {
+ Preferences prefs = Preferences.getPreferences(this);
+ prefs.setFirstTime(false);
+ Intent intent = new Intent(FirstTimeActivity.this, KeyListActivity.class);
+ startActivity(intent);
+ finish();
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 3ff3b56bf..dbc557f9a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -40,17 +40,19 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.keyimport.FileImportCache;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.OperationResults.ImportResult;
+import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
@@ -98,6 +100,7 @@ public class ImportKeysActivity extends ActionBarActivity {
public static final int VIEW_PAGER_HEIGHT = 64; // dp
+ private static final int ALL_TABS = -1;
private static final int TAB_KEYSERVER = 0;
private static final int TAB_QR_CODE = 1;
private static final int TAB_FILE = 2;
@@ -150,7 +153,7 @@ public class ImportKeysActivity extends ActionBarActivity {
}
Bundle serverBundle = null;
- boolean serverOnly = false;
+ int showTabOnly = ALL_TABS;
if (ACTION_IMPORT_KEY.equals(action)) {
/* Keychain's own Actions */
@@ -214,7 +217,7 @@ public class ImportKeysActivity extends ActionBarActivity {
serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query);
serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true);
// display server tab only
- serverOnly = true;
+ showTabOnly = TAB_KEYSERVER;
mSwitchToTab = TAB_KEYSERVER;
// action: search immediately
@@ -227,13 +230,20 @@ public class ImportKeysActivity extends ActionBarActivity {
);
return;
}
- } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)
- || ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(action)) {
+ } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
// NOTE: this only displays the appropriate fragment, no actions are taken
mSwitchToTab = TAB_FILE;
// no immediate actions!
startListFragment(savedInstanceState, null, null, null);
+ } else if (ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(action)) {
+ // NOTE: this only displays the appropriate fragment, no actions are taken
+ mSwitchToTab = TAB_FILE;
+ // display file tab only
+ showTabOnly = TAB_FILE;
+
+ // no immediate actions!
+ startListFragment(savedInstanceState, null, null, null);
} else if (ACTION_IMPORT_KEY_FROM_QR_CODE.equals(action)) {
// also exposed in AndroidManifest
@@ -259,10 +269,10 @@ public class ImportKeysActivity extends ActionBarActivity {
startListFragment(savedInstanceState, null, null, null);
}
- initTabs(serverBundle, serverOnly);
+ initTabs(serverBundle, showTabOnly);
}
- private void initTabs(Bundle serverBundle, boolean serverOnly) {
+ private void initTabs(Bundle serverBundle, int showTabOnly) {
mTabsAdapter = new PagerTabStripAdapter(this);
mViewPager.setAdapter(mTabsAdapter);
mSlidingTabLayout.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@@ -285,15 +295,34 @@ public class ImportKeysActivity extends ActionBarActivity {
}
});
- mTabsAdapter.addTab(ImportKeysServerFragment.class,
- serverBundle, getString(R.string.import_tab_keyserver));
- if (!serverOnly) {
- mTabsAdapter.addTab(ImportKeysQrCodeFragment.class,
- null, getString(R.string.import_tab_qr_code));
- mTabsAdapter.addTab(ImportKeysFileFragment.class,
- null, getString(R.string.import_tab_direct));
- mTabsAdapter.addTab(ImportKeysKeybaseFragment.class,
- null, getString(R.string.import_tab_keybase));
+ switch (showTabOnly) {
+ case ALL_TABS:
+ // show all tabs
+ mTabsAdapter.addTab(ImportKeysServerFragment.class,
+ serverBundle, getString(R.string.import_tab_keyserver));
+ mTabsAdapter.addTab(ImportKeysQrCodeFragment.class,
+ null, getString(R.string.import_tab_qr_code));
+ mTabsAdapter.addTab(ImportKeysFileFragment.class,
+ null, getString(R.string.import_tab_direct));
+ mTabsAdapter.addTab(ImportKeysKeybaseFragment.class,
+ null, getString(R.string.import_tab_keybase));
+ break;
+ case TAB_KEYSERVER:
+ mTabsAdapter.addTab(ImportKeysServerFragment.class,
+ serverBundle, getString(R.string.import_tab_keyserver));
+ break;
+ case TAB_QR_CODE:
+ mTabsAdapter.addTab(ImportKeysQrCodeFragment.class,
+ null, getString(R.string.import_tab_qr_code));
+ break;
+ case TAB_FILE:
+ mTabsAdapter.addTab(ImportKeysFileFragment.class,
+ null, getString(R.string.import_tab_direct));
+ break;
+ case TAB_KEYBASE:
+ mTabsAdapter.addTab(ImportKeysKeybaseFragment.class,
+ null, getString(R.string.import_tab_keybase));
+ break;
}
// update layout after operations
@@ -354,9 +383,8 @@ public class ImportKeysActivity extends ActionBarActivity {
ImportKeysServerFragment f = (ImportKeysServerFragment)
getActiveFragment(mViewPager, TAB_KEYSERVER);
- // TODO: Currently it simply uses keyserver nr 0
- String keyserver = Preferences.getPreferences(ImportKeysActivity.this)
- .getKeyServers()[0];
+ // ask favorite keyserver
+ String keyserver = Preferences.getPreferences(ImportKeysActivity.this).getKeyServers()[0];
// set fields of ImportKeysServerFragment
f.setQueryAndKeyserver(query, keyserver);
@@ -427,15 +455,15 @@ public class ImportKeysActivity extends ActionBarActivity {
if (returnData == null) {
return;
}
- final ImportResult result =
- returnData.getParcelable(KeychainIntentService.RESULT);
+ final ImportKeyResult result =
+ returnData.getParcelable(KeychainIntentService.RESULT_IMPORT);
if (result == null) {
return;
}
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())) {
Intent intent = new Intent();
- intent.putExtra(EXTRA_RESULT, result);
+ intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
ImportKeysActivity.this.setResult(RESULT_OK, intent);
ImportKeysActivity.this.finish();
return;
@@ -451,7 +479,7 @@ public class ImportKeysActivity extends ActionBarActivity {
return;
}
- result.displayNotify(ImportKeysActivity.this);
+ result.createNotify(ImportKeysActivity.this).show();
}
}
};
@@ -470,19 +498,29 @@ public class ImportKeysActivity extends ActionBarActivity {
// get DATA from selected key entries
ArrayList<ParcelableKeyRing> selectedEntries = mListFragment.getSelectedData();
- data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries);
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+ // instead of given the entries by Intent extra, cache them into a file
+ // to prevent Java Binder problems on heavy imports
+ // read FileImportCache for more info.
+ try {
+ FileImportCache cache = new FileImportCache(this);
+ cache.writeCache(selectedEntries);
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // show progress dialog
- saveHandler.showProgressDialog(this);
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
- // start service with intent
- startService(intent);
+ // show progress dialog
+ saveHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Problem writing cache file", e);
+ Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR);
+ }
} else if (ls instanceof ImportKeysListFragment.KeyserverLoaderState) {
ImportKeysListFragment.KeyserverLoaderState sls = (ImportKeysListFragment.KeyserverLoaderState) ls;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index 9a39b6cc3..fde0f5f23 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -35,7 +35,6 @@ import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListKeybaseLoader;
@@ -288,13 +287,13 @@ public class ImportKeysListFragment extends ListFragment implements
if (error == null) {
// No error
mCachedKeyData = ((ImportKeysListLoader) loader).getParcelableRings();
- } else if (error instanceof ImportKeysListLoader.FileHasNoContent) {
- Notify.showNotify(getActivity(), R.string.error_import_file_no_content, Notify.Style.ERROR);
- } else if (error instanceof ImportKeysListLoader.NonPgpPart) {
+ } else if (error instanceof ImportKeysListLoader.NoValidKeysException) {
+ Notify.showNotify(getActivity(), R.string.error_import_no_valid_keys, Notify.Style.ERROR);
+ } else if (error instanceof ImportKeysListLoader.NonPgpPartException) {
Notify.showNotify(getActivity(),
- ((ImportKeysListLoader.NonPgpPart) error).getCount() + " " + getResources().
+ ((ImportKeysListLoader.NonPgpPartException) error).getCount() + " " + getResources().
getQuantityString(R.plurals.error_import_non_pgp_part,
- ((ImportKeysListLoader.NonPgpPart) error).getCount()),
+ ((ImportKeysListLoader.NonPgpPartException) error).getCount()),
Notify.Style.OK
);
} else {
@@ -308,9 +307,11 @@ public class ImportKeysListFragment extends ListFragment implements
if (error == null) {
// No error
} else if (error instanceof Keyserver.QueryTooShortException) {
- Notify.showNotify(getActivity(), R.string.error_keyserver_insufficient_query, Notify.Style.ERROR);
+ Notify.showNotify(getActivity(), R.string.error_query_too_short, Notify.Style.ERROR);
} else if (error instanceof Keyserver.TooManyResponsesException) {
- Notify.showNotify(getActivity(), R.string.error_keyserver_too_many_responses, Notify.Style.ERROR);
+ Notify.showNotify(getActivity(), R.string.error_too_many_responses, Notify.Style.ERROR);
+ } else if (error instanceof Keyserver.QueryTooShortOrTooManyResponsesException) {
+ Notify.showNotify(getActivity(), R.string.error_too_short_or_too_many_responses, Notify.Style.ERROR);
} else if (error instanceof Keyserver.QueryFailedException) {
Log.d(Constants.TAG,
"Unrecoverable keyserver query error: " + error.getLocalizedMessage());
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 9cc623c83..50ff5c753 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -17,29 +17,19 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
import android.view.Menu;
import android.view.MenuItem;
-import com.devspark.appmsg.AppMsg;
-
-import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Constants.choice.algorithm;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.io.IOException;
@@ -51,11 +41,10 @@ public class KeyListActivity extends DrawerActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ // if this is the first time show first time activity
Preferences prefs = Preferences.getPreferences(this);
- if (prefs.getFirstTime()) {
- prefs.setFirstTime(false);
- Intent intent = new Intent(this, FirstTimeActivity.class);
- startActivity(intent);
+ if (prefs.isFirstTime()) {
+ startActivity(new Intent(this, FirstTimeActivity.class));
finish();
return;
}
@@ -85,7 +74,7 @@ public class KeyListActivity extends DrawerActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_key_list_import:
+ case R.id.menu_key_list_add:
importKeys();
return true;
@@ -93,6 +82,12 @@ public class KeyListActivity extends DrawerActivity {
createKey();
return true;
+ case R.id.menu_key_list_import_existing_key:
+ Intent intentImportExisting = new Intent(this, ImportKeysActivity.class);
+ intentImportExisting.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
+ startActivityForResult(intentImportExisting, 0);
+ return true;
+
case R.id.menu_key_list_export:
mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);
return true;
@@ -100,25 +95,27 @@ public class KeyListActivity extends DrawerActivity {
case R.id.menu_key_list_debug_read:
try {
KeychainDatabase.debugRead(this);
- AppMsg.makeText(this, "Restored from backup", AppMsg.STYLE_CONFIRM).show();
+ Notify.showNotify(this, "Restored Notify.Style backup", Notify.Style.INFO);
getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
} catch (IOException e) {
Log.e(Constants.TAG, "IO Error", e);
- AppMsg.makeText(this, "IO Error: " + e.getMessage(), AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);
}
return true;
case R.id.menu_key_list_debug_write:
try {
KeychainDatabase.debugWrite(this);
- AppMsg.makeText(this, "Backup successful", AppMsg.STYLE_CONFIRM).show();
- } catch (IOException e) {
+ Notify.showNotify(this, "Backup Notify.Style", Notify.Style.INFO);
+ } catch(IOException e) {
Log.e(Constants.TAG, "IO Error", e);
- AppMsg.makeText(this, "IO Error: " + e.getMessage(), AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(this, "IO Notify.Style: " + e.getMessage(), Notify.Style.ERROR);
}
return true;
case R.id.menu_key_list_debug_first_time:
+ Preferences prefs = Preferences.getPreferences(this);
+ prefs.setFirstTime(true);
Intent intent = new Intent(this, FirstTimeActivity.class);
startActivity(intent);
finish();
@@ -139,4 +136,4 @@ public class KeyListActivity extends DrawerActivity {
startActivity(intent);
}
-} \ No newline at end of file
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index 9d0a80406..aa17aea3d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -46,14 +47,11 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AbsListView.MultiChoiceModeListener;
import android.widget.AdapterView;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Button;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
@@ -62,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.util.Highlighter;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.Date;
import java.util.HashMap;
@@ -86,6 +85,7 @@ public class KeyListFragment extends LoaderFragment
private Button mButtonEmptyCreate;
private Button mButtonEmptyImport;
+ public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;
/**
* Load custom layout with StickyListView from library
@@ -104,11 +104,8 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onClick(View v) {
- Intent intent = new Intent(getActivity(), EditKeyActivityOld.class);
- intent.setAction(EditKeyActivityOld.ACTION_CREATE_KEY);
- intent.putExtra(EditKeyActivityOld.EXTRA_GENERATE_DEFAULT_KEYS, true);
- intent.putExtra(EditKeyActivityOld.EXTRA_USER_IDS, ""); // show user id view
- startActivityForResult(intent, 0);
+ Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
}
});
mButtonEmptyImport = (Button) view.findViewById(R.id.key_list_empty_button_import);
@@ -117,8 +114,8 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE);
- startActivityForResult(intent, 0);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
+ startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
}
});
@@ -339,8 +336,8 @@ public class KeyListFragment extends LoaderFragment
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
// Can only work on singular secret keys
if(hasSecret && masterKeyIds.length > 1) {
- AppMsg.makeText(getActivity(), R.string.secret_cannot_multiple,
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.secret_cannot_multiple,
+ Notify.Style.ERROR);
return;
}
@@ -437,9 +434,7 @@ public class KeyListFragment extends LoaderFragment
private class ItemViewHolder {
TextView mMainUserId;
TextView mMainUserIdRest;
- FrameLayout mStatusLayout;
- TextView mRevoked;
- ImageView mVerified;
+ ImageView mStatus;
}
@Override
@@ -448,9 +443,7 @@ public class KeyListFragment extends LoaderFragment
ItemViewHolder holder = new ItemViewHolder();
holder.mMainUserId = (TextView) view.findViewById(R.id.mainUserId);
holder.mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
- holder.mStatusLayout = (FrameLayout) view.findViewById(R.id.status_layout);
- holder.mRevoked = (TextView) view.findViewById(R.id.revoked);
- holder.mVerified = (ImageView) view.findViewById(R.id.verified);
+ holder.mStatus = (ImageView) view.findViewById(R.id.status_image);
view.setTag(holder);
return view;
}
@@ -482,30 +475,40 @@ public class KeyListFragment extends LoaderFragment
}
}
- { // set edit button and revoked info, specific by key type
-
- if (cursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) {
- // this is a secret key
- h.mStatusLayout.setVisibility(View.VISIBLE);
- h.mRevoked.setVisibility(View.GONE);
- h.mVerified.setVisibility(View.GONE);
- } else {
- // this is a public key - show if it's revoked
-
- boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
- boolean isExpired = !cursor.isNull(INDEX_EXPIRY)
- && new Date(cursor.getLong(INDEX_EXPIRY)*1000).before(new Date());
- if(isRevoked || isExpired) {
- h.mStatusLayout.setVisibility(View.VISIBLE);
- h.mRevoked.setVisibility(View.VISIBLE);
- h.mVerified.setVisibility(View.GONE);
- h.mRevoked.setText(isRevoked ? R.string.revoked : R.string.expired);
+ { // set edit button and status, specific by key type
+
+ boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ boolean isExpired = !cursor.isNull(INDEX_EXPIRY)
+ && new Date(cursor.getLong(INDEX_EXPIRY)*1000).before(new Date());
+ boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
+
+ // Note: order is important!
+ if (isRevoked) {
+ h.mStatus.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
+ h.mStatus.setColorFilter(getResources().getColor(R.color.android_red_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ h.mStatus.setVisibility(View.VISIBLE);
+ } else if (isExpired) {
+ h.mStatus.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_expired_cutout));
+ h.mStatus.setColorFilter(getResources().getColor(R.color.android_orange_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ h.mStatus.setVisibility(View.VISIBLE);
+ } else if (isVerified) {
+ if (cursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) {
+ // this is a secret key
+ h.mStatus.setVisibility(View.GONE);
} else {
- boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
- h.mStatusLayout.setVisibility(isVerified ? View.VISIBLE : View.GONE);
- h.mRevoked.setVisibility(View.GONE);
- h.mVerified.setVisibility(isVerified ? View.VISIBLE : View.GONE);
+ // this is a public key - show if it's verified
+ h.mStatus.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_verified_cutout));
+ h.mStatus.setColorFilter(getResources().getColor(R.color.android_green_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ h.mStatus.setVisibility(View.VISIBLE);
}
+ } else {
+ h.mStatus.setVisibility(View.GONE);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
index 75c967c60..43de6774b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
@@ -178,9 +178,11 @@ public class LogDisplayFragment extends ListFragment implements OnTouchListener
if (entry.mParameters != null && entry.mParameters.length > 0
&& entry.mParameters[0] instanceof Integer) {
ih.mText.setText(getResources().getQuantityString(entry.mType.getMsgId(),
- (Integer) entry.mParameters[0], entry.mParameters));
+ (Integer) entry.mParameters[0],
+ entry.mParameters));
} else {
- ih.mText.setText(getResources().getString(entry.mType.getMsgId(), entry.mParameters));
+ ih.mText.setText(getResources().getString(entry.mType.getMsgId(),
+ entry.mParameters));
}
ih.mText.setTextColor(entry.mLevel == LogLevel.DEBUG ? Color.GRAY : Color.BLACK);
convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java
new file mode 100644
index 000000000..a906dfa97
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeActivity.java
@@ -0,0 +1,114 @@
+/*
+ * 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 android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.ActionBarActivity;
+import android.view.View;
+import android.widget.ImageView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ActionBarHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
+import org.sufficientlysecure.keychain.util.Notify.Style;
+import org.sufficientlysecure.keychain.util.QrCodeUtils;
+
+public class QrCodeActivity extends ActionBarActivity {
+
+ private ImageView mFingerprintQrCode;
+
+ private static final int QR_CODE_SIZE = 1000;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Inflate a "Done" custom action bar
+ ActionBarHelper.setOneButtonView(getSupportActionBar(),
+ R.string.btn_okay, R.drawable.ic_action_done,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // "Done"
+ finish();
+ }
+ }
+ );
+
+ setContentView(R.layout.qr_code_activity);
+
+ Uri dataUri = getIntent().getData();
+ if (dataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ finish();
+ return;
+ }
+
+ mFingerprintQrCode = (ImageView) findViewById(R.id.qr_code_image);
+
+ mFingerprintQrCode.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+
+ ProviderHelper providerHelper = new ProviderHelper(this);
+ try {
+ byte[] blob = (byte[]) providerHelper.getGenericData(
+ KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ if (blob == null) {
+ Log.e(Constants.TAG, "key not found!");
+ Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
+ finish();
+ }
+
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
+ String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ mFingerprintQrCode.setImageBitmap(QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE));
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // custom activity transition to get zoom in effect
+ this.overridePendingTransition(R.anim.qr_code_zoom_enter, android.R.anim.fade_out);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ // custom activity transition to get zoom out effect
+ this.overridePendingTransition(0, R.anim.qr_code_zoom_exit);
+ }
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index dbd1b7507..57bfc7bd5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.Log;
@@ -92,7 +93,7 @@ public class UploadKeyActivity extends ActionBarActivity {
intent.setAction(KeychainIntentService.ACTION_UPLOAD_KEYRING);
// set data uri as path to keyring
- Uri blobUri = KeychainContract.KeyRingData.buildPublicKeyRingUri(mDataUri);
+ Uri blobUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
intent.setData(blobUri);
// fill values for this action
@@ -105,7 +106,7 @@ public class UploadKeyActivity extends ActionBarActivity {
// Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
+ getString(R.string.progress_uploading), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index cfdea0611..5201b5df8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -35,7 +35,7 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
@@ -143,16 +143,16 @@ public class ViewCertActivity extends ActionBarActivity
try {
ProviderHelper providerHelper = new ProviderHelper(this);
- WrappedPublicKeyRing signeeRing =
- providerHelper.getWrappedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
- WrappedPublicKeyRing signerRing =
- providerHelper.getWrappedPublicKeyRing(sig.getKeyId());
+ CanonicalizedPublicKeyRing signeeRing =
+ providerHelper.getCanonicalizedPublicKeyRing(data.getLong(INDEX_MASTER_KEY_ID));
+ CanonicalizedPublicKeyRing signerRing =
+ providerHelper.getCanonicalizedPublicKeyRing(sig.getKeyId());
try {
sig.init(signerRing.getPublicKey());
if (sig.verifySignature(signeeRing.getPublicKey(), signeeUid)) {
mStatus.setText(R.string.cert_verify_ok);
- mStatus.setTextColor(getResources().getColor(R.color.result_green));
+ mStatus.setTextColor(getResources().getColor(R.color.android_green_light));
} else {
mStatus.setText(R.string.cert_verify_failed);
mStatus.setTextColor(getResources().getColor(R.color.alert));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index a9cd0976b..44a51a75f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -19,9 +19,9 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
@@ -43,8 +43,9 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
-
-import com.devspark.appmsg.AppMsg;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -54,11 +55,12 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.OperationResults.ImportResult;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout.TabColorizer;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.Date;
import java.util.HashMap;
@@ -81,11 +83,11 @@ public class ViewKeyActivity extends ActionBarActivity implements
private ViewPager mViewPager;
private SlidingTabLayout mSlidingTabLayout;
private PagerTabStripAdapter mTabsAdapter;
- private View mStatusDivider;
- private View mStatusRevoked;
- private View mStatusExpired;
- public static final int REQUEST_CODE_LOOKUP_KEY = 0x00007006;
+ private LinearLayout mStatusLayout;
+ private TextView mStatusText;
+ private ImageView mStatusImage;
+ private View mStatusDivider;
// NFC
private NfcAdapter mNfcAdapter;
@@ -115,9 +117,10 @@ public class ViewKeyActivity extends ActionBarActivity implements
setContentView(R.layout.view_key_activity);
- mStatusDivider = findViewById(R.id.status_divider);
- mStatusRevoked = findViewById(R.id.view_key_revoked);
- mStatusExpired = findViewById(R.id.view_key_expired);
+ mStatusLayout = (LinearLayout) findViewById(R.id.view_key_status_layout);
+ mStatusText = (TextView) findViewById(R.id.view_key_status_text);
+ mStatusImage = (ImageView) findViewById(R.id.view_key_status_image);
+ mStatusDivider = findViewById(R.id.view_key_status_divider);
mViewPager = (ViewPager) findViewById(R.id.view_key_pager);
mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.view_key_sliding_tab_layout);
@@ -140,20 +143,27 @@ public class ViewKeyActivity extends ActionBarActivity implements
switchToTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
}
- Uri dataUri = getDataUri();
- if (dataUri == null) {
- Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
+ mDataUri = getIntent().getData();
+ if (mDataUri == null) {
+ Log.e(Constants.TAG, "Data missing. Should be uri of key!");
finish();
return;
}
+ if (mDataUri.getHost().equals(ContactsContract.AUTHORITY)) {
+ mDataUri = ContactHelper.dataUriFromContactUri(this, mDataUri);
+ }
+
+ Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
- loadData(dataUri);
+ // Prepare the loaders. Either re-connect with an existing ones,
+ // or start new ones.
+ getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
- initNfc(dataUri);
+ initNfc(mDataUri);
mShowAdvancedTabs = false;
- initTabs(dataUri);
+ initTabs(mDataUri);
// switch to tab selected by extra
mViewPager.setCurrentItem(switchToTab);
@@ -230,24 +240,6 @@ public class ViewKeyActivity extends ActionBarActivity implements
mSlidingTabLayout.setViewPager(mViewPager);
}
- private Uri getDataUri() {
- Uri dataUri = getIntent().getData();
- if (dataUri != null && dataUri.getHost().equals(ContactsContract.AUTHORITY)) {
- dataUri = ContactHelper.dataUriFromContactUri(this, dataUri);
- }
- return dataUri;
- }
-
- private void loadData(Uri dataUri) {
- mDataUri = dataUri;
-
- Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
-
- // Prepare the loaders. Either re-connect with an existing ones,
- // or start new ones.
- getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
@@ -268,14 +260,6 @@ public class ViewKeyActivity extends ActionBarActivity implements
startActivity(homeIntent);
return true;
}
- case R.id.menu_key_view_update: {
- updateFromKeyserver(mDataUri, mProviderHelper);
- return true;
- }
- case R.id.menu_key_view_export_keyserver: {
- uploadToKeyserver(mDataUri);
- return true;
- }
case R.id.menu_key_view_export_file: {
exportToFile(mDataUri, mExportHelper, mProviderHelper);
return true;
@@ -295,7 +279,7 @@ public class ViewKeyActivity extends ActionBarActivity implements
}
}
} catch (ProviderHelper.NotFoundException e) {
- AppMsg.makeText(this, R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
Log.e(Constants.TAG, "Key not found", e);
}
return super.onOptionsItemSelected(item);
@@ -317,26 +301,6 @@ public class ViewKeyActivity extends ActionBarActivity implements
);
}
- private void uploadToKeyserver(Uri dataUri) throws ProviderHelper.NotFoundException {
- Intent uploadIntent = new Intent(this, UploadKeyActivity.class);
- uploadIntent.setData(dataUri);
- startActivityForResult(uploadIntent, 0);
- }
-
- private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper)
- throws ProviderHelper.NotFoundException {
- byte[] blob = (byte[]) providerHelper.getGenericData(
- KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
- KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
-
- Intent queryIntent = new Intent(this, ImportKeysActivity.class);
- queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT);
- queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
-
- startActivityForResult(queryIntent, REQUEST_CODE_LOOKUP_KEY);
- }
-
private void deleteKey(Uri dataUri, ExportHelper exportHelper) {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@@ -352,22 +316,11 @@ public class ViewKeyActivity extends ActionBarActivity implements
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case REQUEST_CODE_LOOKUP_KEY: {
- if (resultCode == Activity.RESULT_OK) {
- ImportResult result = data.getParcelableExtra(ImportKeysActivity.EXTRA_RESULT);
- if (result != null) {
- result.displayNotify(this);
- }
- }
- break;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
-
- break;
- }
+ if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) {
+ OperationResultParcel result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT);
+ result.createNotify(this).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
}
}
@@ -455,8 +408,8 @@ public class ViewKeyActivity extends ActionBarActivity implements
public void handleMessage(Message msg) {
switch (msg.what) {
case NFC_SENT:
- AppMsg.makeText(ViewKeyActivity.this, R.string.nfc_successful,
- AppMsg.STYLE_INFO).show();
+ Notify.showNotify(
+ ViewKeyActivity.this, R.string.nfc_successful, Notify.Style.INFO);
break;
}
}
@@ -515,22 +468,32 @@ public class ViewKeyActivity extends ActionBarActivity implements
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(masterKeyId);
getSupportActionBar().setSubtitle(keyIdStr);
- // If this key is revoked, it cannot be used for anything!
- if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {
+ boolean isRevoked = data.getInt(INDEX_UNIFIED_IS_REVOKED) > 0;
+ boolean isExpired = !data.isNull(INDEX_UNIFIED_EXPIRY)
+ && new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000).before(new Date());
+
+ // Note: order is important
+ if (isRevoked) {
+ mStatusText.setText(R.string.view_key_revoked);
+ mStatusText.setTextColor(getResources().getColor(R.color.android_red_dark));
+ mStatusImage.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_revoked_cutout));
+ mStatusImage.setColorFilter(getResources().getColor(R.color.android_red_dark),
+ PorterDuff.Mode.SRC_ATOP);
+ mStatusDivider.setVisibility(View.VISIBLE);
+ mStatusLayout.setVisibility(View.VISIBLE);
+ } else if (isExpired) {
+ mStatusText.setText(R.string.view_key_expired);
+ mStatusText.setTextColor(getResources().getColor(R.color.android_orange_dark));
+ mStatusImage.setImageDrawable(
+ getResources().getDrawable(R.drawable.status_signature_expired_cutout));
+ mStatusImage.setColorFilter(getResources().getColor(R.color.android_orange_dark),
+ PorterDuff.Mode.SRC_ATOP);
mStatusDivider.setVisibility(View.VISIBLE);
- mStatusRevoked.setVisibility(View.VISIBLE);
- mStatusExpired.setVisibility(View.GONE);
+ mStatusLayout.setVisibility(View.VISIBLE);
} else {
- mStatusRevoked.setVisibility(View.GONE);
-
- Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
- if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {
- mStatusDivider.setVisibility(View.VISIBLE);
- mStatusExpired.setVisibility(View.VISIBLE);
- } else {
- mStatusDivider.setVisibility(View.GONE);
- mStatusExpired.setVisibility(View.GONE);
- }
+ mStatusDivider.setVisibility(View.GONE);
+ mStatusLayout.setVisibility(View.GONE);
}
break;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index f0636cf2c..370a7312f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
@@ -27,19 +28,21 @@ import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.ListView;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
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.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import java.util.Date;
@@ -52,7 +55,9 @@ public class ViewKeyMainFragment extends LoaderFragment implements
private View mActionEditDivider;
private View mActionEncrypt;
private View mActionCertify;
- private View mActionCertifyDivider;
+ private View mActionCertifyText;
+ private ImageView mActionCertifyImage;
+ private View mActionUpdate;
private ListView mUserIds;
@@ -76,7 +81,12 @@ public class ViewKeyMainFragment extends LoaderFragment implements
mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider);
mActionEncrypt = view.findViewById(R.id.view_key_action_encrypt);
mActionCertify = view.findViewById(R.id.view_key_action_certify);
- mActionCertifyDivider = view.findViewById(R.id.view_key_action_certify_divider);
+ mActionCertifyText = view.findViewById(R.id.view_key_action_certify_text);
+ mActionCertifyImage = (ImageView) view.findViewById(R.id.view_key_action_certify_image);
+ // make certify image gray, like action icons
+ mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
+ PorterDuff.Mode.SRC_IN);
+ mActionUpdate = view.findViewById(R.id.view_key_action_update);
return root;
}
@@ -116,6 +126,15 @@ public class ViewKeyMainFragment extends LoaderFragment implements
editKey(mDataUri);
}
});
+ mActionUpdate.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ try {
+ updateFromKeyserver(mDataUri, new ProviderHelper(getActivity()));
+ } catch (NotFoundException e) {
+ Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR);
+ }
+ }
+ });
mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0);
mUserIds.setAdapter(mUserIdsAdapter);
@@ -182,6 +201,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements
if (data.getInt(INDEX_UNIFIED_IS_REVOKED) != 0) {
mActionEdit.setEnabled(false);
mActionCertify.setEnabled(false);
+ mActionCertifyText.setEnabled(false);
mActionEncrypt.setEnabled(false);
} else {
mActionEdit.setEnabled(true);
@@ -189,9 +209,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements
Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
if (!data.isNull(INDEX_UNIFIED_EXPIRY) && expiryDate.before(new Date())) {
mActionCertify.setEnabled(false);
+ mActionCertifyText.setEnabled(false);
mActionEncrypt.setEnabled(false);
} else {
mActionCertify.setEnabled(true);
+ mActionCertifyText.setEnabled(true);
mActionEncrypt.setEnabled(true);
}
}
@@ -225,7 +247,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements
private void encrypt(Uri dataUri) {
// If there is no encryption key, don't bother.
if (!mHasEncrypt) {
- AppMsg.makeText(getActivity(), R.string.error_no_encrypt_subkey, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_no_encrypt_subkey, Notify.Style.ERROR);
return;
}
try {
@@ -243,18 +265,30 @@ public class ViewKeyMainFragment extends LoaderFragment implements
}
}
+ private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper)
+ throws ProviderHelper.NotFoundException {
+ byte[] blob = (byte[]) providerHelper.getGenericData(
+ KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
+
+ Intent queryIntent = new Intent(getActivity(), ImportKeysActivity.class);
+ queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT);
+ queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
+
+ startActivityForResult(queryIntent, 0);
+ }
+
private void certify(Uri dataUri) {
Intent signIntent = new Intent(getActivity(), CertifyKeyActivity.class);
signIntent.setData(dataUri);
- startActivity(signIntent);
+ startActivityForResult(signIntent, 0);
}
private void editKey(Uri dataUri) {
Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri));
-// editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
-// startActivityForResult(editIntent, 0);
- startActivity(editIntent);
+ startActivityForResult(editIntent, 0);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
index 52b573f47..54ab76464 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyShareFragment.java
@@ -20,7 +20,14 @@ package org.sufficientlysecure.keychain.ui;
import android.annotation.TargetApi;
import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.TransitionDrawable;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
@@ -33,8 +40,6 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
-import com.devspark.appmsg.AppMsg;
-
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
@@ -44,9 +49,10 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.Notify;
import org.sufficientlysecure.keychain.util.QrCodeUtils;
import java.io.IOException;
@@ -65,6 +71,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
private View mKeyClipboardButton;
private View mNfcHelpButton;
private View mNfcPrefsButton;
+ private View mKeyUploadButton;
ProviderHelper mProviderHelper;
@@ -89,6 +96,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
mNfcHelpButton = view.findViewById(R.id.view_key_action_nfc_help);
mNfcPrefsButton = view.findViewById(R.id.view_key_action_nfc_prefs);
+ mKeyUploadButton = view.findViewById(R.id.view_key_action_upload);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mNfcPrefsButton.setVisibility(View.VISIBLE);
@@ -139,6 +147,12 @@ public class ViewKeyShareFragment extends LoaderFragment implements
showNfcPrefs();
}
});
+ mKeyUploadButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ uploadToKeyserver();
+ }
+ });
return root;
}
@@ -152,7 +166,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
KeyRings.buildUnifiedKeyRingUri(dataUri),
Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
String fingerprint = PgpKeyHelper.convertFingerprintToHex(data);
- if(!toClipboard){
+ if (!toClipboard) {
content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
} else {
content = fingerprint;
@@ -171,13 +185,13 @@ public class ViewKeyShareFragment extends LoaderFragment implements
} else {
message = getResources().getString(R.string.key_copied_to_clipboard);
}
- AppMsg.makeText(getActivity(), message, AppMsg.STYLE_INFO).show();
+ Notify.showNotify(getActivity(), message, Notify.Style.OK);
} else {
// Android will fail with android.os.TransactionTooLargeException if key is too big
// see http://www.lonestarprod.com/?p=34
if (content.length() >= 86389) {
- AppMsg.makeText(getActivity(), R.string.key_too_big_for_sharing,
- AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.key_too_big_for_sharing,
+ Notify.Style.ERROR);
return;
}
@@ -195,19 +209,20 @@ public class ViewKeyShareFragment extends LoaderFragment implements
}
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "error processing key!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
} catch (IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
+ Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR);
}
}
private void showQrCodeDialog() {
- ShareQrCodeDialogFragment dialog = ShareQrCodeDialogFragment.newInstance(mDataUri);
- dialog.show(ViewKeyShareFragment.this.getActivity().getSupportFragmentManager(), "shareQrCodeDialog");
+ Intent qrCodeIntent = new Intent(getActivity(), QrCodeActivity.class);
+ qrCodeIntent.setData(mDataUri);
+ startActivity(qrCodeIntent);
}
private void showNfcHelpDialog() {
@@ -292,10 +307,7 @@ public class ViewKeyShareFragment extends LoaderFragment implements
String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
- String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- mFingerprintQrCode.setImageBitmap(
- QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE)
- );
+ loadQrCode(fingerprint);
break;
}
@@ -311,4 +323,42 @@ public class ViewKeyShareFragment extends LoaderFragment implements
*/
public void onLoaderReset(Loader<Cursor> loader) {
}
+
+ /**
+ * Load QR Code asynchronously and with a fade in animation
+ *
+ * @param fingerprint
+ */
+ private void loadQrCode(final String fingerprint) {
+ AsyncTask<Void, Void, Bitmap> loadTask =
+ new AsyncTask<Void, Void, Bitmap>() {
+ protected Bitmap doInBackground(Void... unused) {
+ String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ return QrCodeUtils.getQRCodeBitmap(qrCodeContent, QR_CODE_SIZE);
+ }
+
+ protected void onPostExecute(Bitmap qrCode) {
+ mFingerprintQrCode.setImageBitmap(qrCode);
+
+ // Transition drawable with a transparent drawable and the final bitmap
+ final TransitionDrawable td =
+ new TransitionDrawable(new Drawable[]{
+ new ColorDrawable(Color.TRANSPARENT),
+ new BitmapDrawable(getResources(), qrCode)
+ });
+
+ mFingerprintQrCode.setImageDrawable(td);
+ td.startTransition(200);
+ }
+ };
+
+ loadTask.execute();
+ }
+
+ private void uploadToKeyserver() {
+ Intent uploadIntent = new Intent(getActivity(), UploadKeyActivity.class);
+ uploadIntent.setData(mDataUri);
+ startActivityForResult(uploadIntent, 0);
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
index 99f959035..4971c535c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java
@@ -30,20 +30,21 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PositionAwareInputStream;
import java.io.BufferedInputStream;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
public class ImportKeysListLoader
extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> {
- public static class FileHasNoContent extends Exception {
-
+ public static class NoValidKeysException extends Exception {
}
- public static class NonPgpPart extends Exception {
+ public static class NonPgpPartException extends Exception {
private int mCount;
- public NonPgpPart(int count) {
+ public NonPgpPartException(int count) {
this.mCount = count;
}
@@ -67,7 +68,6 @@ public class ImportKeysListLoader
@Override
public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() {
-
// This has already been loaded! nvm any further, just return
if (mEntryListWrapper != null) {
return mEntryListWrapper;
@@ -119,9 +119,6 @@ public class ImportKeysListLoader
* @return
*/
private void generateListOfKeyrings(InputData inputData) {
-
- boolean isEmpty = true;
-
PositionAwareInputStream progressIn = new PositionAwareInputStream(
inputData.getInputStream());
@@ -130,27 +127,23 @@ public class ImportKeysListLoader
// armor blocks
BufferedInputStream bufferedInput = new BufferedInputStream(progressIn);
try {
-
- // read all available blocks... (asc files can contain many blocks with BEGIN END)
- while (bufferedInput.available() > 0) {
- // TODO: deal with non-keyring objects?
- List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
- for(UncachedKeyRing key : rings) {
- ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key);
- mData.add(item);
- mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(key.getEncoded()));
- isEmpty = false;
- }
+ // parse all keyrings
+ Iterator<UncachedKeyRing> it = UncachedKeyRing.fromStream(bufferedInput);
+ while (it.hasNext()) {
+ UncachedKeyRing ring = it.next();
+ ImportKeysListEntry item = new ImportKeysListEntry(getContext(), ring);
+ mData.add(item);
+ mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(ring.getEncoded()));
}
- } catch (Exception e) {
- Log.e(Constants.TAG, "Exception on parsing key file!", e);
- mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
- }
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "IOException on parsing key file! Return NoValidKeysException!", e);
- if (isEmpty) {
- Log.e(Constants.TAG, "File has no content!", new FileHasNoContent());
+ NoValidKeysException e1 = new NoValidKeysException();
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>
- (mData, new FileHasNoContent());
+ (mData, e1);
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "Other Exception on parsing key file!", e);
+ mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mData, e);
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
index 18312660a..6d46f3c8f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java
@@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.database.Cursor;
+import android.graphics.PorterDuff;
+import android.graphics.Typeface;
import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
@@ -156,7 +158,10 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
if (isRevoked) {
// set revocation icon (can this even be primary?)
- vVerified.setImageResource(R.drawable.key_certify_revoke);
+ vVerified.setImageResource(R.drawable.status_signature_revoked_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.bg_gray),
+ PorterDuff.Mode.SRC_IN);
// disable and strike through text for revoked user ids
vName.setEnabled(false);
@@ -170,22 +175,33 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
vAddress.setEnabled(true);
vComment.setEnabled(true);
- // verified: has been verified
- // isPrimary: show small star icon for primary user ids
- int verified = cursor.getInt(INDEX_VERIFIED);
- switch (verified) {
+ if (isPrimary) {
+ vName.setTypeface(null, Typeface.BOLD);
+ vAddress.setTypeface(null, Typeface.BOLD);
+ } else {
+ vName.setTypeface(null, Typeface.NORMAL);
+ vAddress.setTypeface(null, Typeface.NORMAL);
+ }
+
+ int isVerified = cursor.getInt(INDEX_VERIFIED);
+ switch (isVerified) {
case Certs.VERIFIED_SECRET:
- vVerified.setImageResource(isPrimary
- ? R.drawable.key_certify_primary_ok_depth0
- : R.drawable.key_certify_ok_depth0);
+ vVerified.setImageResource(R.drawable.status_signature_verified_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.android_green_dark),
+ PorterDuff.Mode.SRC_IN);
break;
case Certs.VERIFIED_SELF:
- vVerified.setImageResource(isPrimary
- ? R.drawable.key_certify_primary_ok_self
- : R.drawable.key_certify_ok_self);
+ vVerified.setImageResource(R.drawable.status_signature_unverified_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.bg_gray),
+ PorterDuff.Mode.SRC_IN);
break;
default:
- vVerified.setImageResource(R.drawable.key_certify_error);
+ vVerified.setImageResource(R.drawable.status_signature_invalid_cutout);
+ vVerified.setColorFilter(
+ mContext.getResources().getColor(R.color.android_red_dark),
+ PorterDuff.Mode.SRC_IN);
break;
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index d723f88af..ef2659cf8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -33,8 +33,8 @@ import android.support.v4.app.FragmentActivity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@@ -44,8 +44,8 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
-import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
@@ -62,7 +62,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
private Messenger mMessenger;
private EditText mPassphraseEditText;
- private boolean mCanKB;
/**
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
@@ -102,10 +101,10 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// check if secret key has a passphrase
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
try {
- if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
+ if (!new ProviderHelper(context).getCanonicalizedSecretKeyRing(secretKeyId).hasPassphrase()) {
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
}
- } catch(ProviderHelper.NotFoundException e) {
+ } catch (ProviderHelper.NotFoundException e) {
throw new PgpGeneralException("Error: Key not found!", e);
}
}
@@ -120,11 +119,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
return frag;
}
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
/**
* Creates dialog
*/
@@ -138,7 +132,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
alert.setTitle(R.string.title_authentication);
- final WrappedSecretKeyRing secretRing;
+ final CanonicalizedSecretKeyRing secretRing;
String userId;
if (secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none) {
@@ -147,7 +141,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
} else {
try {
ProviderHelper helper = new ProviderHelper(activity);
- secretRing = helper.getWrappedSecretKeyRing(secretKeyId);
+ secretRing = helper.getCanonicalizedSecretKeyRing(secretKeyId);
// yes the inner try/catch block is necessary, otherwise the final variable
// above can't be statically verified to have been set in all cases because
// the catch clause doesn't return.
@@ -165,7 +159,6 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
});
alert.setCancelable(false);
- mCanKB = false;
return alert.create();
}
@@ -190,7 +183,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// Early breakout if we are dealing with a symmetric key
if (secretRing == null) {
PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric,
- passphrase, getString(R.string.passp_cache_notif_pwd));
+ passphrase, getString(R.string.passp_cache_notif_pwd));
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
@@ -198,9 +191,9 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
return;
}
- WrappedSecretKey unlockedSecretKey = null;
+ CanonicalizedSecretKey unlockedSecretKey = null;
- for(WrappedSecretKey clickSecretKey : secretRing.secretKeyIterator()) {
+ for (CanonicalizedSecretKey clickSecretKey : secretRing.secretKeyIterator()) {
try {
boolean unlocked = clickSecretKey.unlock(passphrase);
if (unlocked) {
@@ -232,9 +225,9 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
try {
PassphraseCacheService.addCachedPassphrase(activity, masterKeyId, passphrase,
- secretRing.getPrimaryUserIdWithFallback());
- } catch(PgpGeneralException e) {
- Log.e(Constants.TAG, "adding of a passhrase failed", e);
+ secretRing.getPrimaryUserIdWithFallback());
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "adding of a passphrase failed", e);
}
if (unlockedSecretKey.getKeyId() != masterKeyId) {
@@ -258,20 +251,30 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
});
- mCanKB = true;
- return alert.show();
- }
+ // Hack to open keyboard.
+ // This is the only method that I found to work across all Android versions
+ // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
+ // Notes: * onCreateView can't be used because we want to add buttons to the dialog
+ // * opening in onActivityCreated does not work on Android 4.4
+ mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPassphraseEditText.post(new Runnable() {
+ @Override
+ public void run() {
+ InputMethodManager imm = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+ }
+ });
+ mPassphraseEditText.requestFocus();
- @Override
- public void onActivityCreated(Bundle arg0) {
- super.onActivityCreated(arg0);
- if (mCanKB) {
- // request focus and open soft keyboard
- mPassphraseEditText.requestFocus();
- getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-
- mPassphraseEditText.setOnEditorActionListener(this);
- }
+ mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
+ mPassphraseEditText.setOnEditorActionListener(this);
+
+ return alert.show();
}
@Override
@@ -282,6 +285,27 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
sendMessageToHandler(MESSAGE_CANCEL);
}
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ Log.d(Constants.TAG, "onDismiss");
+
+ // hide keyboard on dismiss
+ hideKeyboard();
+ }
+
+ private void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ //check if no view has focus:
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
+ }
+
/**
* Associate the "done" button on the soft keyboard with the okay button in the view
*/
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
index 93da48b75..1386ed098 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
@@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Message;
@@ -32,6 +33,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
@@ -164,18 +166,50 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
}
});
+ // Hack to open keyboard.
+ // This is the only method that I found to work across all Android versions
+ // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
+ // Notes: * onCreateView can't be used because we want to add buttons to the dialog
+ // * opening in onActivityCreated does not work on Android 4.4
+ mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPassphraseEditText.post(new Runnable() {
+ @Override
+ public void run() {
+ InputMethodManager imm = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ });
+ }
+ });
+ mPassphraseEditText.requestFocus();
+
+ mPassphraseAgainEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
+ mPassphraseAgainEditText.setOnEditorActionListener(this);
+
return alert.show();
}
@Override
- public void onActivityCreated(Bundle arg0) {
- super.onActivityCreated(arg0);
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
- // request focus and open soft keyboard
- mPassphraseEditText.requestFocus();
- getDialog().getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE);
+ // hide keyboard on dismiss
+ hideKeyboard();
+ }
- mPassphraseAgainEditText.setOnEditorActionListener(this);
+ private void hideKeyboard() {
+ InputMethodManager inputManager = (InputMethodManager) getActivity()
+ .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ //check if no view has focus:
+ View v = getActivity().getCurrentFocus();
+ if (v == null)
+ return;
+
+ inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
deleted file mode 100644
index 24608784b..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2012-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.dialog;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.net.Uri;
-import android.os.Bundle;
-import android.support.v4.app.DialogFragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.devspark.appmsg.AppMsg;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.QrCodeUtils;
-
-public class ShareQrCodeDialogFragment extends DialogFragment {
- private static final String ARG_KEY_URI = "uri";
-
- private ImageView mImage;
- private TextView mText;
-
- private static final int QR_CODE_SIZE = 1000;
-
- /**
- * Creates new instance of this dialog fragment
- */
- public static ShareQrCodeDialogFragment newInstance(Uri dataUri) {
- ShareQrCodeDialogFragment frag = new ShareQrCodeDialogFragment();
- Bundle args = new Bundle();
- args.putParcelable(ARG_KEY_URI, dataUri);
-
- frag.setArguments(args);
-
- return frag;
- }
-
- /**
- * Creates dialog
- */
- @Override
- public Dialog onCreateDialog(Bundle savedInstanceState) {
- final Activity activity = getActivity();
-
- Uri dataUri = getArguments().getParcelable(ARG_KEY_URI);
-
- CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(getActivity());
- alert.setTitle(R.string.share_qr_code_dialog_title);
-
- LayoutInflater inflater = activity.getLayoutInflater();
- View view = inflater.inflate(R.layout.share_qr_code_dialog, null);
- alert.setView(view);
-
- mImage = (ImageView) view.findViewById(R.id.share_qr_code_dialog_image);
- mText = (TextView) view.findViewById(R.id.share_qr_code_dialog_text);
-
- ProviderHelper providerHelper = new ProviderHelper(getActivity());
- String content;
- try {
- alert.setPositiveButton(R.string.btn_okay, null);
-
- byte[] blob = (byte[]) providerHelper.getGenericData(
- KeyRings.buildUnifiedKeyRingUri(dataUri),
- KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
- if (blob == null) {
- Log.e(Constants.TAG, "key not found!");
- AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
- return null;
- }
-
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);
- mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint);
- content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
- setQrCode(content);
- } catch (ProviderHelper.NotFoundException e) {
- Log.e(Constants.TAG, "key not found!", e);
- AppMsg.makeText(getActivity(), R.string.error_key_not_found, AppMsg.STYLE_ALERT).show();
- return null;
- }
-
- return alert.show();
- }
-
- private void setQrCode(String data) {
- mImage.setImageBitmap(QrCodeUtils.getQRCodeBitmap(data, QR_CODE_SIZE));
- }
-
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java
index 67f81fb24..22e3f5d66 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Notify.java
@@ -29,7 +29,7 @@ import com.github.johnpersano.supertoasts.SuperToast;
*/
public class Notify {
- public static enum Style {OK, WARN, ERROR}
+ public static enum Style {OK, WARN, INFO, ERROR}
/**
* Shows a simple in-layout notification with the CharSequence given as parameter
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
index 869eea03f..95a259336 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
@@ -46,13 +46,13 @@ public class ProgressScaler implements Progressable {
public void setProgress(int resourceId, int progress, int max) {
if (mWrapped != null) {
- mWrapped.setProgress(resourceId, progress, mMax);
+ mWrapped.setProgress(resourceId, mFrom + progress * (mTo - mFrom) / max, mMax);
}
}
public void setProgress(int progress, int max) {
if (mWrapped != null) {
- mWrapped.setProgress(progress, max);
+ mWrapped.setProgress(mFrom + progress * (mTo - mFrom) / max, mMax);
}
}
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml b/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml
new file mode 100644
index 000000000..4a021c676
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_in_from_left.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="-100%"
+ android:toXDelta="0"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml b/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml
new file mode 100644
index 000000000..1329f8bef
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_in_from_right.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="100%"
+ android:toXDelta="0"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml b/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml
new file mode 100644
index 000000000..4e02af2a4
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_out_to_left.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0"
+ android:toXDelta="-100%"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml b/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml
new file mode 100644
index 000000000..7b5f63e0d
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/frag_slide_out_to_right.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set>
+ <translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0"
+ android:toXDelta="100%"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:duration="500" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml b/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml
new file mode 100644
index 000000000..2b95cfba6
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/qr_code_zoom_enter.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator">
+ <scale
+ android:fromXScale="0.5"
+ android:toXScale="1.0"
+ android:fromYScale="0.5"
+ android:toYScale="1.0"
+ android:pivotX="50%p"
+ android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <alpha
+ android:fromAlpha="0"
+ android:toAlpha="1.0"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml b/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml
new file mode 100644
index 000000000..772375739
--- /dev/null
+++ b/OpenKeychain/src/main/res/anim/qr_code_zoom_exit.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/decelerate_interpolator"
+ android:zAdjustment="top">
+ <scale
+ android:fromXScale="1.0"
+ android:toXScale="0.5"
+ android:fromYScale="1.0"
+ android:toYScale="0.5"
+ android:pivotX="50%p"
+ android:pivotY="50%p"
+ android:duration="@android:integer/config_mediumAnimTime" />
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="0"
+ android:duration="@android:integer/config_mediumAnimTime" />
+</set> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png
new file mode 100644
index 000000000..acc198fc3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png
deleted file mode 100644
index 391d1c988..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png
deleted file mode 100644
index 76944469c..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png
deleted file mode 100644
index 815701015..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index 026869c14..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index 12d2e026e..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png
deleted file mode 100644
index c39d3a87c..000000000
--- a/OpenKeychain/src/main/res/drawable-hdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png
new file mode 100644
index 000000000..a1b090630
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png
new file mode 100644
index 000000000..e567055aa
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png
new file mode 100644
index 000000000..98e32eadc
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png
new file mode 100644
index 000000000..21e8b536a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..84ac9bec2
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png
new file mode 100644
index 000000000..9ae2a09ab
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..967e00e80
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png
new file mode 100644
index 000000000..16e1d7181
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..244dd0708
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png
new file mode 100644
index 000000000..5c3ba866d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..82cc25a4b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png
new file mode 100644
index 000000000..b8b472a5a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..e752eaeab
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png
new file mode 100644
index 000000000..d8141b47b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..08a9f464c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-hdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png
deleted file mode 100644
index 79fddf78a..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png
deleted file mode 100644
index c400a1820..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png
deleted file mode 100644
index fb1654b53..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png
deleted file mode 100644
index 5228a4b6b..000000000
--- a/OpenKeychain/src/main/res/drawable-ldpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png
new file mode 100644
index 000000000..58476bfc9
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png
deleted file mode 100644
index 6def8769f..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png
deleted file mode 100644
index e16ec810a..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png
deleted file mode 100644
index 715a16487..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index c376a2897..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index 45a261b24..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png
deleted file mode 100644
index 62ee0ca42..000000000
--- a/OpenKeychain/src/main/res/drawable-mdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png
new file mode 100644
index 000000000..cfc39f0e7
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png
new file mode 100644
index 000000000..824dc2672
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png
new file mode 100644
index 000000000..9bca59ae3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png
new file mode 100644
index 000000000..81a900147
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..bc91094b5
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png
new file mode 100644
index 000000000..baa78f795
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..bc2f56e2a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png
new file mode 100644
index 000000000..7cf985274
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..2d2593194
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png
new file mode 100644
index 000000000..3d4665320
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..0fc74d07e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png
new file mode 100644
index 000000000..8348b32b3
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..96a2d1413
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png
new file mode 100644
index 000000000..02e53ac8a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..9f7cf837c
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-mdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png
new file mode 100644
index 000000000..022f2dd2e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png
deleted file mode 100644
index 8278ce2b6..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png
deleted file mode 100644
index e2aef1177..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png
deleted file mode 100644
index 9bb6ceffa..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index de6614246..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index ce10da099..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png
deleted file mode 100644
index 1478e726b..000000000
--- a/OpenKeychain/src/main/res/drawable-xhdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png
new file mode 100644
index 000000000..7c6bb2d18
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png
new file mode 100644
index 000000000..da4a5d89a
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png
new file mode 100644
index 000000000..cd02fc1e4
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png
new file mode 100644
index 000000000..f5105c1ae
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..83f6fde35
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png
new file mode 100644
index 000000000..67880d6db
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..29830f5ba
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png
new file mode 100644
index 000000000..2ed67419b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..2f7695043
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png
new file mode 100644
index 000000000..a6f1f2792
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..2ce28c7ca
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png
new file mode 100644
index 000000000..c25a84b4d
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..442c55eee
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png
new file mode 100644
index 000000000..6f435a85e
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..160ec7cbe
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xhdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png b/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png
new file mode 100644
index 000000000..5392deafd
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/create_key_robot.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png
deleted file mode 100644
index 9416720eb..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_error.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png
deleted file mode 100644
index 501a75d63..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png
deleted file mode 100644
index 72ada9c1f..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png
deleted file mode 100644
index 1b52ef04d..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_depth0.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png
deleted file mode 100644
index baa1c00d2..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_primary_ok_self.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png b/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png
deleted file mode 100644
index 217f4e914..000000000
--- a/OpenKeychain/src/main/res/drawable-xxhdpi/key_certify_revoke.png
+++ /dev/null
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png
new file mode 100644
index 000000000..5a9664d59
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_closed.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png
new file mode 100644
index 000000000..608f065af
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_error.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png
new file mode 100644
index 000000000..ee34dd396
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_lock_open.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png
new file mode 100644
index 000000000..f475c9d84
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png
new file mode 100644
index 000000000..33a3efed1
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_expired_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png
new file mode 100644
index 000000000..f21c2cf52
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png
new file mode 100644
index 000000000..bc39d3496
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_invalid_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png
new file mode 100644
index 000000000..be1a1d9dc
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png
new file mode 100644
index 000000000..58929661f
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_revoked_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png
new file mode 100644
index 000000000..841cfa958
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png
new file mode 100644
index 000000000..3020357a4
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unknown_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png
new file mode 100644
index 000000000..525d1cf6b
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png
new file mode 100644
index 000000000..3829bb3a0
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_unverified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png
new file mode 100644
index 000000000..54eee5ba0
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png
new file mode 100644
index 000000000..3548ee2b6
--- /dev/null
+++ b/OpenKeychain/src/main/res/drawable-xxhdpi/status_signature_verified_cutout.png
Binary files differ
diff --git a/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml
index 3557c1f00..b2a9c11f5 100644
--- a/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml
+++ b/OpenKeychain/src/main/res/layout/api_account_settings_activity.xml
@@ -1,20 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
+ <include layout="@layout/notify_area" />
+
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical">
+ android:layout_height="match_parent">
- <fragment
- android:id="@+id/api_account_settings_fragment"
- android:name="org.sufficientlysecure.keychain.remote.ui.AccountSettingsFragment"
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical">
+
+ <fragment
+ android:id="@+id/api_account_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.remote.ui.AccountSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
- </LinearLayout>
-</ScrollView>
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
index 49c4ccbfe..1c09820a9 100644
--- a/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
+++ b/OpenKeychain/src/main/res/layout/api_app_settings_activity.xml
@@ -1,33 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
+ <include layout="@layout/notify_area" />
+
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="16dp"
- android:orientation="vertical">
+ android:layout_height="match_parent">
- <fragment
- android:id="@+id/api_app_settings_fragment"
- android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:layout="@layout/api_app_settings_fragment" />
+ android:padding="16dp"
+ android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/api_settings_accounts"
- android:textAppearance="?android:attr/textAppearanceMedium" />
+ <fragment
+ android:id="@+id/api_app_settings_fragment"
+ android:name="org.sufficientlysecure.keychain.remote.ui.AppSettingsFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:layout="@layout/api_app_settings_fragment" />
- <FrameLayout
- android:id="@+id/api_accounts_list_fragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/api_settings_accounts"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <FrameLayout
+ android:id="@+id/api_accounts_list_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
- </LinearLayout>
-</ScrollView>
+ </LinearLayout>
+ </ScrollView>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/certify_key_activity.xml b/OpenKeychain/src/main/res/layout/certify_key_activity.xml
index bb43fa805..34d4dbd57 100644
--- a/OpenKeychain/src/main/res/layout/certify_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/certify_key_activity.xml
@@ -1,171 +1,192 @@
<?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"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="wrap_content"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical">
+ <include layout="@layout/notify_area" />
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_certification_key" />
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
- <fragment
- android:id="@+id/sign_key_select_key_fragment"
- android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- tools:layout="@layout/select_secret_key_layout_fragment" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_key_to_certify" />
-
- <TableLayout
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_marginLeft="8dp"
- android:shrinkColumns="1">
-
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <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
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_main_user_id" />
-
- <TextView
- android:id="@+id/main_user_id"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </TableRow>
-
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_certification_key" />
+
+ <fragment
+ android:id="@+id/sign_key_select_key_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ tools:layout="@layout/select_secret_key_layout_fragment" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_key_to_certify" />
+
+ <TableLayout
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_marginLeft="8dp"
+ android:shrinkColumns="1">
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <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
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_main_user_id" />
+
+ <TextView
+ android:id="@+id/main_user_id"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </TableRow>
+
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <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/view_key_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:layout_marginTop="14dp"
+ android:text="@string/section_uids_to_certify" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FixedListView
+ android:id="@+id/view_key_user_ids"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:descendantFocusability="blocksDescendants" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_upload_key" />
+
+ <CheckBox
+ android:id="@+id/sign_key_upload_checkbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:checked="false"
+ android:text="@string/label_send_key" />
+
+ <Spinner
+ android:id="@+id/upload_key_keyserver"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:enabled="false" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_actions"
+ android:layout_weight="1" />
+
+ <LinearLayout
+ android:id="@+id/certify_key_certify_button"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ android:paddingRight="4dp"
+ android:layout_marginBottom="8dp"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
<TextView
+ android:paddingLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:text="@string/key_view_action_certify"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+
+ <ImageView
+ android:id="@+id/certify_key_action_certify_image"
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/view_key_fingerprint"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:typeface="monospace" />
-
- </TableRow>
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/status_signature_verified_cutout"
+ android:layout_gravity="center_vertical" />
- </TableLayout>
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_uids_to_certify" />
+ </LinearLayout>
- <org.sufficientlysecure.keychain.ui.widget.FixedListView
- android:id="@+id/view_key_user_ids"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:descendantFocusability="blocksDescendants" />
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_upload_key" />
+ </LinearLayout>
- <CheckBox
- android:id="@+id/sign_key_upload_checkbox"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:checked="false"
- android:text="@string/label_send_key" />
+ </ScrollView>
- <Spinner
- android:id="@+id/upload_key_keyserver"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:enabled="false" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_actions"
- android:layout_weight="1" />
-
- <TextView
- android:id="@+id/sign_key_sign_button"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_marginBottom="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/key_view_action_certify"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:drawableRight="@drawable/ic_action_good"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
- android:clickable="true"
- style="@style/SelectableItem" />
-
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/create_key_activity.xml b/OpenKeychain/src/main/res/layout/create_key_activity.xml
index 673f43084..0bd053c49 100644
--- a/OpenKeychain/src/main/res/layout/create_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/create_key_activity.xml
@@ -1,54 +1,15 @@
-<?xml version="1.0" encoding="UTF-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:orientation="vertical">
+ android:layout_height="match_parent">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- android:text="Enter Full Name, Email and Passphrase!"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <AutoCompleteTextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="textPersonName"
- android:hint="Name"
- android:ems="10"
- android:id="@+id/name" />
-
- <AutoCompleteTextView
- android:id="@+id/email"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="bla@example.com"
- android:layout_weight="1"
- android:ems="10"
- android:inputType="textEmailAddress" />
+ <include layout="@layout/notify_area" />
- <EditText
+ <FrameLayout
+ android:id="@+id/create_key_fragment_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="textPassword"
- android:hint="passphrase"
- android:ems="10"
- android:id="@+id/passphrase"
- android:layout_gravity="center_horizontal" />
-
-
- <Button
- android:id="@+id/create_key_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_horizontal"
- android:layout_margin="8dp"
- android:text="@string/first_time_create_key"
- android:background="@drawable/button_edgy"
- android:drawableLeft="@drawable/ic_action_new_account" />
-
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
new file mode 100644
index 000000000..6814cd259
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_key_final_fragment.xml
@@ -0,0 +1,152 @@
+<?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="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/create_key_buttons_divider">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:text="@string/create_key_final_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:text="@string/label_name"
+ android:textColor="@color/tertiary_text_light"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:id="@+id/name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="2dp"
+ android:text="@string/label_email"
+ android:textColor="@color/tertiary_text_light"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
+ android:id="@+id/email"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_marginBottom="8dp"
+ android:layout_marginLeft="8dp" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <CheckBox
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/create_key_upload"
+ android:id="@+id/create_key_upload" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="8dp"
+ android:text="@string/create_key_final_robot_text"
+ android:textColor="@color/android_green_dark"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:drawableLeft="@drawable/create_key_robot"
+ android:drawablePadding="8dp" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:id="@+id/create_key_buttons">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/btn_back"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_vertical" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/create_key_create_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/btn_create_key"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_new_account"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+
+ <View
+ android:id="@+id/create_key_buttons_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider"
+ android:layout_alignTop="@+id/create_key_buttons"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml
new file mode 100644
index 000000000..588cbb050
--- /dev/null
+++ b/OpenKeychain/src/main/res/layout/create_key_input_fragment.xml
@@ -0,0 +1,144 @@
+<?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="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="false"
+ android:layout_above="@+id/create_key_button_divider">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="@string/create_key_text" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/label_user_id" />
+
+ <AutoCompleteTextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:imeOptions="actionNext"
+ android:inputType="textPersonName"
+ android:hint="@string/create_key_hint_full_name"
+ android:ems="10"
+ android:id="@+id/name" />
+
+ <AutoCompleteTextView
+ android:id="@+id/email"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:imeOptions="actionNext"
+ android:hint="@string/label_email"
+ android:ems="10"
+ android:inputType="textEmailAddress" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_passphrase" />
+
+ <EditText
+ android:id="@+id/passphrase"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:inputType="textPassword"
+ android:hint="@string/label_passphrase"
+ android:ems="10"
+ android:layout_gravity="center_horizontal" />
+
+ <EditText
+ android:id="@+id/passphrase_again"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:inputType="textPassword"
+ android:hint="@string/label_passphrase_again"
+ android:ems="10"
+ android:layout_gravity="center_horizontal" />
+
+ </LinearLayout>
+ </ScrollView>
+
+ <View
+ android:id="@+id/create_key_button_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:background="?android:attr/listDivider"
+ android:layout_alignTop="@+id/create_key_buttons"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:id="@+id/create_key_buttons">
+
+ <TextView
+ android:id="@+id/create_key_back_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text=""
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/create_key_button"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/btn_next"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_new_account"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_vertical" />
+ </LinearLayout>
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
index 05877656b..fcad91df3 100644
--- a/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
+++ b/OpenKeychain/src/main/res/layout/decrypt_result_include.xml
@@ -8,7 +8,7 @@
android:paddingRight="16dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
- android:background="@color/result_purple">
+ android:background="@color/android_purple_light">
<View
android:layout_width="match_parent"
diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity.xml b/OpenKeychain/src/main/res/layout/edit_key_activity.xml
index b6c5a1c9a..7e71ccf53 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/edit_key_activity.xml
@@ -1,46 +1,15 @@
<?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="fill_parent"
- android:layout_height="fill_parent"
- android:fillViewport="true"
- android:orientation="vertical" >
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <LinearLayout
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="16dp"
- android:paddingRight="16dp" >
+ <include layout="@layout/notify_area"/>
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:padding="4dp"
- android:text="@string/label_passphrase" />
+ <FrameLayout
+ android:id="@+id/edit_key_fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
- <CheckBox
- android:id="@+id/edit_key_no_passphrase"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/label_no_passphrase" />
-
- <Button
- android:id="@+id/edit_key_btn_change_passphrase"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:text="@string/btn_set_passphrase"
- android:drawableLeft="@drawable/ic_action_edit"
- android:background="@drawable/button_edgy" />
-
- <LinearLayout
- android:id="@+id/edit_key_container"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
- </LinearLayout>
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml b/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml
index 856bef36a..a4258b998 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml
+++ b/OpenKeychain/src/main/res/layout/edit_key_subkey_added_item.xml
@@ -9,7 +9,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="10dp"
- android:background="@color/result_green" />
+ android:background="@color/android_green_light" />
<TableLayout
android:layout_width="0dp"
diff --git a/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml b/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml
index e69452db1..ef0e2626e 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml
+++ b/OpenKeychain/src/main/res/layout/edit_key_user_id_added_item.xml
@@ -10,7 +10,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:minWidth="10dp"
- android:background="@color/result_green" />
+ android:background="@color/android_green_light" />
<LinearLayout
android:orientation="vertical"
diff --git a/OpenKeychain/src/main/res/layout/first_time_activity.xml b/OpenKeychain/src/main/res/layout/first_time_activity.xml
index 514f34212..2d750c1a7 100644
--- a/OpenKeychain/src/main/res/layout/first_time_activity.xml
+++ b/OpenKeychain/src/main/res/layout/first_time_activity.xml
@@ -2,18 +2,100 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingTop="16dp"
- android:paddingBottom="8dp">
+ android:paddingTop="16dp">
<LinearLayout
+ android:id="@+id/first_time_buttons"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:layout_alignParentBottom="true"
android:orientation="vertical">
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/first_time_import_key"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/first_time_import_key"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_collection"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem" />
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/first_time_create_key"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/first_time_create_key"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_new_account"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:layout_gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/first_time_cancel"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/first_time_skip"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:layout_gravity="center_horizontal" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:layout_above="@+id/first_time_buttons">
+
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/app_name"
android:drawableLeft="@drawable/ic_launcher"
@@ -23,12 +105,15 @@
<ImageView
android:layout_width="wrap_content"
- android:layout_marginLeft="64dp"
- android:layout_marginRight="64dp"
+ android:layout_height="0dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
- android:layout_height="256dp"
- android:src="@drawable/first_time_1" />
+ android:adjustViewBounds="true"
+ android:src="@drawable/first_time_1"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="1" />
<TextView
android:layout_width="wrap_content"
@@ -38,51 +123,9 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/first_time_text1"
android:layout_gravity="center_horizontal"
- android:gravity="center_horizontal" />
+ android:gravity="center_horizontal"
+ android:layout_marginBottom="16dp" />
-
- </LinearLayout>
-
- <Button
- android:id="@+id/first_time_cancel"
- android:layout_alignParentBottom="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:text="@string/first_time_skip"
- android:background="@drawable/button_edgy" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_above="@id/first_time_cancel"
- android:orientation="horizontal">
-
- <Button
- android:id="@+id/first_time_create_key"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_weight="1"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:text="@string/first_time_create_key"
- android:background="@drawable/button_edgy"
- android:drawableLeft="@drawable/ic_action_new_account" />
-
- <Button
- android:id="@+id/first_time_import_key"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_gravity="center_horizontal"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:text="@string/first_time_import_key"
- android:background="@drawable/button_edgy"
- android:drawableLeft="@drawable/ic_action_download" />
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/key_list_activity.xml b/OpenKeychain/src/main/res/layout/key_list_activity.xml
index c4c2c35cb..297fc526e 100644
--- a/OpenKeychain/src/main/res/layout/key_list_activity.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_activity.xml
@@ -2,9 +2,18 @@
<android.support.v4.widget.FixedDrawerLayout 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">
- <include layout="@layout/key_list_content"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/notify_area" />
+
+ <include layout="@layout/key_list_content" />
+
+ </LinearLayout>
<include layout="@layout/drawer_list" />
diff --git a/OpenKeychain/src/main/res/layout/key_list_fragment.xml b/OpenKeychain/src/main/res/layout/key_list_fragment.xml
index 32b77baac..f1da19b72 100644
--- a/OpenKeychain/src/main/res/layout/key_list_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_fragment.xml
@@ -56,6 +56,7 @@
android:textSize="14sp"
android:text="@string/key_list_empty_button_create"
android:drawableLeft="@drawable/ic_action_new_account"
+ android:drawablePadding="8dp"
android:background="@drawable/button_edgy"/>
<TextView
@@ -73,7 +74,8 @@
android:layout_margin="4dp"
android:textSize="14sp"
android:text="@string/key_list_empty_button_import"
- android:drawableLeft="@drawable/ic_action_download"
+ android:drawableLeft="@drawable/ic_action_collection"
+ android:drawablePadding="8dp"
android:background="@drawable/button_edgy" />
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/key_list_header.xml b/OpenKeychain/src/main/res/layout/key_list_header.xml
index 8cb0d4262..4809fc5ab 100644
--- a/OpenKeychain/src/main/res/layout/key_list_header.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_header.xml
@@ -21,7 +21,7 @@
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="8dp"
- android:visibility="visible"
+ android:visibility="gone"
android:textColor="@android:color/darker_gray" />
</RelativeLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/key_list_item.xml b/OpenKeychain/src/main/res/layout/key_list_item.xml
index 99e4c0268..8d600464c 100644
--- a/OpenKeychain/src/main/res/layout/key_list_item.xml
+++ b/OpenKeychain/src/main/res/layout/key_list_item.xml
@@ -38,30 +38,12 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
</LinearLayout>
- <FrameLayout
- android:id="@+id/status_layout"
+ <ImageView
android:layout_width="wrap_content"
- android:layout_height="match_parent">
-
- <TextView
- android:id="@+id/revoked"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:ellipsize="end"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:text="@string/revoked"
- android:textColor="#e00"
- android:layout_gravity="center"
- android:padding="12dp" />
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/verified"
- android:layout_gravity="center"
- android:src="@drawable/key_certify_ok_depth0"
- android:padding="16dp" />
- </FrameLayout>
+ android:layout_height="wrap_content"
+ android:id="@+id/status_image"
+ android:layout_gravity="center"
+ android:src="@drawable/status_signature_revoked_cutout"
+ android:padding="16dp" />
</LinearLayout>
diff --git a/OpenKeychain/src/main/res/layout/notify_area.xml b/OpenKeychain/src/main/res/layout/notify_area.xml
index d1ba265a5..43df1cde8 100644
--- a/OpenKeychain/src/main/res/layout/notify_area.xml
+++ b/OpenKeychain/src/main/res/layout/notify_area.xml
@@ -1,12 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/card_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="@color/emphasis"
android:orientation="vertical" />
</merge> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml b/OpenKeychain/src/main/res/layout/qr_code_activity.xml
index f96b993c5..57c869db6 100644
--- a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml
+++ b/OpenKeychain/src/main/res/layout/qr_code_activity.xml
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <FrameLayout
- android:id="@+id/edit_key_fragment_container"
+ <ImageView
+ android:id="@+id/qr_code_image"
+ android:padding="32dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical" />
+ style="@style/SelectableItem" />
</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml b/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml
deleted file mode 100644
index 0b58ae72f..000000000
--- a/OpenKeychain/src/main/res/layout/share_qr_code_dialog.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?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:orientation="vertical" >
-
- <TextView
- android:id="@+id/share_qr_code_dialog_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="8dp"
- android:textAppearance="@android:style/TextAppearance.Medium" />
-
- <ImageView
- android:id="@+id/share_qr_code_dialog_image"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
-</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/upload_key_activity.xml b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
index 5a6f732d5..736617ba5 100644
--- a/OpenKeychain/src/main/res/layout/upload_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/upload_key_activity.xml
@@ -1,55 +1,62 @@
<?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"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <LinearLayout
+ <include layout="@layout/notify_area" />
+
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:orientation="vertical">
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_key_server" />
+ android:layout_height="match_parent">
- <Spinner
- android:id="@+id/upload_key_keyserver"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp" />
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_marginTop="14dp"
- android:text="@string/section_actions"
- android:layout_weight="1" />
-
- <TextView
- android:id="@+id/upload_key_action_upload"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_marginBottom="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/btn_export_to_server"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:drawableRight="@drawable/ic_action_upload"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
- android:clickable="true"
- style="@style/SelectableItem" />
-
- </LinearLayout>
-
-</ScrollView> \ No newline at end of file
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_key_server" />
+
+ <Spinner
+ android:id="@+id/upload_key_keyserver"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_marginTop="14dp"
+ android:text="@string/section_actions"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/upload_key_action_upload"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_marginBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="@string/btn_export_to_server"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:drawableRight="@drawable/ic_action_upload"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:clickable="true"
+ style="@style/SelectableItem" />
+
+ </LinearLayout>
+
+ </ScrollView>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenKeychain/src/main/res/layout/view_key_activity.xml b/OpenKeychain/src/main/res/layout/view_key_activity.xml
index 481b1ddf5..b15a73c0e 100644
--- a/OpenKeychain/src/main/res/layout/view_key_activity.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_activity.xml
@@ -4,42 +4,38 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <LinearLayout
- android:id="@+id/card_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical" />
+ <include layout="@layout/notify_area"/>
- <TextView
- android:layout_width="match_parent"
+ <LinearLayout
+ android:id="@+id/view_key_status_layout"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="This key is expired!"
- android:id="@+id/view_key_expired"
- android:textColor="@color/alert"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center_vertical|center_horizontal"
+ android:layout_gravity="center"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
- android:visibility="gone" />
+ android:orientation="horizontal">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="This key has been revoked!"
- android:id="@+id/view_key_revoked"
- android:textColor="@color/alert"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:gravity="center_vertical|center_horizontal"
- android:visibility="gone"
- android:layout_marginTop="8dp"
- android:layout_marginBottom="8dp" />
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/view_key_status_image" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/view_key_status_text"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="8dp"/>
+
+ </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider"
android:visibility="gone"
- android:id="@+id/status_divider" />
+ android:id="@+id/view_key_status_divider" />
<org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout
android:id="@+id/view_key_sliding_tab_layout"
diff --git a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
index d93420a99..7a54fab05 100644
--- a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml
@@ -17,7 +17,6 @@
style="@style/SectionHeader"
android:layout_width="wrap_content"
android:layout_height="0dp"
- android:layout_marginBottom="4dp"
android:layout_marginTop="8dp"
android:text="@string/section_user_ids"
android:layout_weight="1" />
@@ -35,20 +34,34 @@
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
- <TextView
+ <LinearLayout
android:id="@+id/view_key_action_certify"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/key_view_action_certify"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:drawableRight="@drawable/ic_action_good"
- android:drawablePadding="8dp"
- android:gravity="center_vertical"
+ android:layout_height="?android:attr/listPreferredItemHeight"
android:clickable="true"
- style="@style/SelectableItem" />
+ android:paddingRight="4dp"
+ style="@style/SelectableItem"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/view_key_action_certify_text"
+ android:paddingLeft="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="0dip"
+ android:layout_height="match_parent"
+ android:text="@string/key_view_action_certify"
+ android:layout_weight="1"
+ android:gravity="center_vertical" />
+
+ <ImageView
+ android:id="@+id/view_key_action_certify_image"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:padding="8dp"
+ android:src="@drawable/status_signature_verified_cutout"
+ android:layout_gravity="center_vertical" />
+
+ </LinearLayout>
<TextView
style="@style/SectionHeader"
@@ -84,7 +97,6 @@
android:id="@+id/view_key_action_encrypt"
android:paddingLeft="8dp"
android:paddingRight="8dp"
- android:layout_marginBottom="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -97,6 +109,28 @@
android:drawablePadding="8dp"
android:gravity="center_vertical" />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
+ <TextView
+ android:id="@+id/view_key_action_update"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_marginBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:text="@string/key_view_action_update"
+ android:layout_weight="1"
+ android:drawableRight="@drawable/ic_action_download"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical" />
+
</LinearLayout>
</ScrollView>
diff --git a/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml
index 1cd2b9f1b..a8786c461 100644
--- a/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_share_fragment.xml
@@ -129,6 +129,29 @@
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
+ <TextView
+ android:id="@+id/view_key_action_upload"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_marginBottom="8dp"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:clickable="true"
+ style="@style/SelectableItem"
+ android:text="@string/key_view_action_upload"
+ android:layout_weight="1"
+ android:drawableRight="@drawable/ic_action_upload"
+ android:drawablePadding="8dp"
+ android:gravity="center_vertical"
+ android:editable="false" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:background="?android:attr/listDivider" />
+
<LinearLayout
android:id="@+id/view_key_action_nfc_help"
android:layout_width="match_parent"
diff --git a/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml b/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml
index 7de2f9c05..157903000 100644
--- a/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml
+++ b/OpenKeychain/src/main/res/layout/view_key_user_id_item.xml
@@ -24,7 +24,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/certified"
- android:src="@drawable/key_certify_ok_self"
android:layout_gravity="center_horizontal" />
</LinearLayout>
@@ -64,9 +63,9 @@
</LinearLayout>
<ImageView
+ android:id="@+id/edit_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:id="@+id/edit_image"
android:src="@drawable/ic_action_edit"
android:padding="8dp"
android:layout_gravity="center_horizontal" />
diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml
index e865df182..056dd5986 100644
--- a/OpenKeychain/src/main/res/menu/key_list.xml
+++ b/OpenKeychain/src/main/res/menu/key_list.xml
@@ -10,7 +10,7 @@
app:showAsAction="collapseActionView|ifRoom" />
<item
- android:id="@+id/menu_key_list_import"
+ android:id="@+id/menu_key_list_add"
app:showAsAction="ifRoom|withText"
android:icon="@drawable/ic_action_add_person"
android:title="@string/menu_add_keys" />
@@ -27,6 +27,11 @@
android:title="@string/menu_create_key" />
<item
+ android:id="@+id/menu_key_list_import_existing_key"
+ app:showAsAction="never"
+ android:title="@string/menu_import_existing_key" />
+
+ <item
android:id="@+id/menu_key_list_debug_read"
app:showAsAction="never"
android:title="Debug / DB restore"
diff --git a/OpenKeychain/src/main/res/menu/key_view.xml b/OpenKeychain/src/main/res/menu/key_view.xml
index 64877d725..5a156b5ac 100644
--- a/OpenKeychain/src/main/res/menu/key_view.xml
+++ b/OpenKeychain/src/main/res/menu/key_view.xml
@@ -3,23 +3,6 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/menu_key_keyserver"
- android:icon="@drawable/ic_action_cloud"
- app:showAsAction="always"
- android:title="@string/menu_key_server">
- <menu>
- <item
- android:id="@+id/menu_key_view_update"
- app:showAsAction="never"
- android:title="@string/menu_update_key" />
- <item
- android:id="@+id/menu_key_view_export_keyserver"
- app:showAsAction="never"
- android:title="@string/menu_export_key_to_server" />
- </menu>
- </item>
-
- <item
android:id="@+id/menu_key_view_export_file"
android:icon="@drawable/ic_action_import_export"
app:showAsAction="never"
diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml
index de6962dc9..9d9868457 100644
--- a/OpenKeychain/src/main/res/values-de/strings.xml
+++ b/OpenKeychain/src/main/res/values-de/strings.xml
@@ -247,8 +247,8 @@
<string name="error_only_files_are_supported">Binäre Daten ohne ohne Datei im Dateisystem werden nicht unterstützt.</string>
<string name="error_jelly_bean_needed">Android 4.1 wird benötigt um Androids NFC Beam nutzen zu können!</string>
<string name="error_nfc_needed">NFC steht auf diesem Gerät nicht zur Verfügung!</string>
- <string name="error_keyserver_insufficient_query">zu kurze Schlüsselanfrage</string>
- <string name="error_keyserver_too_many_responses">Die Schlüsselanfrage liefert zu viele Ergebnisse. Bitte verfeinern sie sie Anfrage.</string>
+ <string name="error_query_too_short">zu kurze Schlüsselanfrage</string>
+ <string name="error_too_many_responses">Die Schlüsselanfrage liefert zu viele Ergebnisse. Bitte verfeinern sie sie Anfrage.</string>
<string name="error_generic_report_bug">Ein allgemeiner Fehler trat auf, bitte schreiben Sie einen neuen Bugreport für OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">Ein Teil der geladenen Datei ist ein gültiges OpenPGP Objekt aber kein OpenPGP Schlüssel</item>
diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml
index 50afd5c57..719ddf414 100644
--- a/OpenKeychain/src/main/res/values-es/strings.xml
+++ b/OpenKeychain/src/main/res/values-es/strings.xml
@@ -250,10 +250,10 @@
<string name="error_jelly_bean_needed">¡Necesita Android 4.1 para usar la característica NFC Beam (haz NFC) de Android!</string>
<string name="error_nfc_needed">¡NFC no está disponible en tu dispositivo!</string>
<string name="error_nothing_import">¡No se encontraron claves!</string>
- <string name="error_keyserver_insufficient_query">Petición de búsqueda de clave demasiado corta</string>
+ <string name="error_query_too_short">Petición de búsqueda de clave demasiado corta</string>
<string name="error_searching_keys">Error irrecuperable buscando claves en el servidor</string>
- <string name="error_keyserver_too_many_responses">La petición de búsqueda de clave devolvió demasiados candidatos; por favor refine su petición</string>
- <string name="error_import_file_no_content">El Fichero/Portapapeles está vacío</string>
+ <string name="error_too_many_responses">La petición de búsqueda de clave devolvió demasiados candidatos; por favor refine su petición</string>
+ <string name="error_import_no_valid_keys">El Fichero/Portapapeles está vacío</string>
<string name="error_generic_report_bug">Ha ocurrido un error genérico, por favor, informa de este bug a OpenKeychain</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">parte del archivo cargado es un objeto OpenPGP válido pero no una clave OpenPGP</item>
@@ -370,7 +370,7 @@
<item quantity="one">Clave%2$s importada con éxito.</item>
<item quantity="other">%1$d claves%2$s importadas con éxito.</item>
</plurals>
- <string name="import_view_log">Ver registro (log)</string>
+ <string name="view_log">Ver registro (log)</string>
<string name="import_error_nothing">No hay nada que importar.</string>
<string name="import_error">¡Error importando claves!</string>
<string name="import_with_warnings">, con advertencias</string>
diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml
index f3627e9eb..0c5c5de67 100644
--- a/OpenKeychain/src/main/res/values-fr/strings.xml
+++ b/OpenKeychain/src/main/res/values-fr/strings.xml
@@ -250,10 +250,10 @@
<string name="error_jelly_bean_needed">Il vous faut Android 4.1 pour utiliser la fonction Beam NFC d\'Android !</string>
<string name="error_nfc_needed">La NFC n\'est pas disponible sur votre appareil !</string>
<string name="error_nothing_import">Aucune clef trouvée !</string>
- <string name="error_keyserver_insufficient_query">La requête de recherche de clef est trop courte</string>
+ <string name="error_query_too_short">La requête de recherche de clef est trop courte</string>
<string name="error_searching_keys">Erreur irrécupérable lors de la recherche de clef sur le serveur</string>
- <string name="error_keyserver_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner la requête</string>
- <string name="error_import_file_no_content">Le fichier/le presse-papiers est vide</string>
+ <string name="error_too_many_responses">La requête de recherche de clef a retourné trop de candidats. Veuillez raffiner la requête</string>
+ <string name="error_import_no_valid_keys">Le fichier/le presse-papiers est vide</string>
<string name="error_generic_report_bug">Une erreur générique est survenue, veuillez créer un nouveau rapport de bogue pour OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">une partie du fichier chargé est un objet OpenPGP valide mais pas une clef OpenPGP</item>
@@ -358,7 +358,7 @@
<item quantity="one">Clef importée avec succès</item>
<item quantity="other">%1$d clefs importées avec succès</item>
</plurals>
- <string name="import_view_log">Consulter le journal</string>
+ <string name="view_log">Consulter le journal</string>
<string name="import_error_nothing">Rien à importer.</string>
<string name="import_error">Erreur lors de l\'importation des clefs !</string>
<string name="import_with_warnings">, avec des avertissements</string>
diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml
index c8ec2614d..074efb78b 100644
--- a/OpenKeychain/src/main/res/values-it/strings.xml
+++ b/OpenKeychain/src/main/res/values-it/strings.xml
@@ -250,10 +250,10 @@
<string name="error_jelly_bean_needed">Devi avere Android 4.1 per usare Android NFC Beam!</string>
<string name="error_nfc_needed">NFC non disponibile nel tuo dispositivo!</string>
<string name="error_nothing_import">Nessuna chiave trovata!</string>
- <string name="error_keyserver_insufficient_query">Chiave della query di ricerca troppo corta</string>
+ <string name="error_query_too_short">Chiave della query di ricerca troppo corta</string>
<string name="error_searching_keys">Errore irreversibile nella ricerca di chiavi sul server</string>
- <string name="error_keyserver_too_many_responses">Chiave della query di ricerca ha generato troppi candidati; Si prega di perfezionare la ricerca</string>
- <string name="error_import_file_no_content">File/Appunti vuoti</string>
+ <string name="error_too_many_responses">Chiave della query di ricerca ha generato troppi candidati; Si prega di perfezionare la ricerca</string>
+ <string name="error_import_no_valid_keys">File/Appunti vuoti</string>
<string name="error_generic_report_bug">Si è verificato un errore generico, si prega di creare una nuova segnalazione di errore per OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">parte del file caricato e\' un oggetto OpenPGP valido, ma non una chave OpenPGP</item>
@@ -354,7 +354,7 @@
<string name="import_clipboard_button">Ottieni chiave dagli appunti</string>
<string name="import_keybase_button">Ottieni chiave da Keybase.io</string>
<!--Import result toast-->
- <string name="import_view_log">Mostra registro</string>
+ <string name="view_log">Mostra registro</string>
<string name="import_error_nothing">Niente da importare</string>
<string name="import_error">Errore di importazione chiavi!</string>
<string name="import_with_warnings">, con avvisi</string>
diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml
index 13463df25..070ee7f5a 100644
--- a/OpenKeychain/src/main/res/values-ja/strings.xml
+++ b/OpenKeychain/src/main/res/values-ja/strings.xml
@@ -246,10 +246,10 @@
<string name="error_jelly_bean_needed">Android NFC Beam機能を使うにはAndroid 4.1 が必要です!</string>
<string name="error_nfc_needed">あなたのデバイスにはNFCが存在しません!</string>
<string name="error_nothing_import">鍵が見当りません!</string>
- <string name="error_keyserver_insufficient_query">鍵検索のクエリが短かすぎます</string>
+ <string name="error_query_too_short">鍵検索のクエリが短かすぎます</string>
<string name="error_searching_keys">サーバでの鍵の検索が回復不可能なエラーになりました</string>
- <string name="error_keyserver_too_many_responses">鍵検索のクエリが沢山の候補を返しました; クエリを精密化してください</string>
- <string name="error_import_file_no_content">ファイル/クリップボードが空です</string>
+ <string name="error_too_many_responses">鍵検索のクエリが沢山の候補を返しました; クエリを精密化してください</string>
+ <string name="error_import_no_valid_keys">ファイル/クリップボードが空です</string>
<string name="error_generic_report_bug">一般エラーが発生しました、この新しいバグの情報をOpenKeychainプロジェクトに送ってください</string>
<plurals name="error_import_non_pgp_part">
<item quantity="other">読み込んだファイルのOpenPGPオブジェクト部分は正しいですが、OpenPGPの鍵ではありません</item>
@@ -358,7 +358,7 @@
<plurals name="import_keys_updated">
<item quantity="other">%1$d の鍵%2$sのアップデートに成功。</item>
</plurals>
- <string name="import_view_log">ログを見る</string>
+ <string name="view_log">ログを見る</string>
<string name="import_error_nothing">インポートするものがありません。</string>
<string name="import_error">鍵のインポートのエラー!</string>
<string name="import_with_warnings">、とワーニング</string>
diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml
index 539a431b5..1e697c8db 100644
--- a/OpenKeychain/src/main/res/values-ru/strings.xml
+++ b/OpenKeychain/src/main/res/values-ru/strings.xml
@@ -254,10 +254,10 @@
<string name="error_jelly_bean_needed">Для использования NFC Beam требуется Android 4.1+ !</string>
<string name="error_nfc_needed">Ваше устройство не поддерживает NFC!</string>
<string name="error_nothing_import">Ключи не найдены!</string>
- <string name="error_keyserver_insufficient_query">Запрос слишком короткий</string>
+ <string name="error_query_too_short">Запрос слишком короткий</string>
<string name="error_searching_keys">Ошибка поиска ключей на сервере</string>
- <string name="error_keyserver_too_many_responses">Поиск ключа вернул слишком много вариантов; Пожалуйста, уточните запрос</string>
- <string name="error_import_file_no_content">Файл/Буфер пуст</string>
+ <string name="error_too_many_responses">Поиск ключа вернул слишком много вариантов; Пожалуйста, уточните запрос</string>
+ <string name="error_import_no_valid_keys">Файл/Буфер пуст</string>
<string name="error_generic_report_bug">Выявлена ошибка. Пожалуйста, сообщите о ней разработчику.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">часть загруженного файла содержит данные OpenPGP, но это не ключ</item>
@@ -371,7 +371,7 @@
<item quantity="few">и обновлено %1$d ключей%2$s.</item>
<item quantity="other">и обновлено %1$d ключей%2$s.</item>
</plurals>
- <string name="import_view_log">Смотреть журнал</string>
+ <string name="view_log">Смотреть журнал</string>
<string name="import_error_nothing">Нет данных для импорта.</string>
<string name="import_error">Ошибка импорта ключей!</string>
<string name="import_with_warnings">, с предупреждениями</string>
diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml
index 61fe6ed86..b9a0c4cb9 100644
--- a/OpenKeychain/src/main/res/values-sl/strings.xml
+++ b/OpenKeychain/src/main/res/values-sl/strings.xml
@@ -254,9 +254,9 @@
<string name="error_only_files_are_supported">Neposredni binarni podatki brez dejanske datoteke v datotečnem sistemu niso podprti.</string>
<string name="error_jelly_bean_needed">Za uporabo storitve NFC Beam potrebujete najmanj Android 4.1!</string>
<string name="error_nfc_needed">NFC ni na voljo na vaši napravi!</string>
- <string name="error_keyserver_insufficient_query">Iskalni pojem je prekratek</string>
+ <string name="error_query_too_short">Iskalni pojem je prekratek</string>
<string name="error_searching_keys">Nepremostljiva napaka pri iskanju ključev na strežniku</string>
- <string name="error_keyserver_too_many_responses">Iskanje ključev je vrnilo preveč zadetkov; prosimo redefinirajte iskalni pojem</string>
+ <string name="error_too_many_responses">Iskanje ključev je vrnilo preveč zadetkov; prosimo redefinirajte iskalni pojem</string>
<string name="error_generic_report_bug">Pripetila se je splošna napaka, prosimo ustvarite poročilo o \'hrošču\'.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">Del naložene datoteke je veljavnen objekt OpenPGP a ni ključ.</item>
diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml
index 6f5cc7cf1..4e267e431 100644
--- a/OpenKeychain/src/main/res/values-uk/strings.xml
+++ b/OpenKeychain/src/main/res/values-uk/strings.xml
@@ -252,9 +252,9 @@
<string name="error_jelly_bean_needed">Вам потрібний Android 4.1 для використання функції Androids NFC промінь!</string>
<string name="error_nfc_needed">NFC недоступний на вашому пристрої!</string>
<string name="error_nothing_import">Ключ не знайдено!</string>
- <string name="error_keyserver_insufficient_query">Запит пошуку ключа надто короткий</string>
+ <string name="error_query_too_short">Запит пошуку ключа надто короткий</string>
<string name="error_searching_keys">Невиправна помилка пошуку ключів в сервері</string>
- <string name="error_keyserver_too_many_responses">Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит</string>
+ <string name="error_too_many_responses">Запит пошуку ключа видав надто багато варіантів. Уточніть пошуковий запит</string>
<string name="error_generic_report_bug">Трапилася загальна помилка, будь ласка, створіть новий звіт про помилку для OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">частина завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
@@ -352,7 +352,7 @@
<string name="import_clipboard_button">Отримати ключ з буфера обміну</string>
<string name="import_keybase_button">Отримати ключ із Keybase.io</string>
<!--Import result toast-->
- <string name="import_view_log">Переглянути журнал</string>
+ <string name="view_log">Переглянути журнал</string>
<string name="import_error_nothing">Нема що імпортувати.</string>
<string name="import_error">Помилка імпорту ключів!</string>
<!--Intent labels-->
diff --git a/OpenKeychain/src/main/res/values/colors.xml b/OpenKeychain/src/main/res/values/colors.xml
index c0042d215..a21f949d1 100644
--- a/OpenKeychain/src/main/res/values/colors.xml
+++ b/OpenKeychain/src/main/res/values/colors.xml
@@ -5,12 +5,19 @@
<color name="emphasis_dark">#9933cc</color>
<color name="bg_gray">#cecbce</color>
<color name="tertiary_text_light">#808080</color>
+ <color name="alert">#ffdd3333</color>
<color name="holo_gray_light">#33999999</color>
<color name="holo_gray_bright">#33CCCCCC</color>
- <color name="result_red">#ffff4444</color>
- <color name="result_orange">#ffffbb33</color>
- <color name="result_green">#ff99cc00</color>
- <color name="result_purple">#aa66cc</color>
+ <!-- http://developer.android.com/design/style/color.html -->
+ <color name="android_red_light">#ffff4444</color>
+ <color name="android_red_dark">#ffCC0000</color>
+ <color name="android_orange_light">#ffffbb33</color>
+ <color name="android_orange_dark">#ffFF8800</color>
+ <color name="android_green_light">#ff99cc00</color>
+ <color name="android_green_dark">#ff669900</color>
+ <color name="android_purple_light">#ffaa66cc</color>
+ <color name="android_purple_dark">#ff9933CC</color>
+
</resources>
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 87b2a83be..432c8bfc9 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -2,12 +2,12 @@
<resources>
<!-- title -->
- <string name="title_select_recipients">Select Public Key</string>
- <string name="title_select_secret_key">Select Secret Key</string>
+ <string name="title_select_recipients">Select Keys</string>
+ <string name="title_select_secret_key">Select Your Key</string>
<string name="title_encrypt">Encrypt</string>
<string name="title_decrypt">Decrypt</string>
<string name="title_authentication">Passphrase</string>
- <string name="title_create_key">Create Key</string>
+ <string name="title_create_key">Create My Key</string>
<string name="title_edit_key">Edit Key</string>
<string name="title_preferences">Preferences</string>
<string name="title_api_registered_apps">Apps</string>
@@ -71,18 +71,17 @@
<string name="btn_encryption_advanced_settings_hide">Hide advanced settings</string>
<string name="btn_share_encrypted_signed">Share encrypted/signed message…</string>
<string name="btn_view_cert_key">View certification key</string>
+ <string name="btn_create_key">Create key</string>
<!-- menu -->
<string name="menu_preferences">Settings</string>
<string name="menu_help">Help</string>
<string name="menu_export_key">Export to file</string>
<string name="menu_delete_key">Delete key</string>
- <string name="menu_create_key">Create key</string>
- <string name="menu_create_key_expert">Create key (expert)</string>
+ <string name="menu_create_key">Create my key</string>
+ <string name="menu_import_existing_key">Import existing key</string>
<string name="menu_search">Search</string>
<string name="menu_key_server">Keyserver…</string>
- <string name="menu_update_key">Update from keyserver</string>
- <string name="menu_export_key_to_server">Upload to key server</string>
<string name="menu_share">Share…</string>
<string name="menu_share_title_fingerprint">Share fingerprint…</string>
<string name="menu_share_title">Share whole key…</string>
@@ -106,7 +105,7 @@
<string name="label_file">File</string>
<string name="label_no_passphrase">No Passphrase</string>
<string name="label_passphrase">Passphrase</string>
- <string name="label_passphrase_again">Again</string>
+ <string name="label_passphrase_again">Repeat Passphrase</string>
<string name="label_algorithm">Algorithm</string>
<string name="label_ascii_armor">ASCII Armor</string>
<string name="label_conceal_pgp_application">Let others know that you\'re using OpenKeychain</string>
@@ -216,7 +215,7 @@
<string name="ask_save_changed_key">You have made changes to the keyring, would you like to save it?</string>
<string name="ask_empty_id_ok">You have added an empty identity, are you sure you want to continue?</string>
<string name="public_key_deletetion_confirmation">Do you really want to delete the public key \'%s\'?\nYou can\'t undo this!</string>
- <string name="also_export_secret_keys">Also export secret keys?</string>
+ <string name="also_export_secret_keys">Also export secret keys</string>
<string name="key_exported">Successfully exported 1 key.</string>
<string name="keys_exported">Successfully exported %d keys.</string>
@@ -269,10 +268,12 @@
<string name="error_jelly_bean_needed">You need Android 4.1 to use Android\'s NFC Beam feature!</string>
<string name="error_nfc_needed">NFC is not available on your device!</string>
<string name="error_nothing_import">No keys found!</string>
- <string name="error_keyserver_insufficient_query">Key search query too short</string>
- <string name="error_searching_keys">Unrecoverable error searching for keys at server</string>
- <string name="error_keyserver_too_many_responses">Key search query returned too many candidates; Please refine query</string>
- <string name="error_import_file_no_content">File/Clipboard is empty</string>
+ <string name="error_query_too_short">Search query too short. Please refine your query!</string>
+ <string name="error_searching_keys">An error occurred when searching for keys.</string>
+ <string name="error_too_many_responses">Key search query returned too many candidates. Please refine your query!</string>
+ <string name="error_too_short_or_too_many_responses">Either no keys or too many have been found. Please improve your query!</string>
+
+ <string name="error_import_no_valid_keys">No valid keys found in File/Clipboard!</string>
<string name="error_generic_report_bug">A generic error occurred, please create a new bug report for OpenKeychain.</string>
<plurals name="error_import_non_pgp_part">
<item quantity="one">part of the loaded file is a valid OpenPGP object but not a OpenPGP key</item>
@@ -296,11 +297,26 @@
<string name="progress_saving">saving…</string>
<string name="progress_importing">importing…</string>
<string name="progress_exporting">exporting…</string>
+ <string name="progress_uploading">uploading…</string>
<string name="progress_building_key">building key…</string>
<string name="progress_certifying_master_key">certifying master key…</string>
<string name="progress_building_master_key">building master ring…</string>
<string name="progress_adding_sub_keys">adding sub keys…</string>
<string name="progress_saving_key_ring">saving key…</string>
+ <string name="progress_generating_rsa">generating new RSA key…</string>
+ <string name="progress_generating_dsa">generating new DSA key…</string>
+ <string name="progress_generating_elgamal">generating new ElGamal key…</string>
+
+ <string name="progress_modify">modifying keyring…</string>
+
+ <string name="progress_modify_unlock">unlocking keyring…</string>
+ <string name="progress_modify_adduid">adding user ids…</string>
+ <string name="progress_modify_revokeuid">revoking user ids…</string>
+ <string name="progress_modify_primaryuid">changing primary user id…</string>
+ <string name="progress_modify_subkeychange">modifying subkeys…</string>
+ <string name="progress_modify_subkeyrevoke">revoking subkeys…</string>
+ <string name="progress_modify_subkeyadd">adding subkeys…</string>
+ <string name="progress_modify_passphrase">changing passphrase…</string>
<plurals name="progress_exporting_key">
<item quantity="one">exporting key…</item>
@@ -322,6 +338,7 @@
<string name="progress_processing_signature">processing signature…</string>
<string name="progress_verifying_signature">verifying signature…</string>
<string name="progress_signing">signing…</string>
+ <string name="progress_certifying">certifying…</string>
<string name="progress_reading_data">reading data…</string>
<string name="progress_finding_key">finding key…</string>
<string name="progress_decompressing_data">decompressing data…</string>
@@ -330,7 +347,6 @@
<!-- action strings -->
<string name="hint_public_keys">Name/Email/Key ID…</string>
- <string name="hint_secret_keys">Search Secret Keys</string>
<string name="action_share_key_with">Share Key with…</string>
<string name="hint_keybase_search">Name/Keybase.io username…</string>
@@ -391,8 +407,8 @@
<item quantity="other">Successfully imported %1$d keys</item>
</plurals>
<plurals name="import_keys_added_and_updated_2">
- <item quantity="one"> and updated key%2$s.</item>
- <item quantity="other"> and updated %1$d keys%2$s.</item>
+ <item quantity="one">and updated key%2$s.</item>
+ <item quantity="other">and updated %1$d keys%2$s.</item>
</plurals>
<plurals name="import_keys_added">
<item quantity="one">Successfully imported key%2$s.</item>
@@ -402,7 +418,7 @@
<item quantity="one">Successfully updated key%2$s.</item>
<item quantity="other">Successfully updated %1$d keys%2$s.</item>
</plurals>
- <string name="import_view_log">View Log</string>
+ <string name="view_log">View Log</string>
<string name="import_error_nothing">Nothing to import.</string>
<string name="import_error">Error importing keys!</string>
<string name="import_with_warnings">, with warnings</string>
@@ -431,7 +447,7 @@
<string name="api_settings_package_signature">SHA-256 of Package Signature</string>
<string name="api_settings_accounts">Accounts</string>
<string name="api_settings_accounts_empty">No accounts attached to this app.</string>
- <string name="api_create_account_text">The app requests the creation of a new account. Please select an existing private key or create a new one.\nApps are restricted to the usage of keys you select here!</string>
+ <string name="api_create_account_text">The app requests the creation of a new account. Please select one of your existing keys or create a new one.\nApps are restricted to the usage of keys you select here!</string>
<string name="api_register_text">The displayed app wants to encrypt/decrypt messages and sign them in your name.\nAllow access?\n\nWARNING: If you do not know why this screen appeared, disallow access! You can revoke access later using the \'Apps\' screen.</string>
<string name="api_register_allow">Allow access</string>
<string name="api_register_disallow">Disallow access</string>
@@ -458,14 +474,16 @@
<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</string>
- <string name="key_list_empty_button_import">importing keys.</string>
+ <string name="key_list_empty_button_import">importing an existing key.</string>
<!-- Key view -->
<string name="key_view_action_edit">Edit key</string>
<string name="key_view_action_encrypt">Encrypt with this key</string>
<string name="key_view_action_certify">Certify identities</string>
+ <string name="key_view_action_update">Update from keyserver</string>
<string name="key_view_action_share_with">Share with…</string>
<string name="key_view_action_share_nfc">Share over NFC by holding the devices back to back</string>
+ <string name="key_view_action_upload">Upload to key server</string>
<string name="key_view_tab_main">Main Info</string>
<string name="key_view_tab_share">Share</string>
<string name="key_view_tab_keys">Subkeys</string>
@@ -486,6 +504,19 @@
<item>Revoke Subkey</item>
</string-array>
+ <!-- Create key -->
+ <string name="create_key_upload">Upload key to keyserver</string>
+ <string name="create_key_empty">This field is required</string>
+ <string name="create_key_passphrases_not_equal">Passphrases do not match</string>
+ <string name="create_key_final_text">You entered the following identity:</string>
+ <string name="create_key_final_robot_text">Creating a key may take a while, have a cup of coffee in the meantime…\n(3 subkeys, RSA, 4096 bit)</string>
+ <string name="create_key_text">Enter your full name, email address, and choose a passhrase.</string>
+ <string name="create_key_hint_full_name">Full Name, e.g. Max Mustermann</string>
+
+ <!-- View key -->
+ <string name="view_key_revoked">This key has been revoked!</string>
+ <string name="view_key_expired">This key is expired!</string>
+
<!-- Navigation Drawer -->
<string name="nav_keys">Keys</string>
<string name="nav_encrypt">Sign and Encrypt</string>
@@ -496,9 +527,6 @@
<string name="drawer_close">Close navigation drawer</string>
<string name="edit">Edit</string>
<string name="my_keys">My Keys</string>
- <string name="label_secret_key">Secret Key</string>
- <string name="secret_key_yes">available</string>
- <string name="secret_key_no">unavailable</string>
<!-- hints -->
<string name="encrypt_content_edit_text_hint">Write message here to encrypt and/or sign…</string>
@@ -569,6 +597,7 @@
<string name="msg_ip_uid_processing">Processing user id %s</string>
<string name="msg_ip_uid_revoked">User id is revoked</string>
<string name="msg_is_bad_type_public">Tried to import public keyring as secret. This is a bug, please file a report!</string>
+ <string name="msg_is_bad_type_uncanon">Tried to import a keyring without canonicalization. This is a bug, please file a report!</string>
<!-- Import Secret log entries -->
<string name="msg_is">Importing secret key %s</string>
@@ -673,6 +702,7 @@
<string name="msg_mf_uid_add">Adding user id %s</string>
<string name="msg_mf_uid_primary">Changing primary uid to %s</string>
<string name="msg_mf_uid_revoke">Revoking user id %s</string>
+ <string name="msg_mf_uid_error_empty">User ID must not be empty!</string>
<string name="msg_mf_unlock_error">Error unlocking keyring!</string>
<string name="msg_mf_unlock">Unlocking keyring</string>
@@ -684,6 +714,7 @@
<string name="passp_cache_notif_pwd">Password</string>
<!-- unsorted -->
+ <string name="internal_error">Internal error!</string>
<string name="section_certifier_id">Certifier</string>
<string name="section_cert">Certificate Details</string>
<string name="label_user_id">Identity</string>
@@ -697,7 +728,7 @@
<string name="error_key_processing">Error processing key!</string>
<string name="no_subkey">subkey unavailable</string>
<string name="key_stripped">stripped</string>
- <string name="secret_cannot_multiple">Secret keys can only be deleted individually!</string>
+ <string name="secret_cannot_multiple">Your own keys can only be deleted individually!</string>
<string name="title_view_cert">View Certificate Details</string>
<string name="unknown_algorithm">unknown</string>
<string name="can_sign_not">cannot sign</string>
@@ -708,8 +739,8 @@
<!-- First Time -->
<string name="first_time_text1">Take back your privacy with OpenKeychain!</string>
- <string name="first_time_create_key">Create Key</string>
- <string name="first_time_import_key">Import Key</string>
+ <string name="first_time_create_key">Create my key</string>
+ <string name="first_time_import_key">Import existing key</string>
<string name="first_time_skip">Skip Setup</string>
</resources>
diff --git a/README.md b/README.md
index 230fd2d6f..509c57e63 100644
--- a/README.md
+++ b/README.md
@@ -221,10 +221,6 @@ Some parts and some libraries are Apache License v2, MIT X11 License (see below)
https://github.com/Bearded-Hen/Android-Bootstrap
MIT License
-* Android AppMsg
- https://github.com/johnkil/Android-AppMsg
- Apache License v2
-
### Images
* icon.svg
modified version of kgpg_key2_kopete.svgz
diff --git a/Resources/graphics/create_key_robot.svg b/Resources/graphics/create_key_robot.svg
new file mode 100644
index 000000000..7301dc5bb
--- /dev/null
+++ b/Resources/graphics/create_key_robot.svg
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="45"
+ height="45"
+ id="svg4316"
+ version="1.1"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="create_key_robot.svg">
+ <defs
+ id="defs4318" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="5.6"
+ inkscape:cx="19.859075"
+ inkscape:cy="10.23901"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="2558"
+ inkscape:window-height="1419"
+ inkscape:window-x="0"
+ inkscape:window-y="19"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata4321">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-1007.3622)">
+ <g
+ id="g3761">
+ <path
+ id="path5399"
+ d="m 22.166071,1009.3123 c -5.613738,0 -10.131173,4.5175 -10.131173,10.1312 l 0,18.6956 c 0,5.6138 4.517435,10.1365 10.131173,10.1365 l 0.6677,0 c 5.613732,0 10.131173,-4.5227 10.131173,-10.1365 l 0,-18.6956 c 0,-5.6137 -4.517441,-10.1312 -10.131173,-10.1312 l -0.6677,0 z m 0.336476,3.3649 c 4.044517,0 7.223786,3.1791 7.223786,7.1291 l 0,14.9628 c 0,3.95 -3.179269,7.1292 -7.223786,7.1292 -4.044516,0 -7.229038,-3.1792 -7.229038,-7.1292 l 0,-14.9628 c 0,-3.95 3.184522,-7.1291 7.229038,-7.1291 z"
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:1.74663651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:1.74663651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="rect5401"
+ width="33.961983"
+ height="24.945528"
+ x="5.5190063"
+ y="1025.4666"
+ rx="0.79848224"
+ ry="0.79848224" />
+ <path
+ style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:1.74663651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ d="m 11.109664,1043.5389 c 2.495006,2.065 6.666898,3.4226 11.392966,3.4226 4.725665,0 8.89263,-1.3579 11.387713,-3.4226 z"
+ id="path5403"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cscc" />
+ <path
+ sodipodi:type="arc"
+ style="color:#000000;fill:none;stroke:#669900;stroke-width:9.60078144;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ id="path5405"
+ sodipodi:cx="307.48431"
+ sodipodi:cy="407.66223"
+ sodipodi:rx="9.388834"
+ sodipodi:ry="9.388834"
+ d="m 316.87315,407.66223 a 9.388834,9.388834 0 1 1 -18.77767,0"
+ transform="matrix(0.1819265,0,0,0.1819265,-43.957774,964.33702)"
+ sodipodi:start="0"
+ sodipodi:end="3.1415927"
+ sodipodi:open="true" />
+ <path
+ d="m 316.87315,407.66223 a 9.388834,9.388834 0 1 1 -18.77767,0 9.388834,9.388834 0 1 1 18.77767,0 z"
+ sodipodi:ry="9.388834"
+ sodipodi:rx="9.388834"
+ sodipodi:cy="407.66223"
+ sodipodi:cx="307.48431"
+ id="path5407"
+ style="color:#000000;fill:#669900;fill-opacity:1;fill-rule:nonzero;stroke:#669900;stroke-width:9.60078144000000044;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+ sodipodi:type="arc"
+ transform="matrix(0.1819265,0,0,0.1819265,-21.704758,965.01923)" />
+ </g>
+ </g>
+</svg>
diff --git a/Resources/gnupg-infographic/first_time_1.png b/Resources/graphics/first_time_1.png
index 1f340df5c..1f340df5c 100644
--- a/Resources/gnupg-infographic/first_time_1.png
+++ b/Resources/graphics/first_time_1.png
Binary files differ
diff --git a/Resources/gnupg-infographic/first_time_1.svg b/Resources/graphics/first_time_1.svg
index 1f40c5ff3..1f40c5ff3 100644
--- a/Resources/gnupg-infographic/first_time_1.svg
+++ b/Resources/graphics/first_time_1.svg
diff --git a/Resources/gnupg-infographic/README b/Resources/graphics/originals/gnupg-infographic/README
index f29c0a84a..f29c0a84a 100644
--- a/Resources/gnupg-infographic/README
+++ b/Resources/graphics/originals/gnupg-infographic/README
diff --git a/Resources/gnupg-infographic/gnupg-infographic.png b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.png
index 52b8f21ac..52b8f21ac 100644
--- a/Resources/gnupg-infographic/gnupg-infographic.png
+++ b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.png
Binary files differ
diff --git a/Resources/gnupg-infographic/gnupg-infographic.svg b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.svg
index 9a17421e2..9a17421e2 100644
--- a/Resources/gnupg-infographic/gnupg-infographic.svg
+++ b/Resources/graphics/originals/gnupg-infographic/gnupg-infographic.svg
diff --git a/Resources/graphics/ic_action_nfc/NFC.png b/Resources/graphics/originals/ic_action_nfc/NFC.png
index 96af64049..96af64049 100644
--- a/Resources/graphics/ic_action_nfc/NFC.png
+++ b/Resources/graphics/originals/ic_action_nfc/NFC.png
Binary files differ
diff --git a/Resources/graphics/ic_action_qr_code/ic_menu_qr_code.svg b/Resources/graphics/originals/ic_action_qr_code/ic_menu_qr_code.svg
index 5cbe9defc..5cbe9defc 100644
--- a/Resources/graphics/ic_action_qr_code/ic_menu_qr_code.svg
+++ b/Resources/graphics/originals/ic_action_qr_code/ic_menu_qr_code.svg
diff --git a/Resources/graphics/ic_launcher/AUTHORS b/Resources/graphics/originals/ic_launcher/AUTHORS
index dbfcfb4fc..dbfcfb4fc 100644
--- a/Resources/graphics/ic_launcher/AUTHORS
+++ b/Resources/graphics/originals/ic_launcher/AUTHORS
diff --git a/Resources/graphics/ic_launcher/COPYING b/Resources/graphics/originals/ic_launcher/COPYING
index 2faa27568..2faa27568 100644
--- a/Resources/graphics/ic_launcher/COPYING
+++ b/Resources/graphics/originals/ic_launcher/COPYING
diff --git a/Resources/graphics/ic_launcher/kgpg_key2_kopete.svgz b/Resources/graphics/originals/ic_launcher/kgpg_key2_kopete.svgz
index 2d43afb83..2d43afb83 100644
--- a/Resources/graphics/ic_launcher/kgpg_key2_kopete.svgz
+++ b/Resources/graphics/originals/ic_launcher/kgpg_key2_kopete.svgz
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/README.md b/Resources/graphics/originals/modernpgp-icons/README.md
new file mode 100644
index 000000000..c3cc37e5d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/README.md
@@ -0,0 +1,97 @@
+Icons
+=====
+
+A collection of icons representing PGP's implementation in graphical interfaces. Icons are grouped together according to Encryption, Key, and Signature.
+
+
+Projects
+========
+
+* [Mailpile](https://github.com/pagekite/Mailpile) - A fast modern webmail client for Mac, Linux, and Windows
+* [OpenKeychain](https://github.com/open-keychain/open-keychain) - An Android app for managing PGP keys
+* [GPGTools](https://github.com/GPGTools) - A well designed GUI interface for managing PGP keys on Mac OS
+
+
+Encryption
+==========
+
+### Open Lock
+
+The open lock icon SHOULD be used to represent a message or data that is going to be sent "unencrypted"
+
+![Unncrypted Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/encryption/lock-open.png)
+
+
+### Closed Lock
+
+The closed lock icon is used to represent data that WAS encrypted but HAS BEEN successfully decrypted or used for outgoing message or data that WILL BE encrypted.
+
+![Encrypted Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/encryption/lock-closed.png)
+
+
+### Error Lock
+
+The error lock icon represents data that IS encrypted and COULD NOT be decrypted.
+
+![Error Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/encryption/lock-error.png)
+
+
+Keys
+====
+
+### Fingerprint
+Use this icon to represent fingrprint of a PGP key
+
+![Fingerprint Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/keys/icon-fingerprint.png)
+
+### Key
+Use this icon to represent a PGP key or Key ID
+
+![Key Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/keys/icon-key.png)
+
+
+Signatures
+==========
+
+Here are the signature state names + description that Mailpile is currently using.
+
+
+### Invalid
+The signature was invalid or bad
+
+![Invalid Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-invalid-cutout.png)
+
+
+### Revoked
+Watch out, the signature was made with a key that has been revoked- this is not a good thing
+
+![Revoked Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-revoked-cutout.png)
+
+
+### Expired
+The signature was made with an expired key
+
+![Expired Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-expired-cutout.png)
+
+
+### Unknown
+The signature was made with an unknown key, so we can not verify it
+
+![Unknown Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-unknown-cutout.png)
+
+
+### Unverified
+The signature was good but it came from a key that is not verified yet
+
+![Unverified Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-unverified-cutout.png)
+
+
+### Verified
+The signature was good and came from a verified key, w00t!
+
+![Verified Signature Icon](https://raw.githubusercontent.com/ModernPGP/icons/master/signatures/signature-verified-cutout.png)
+
+
+### Error & None
+The "error" state exists when there was a weird programatic error trying to analyze this signature. The "none" state is what some API's return when there is no signature of a message. There is no icons for either of these states.
+
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.png
new file mode 100644
index 000000000..0003ce164
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg
new file mode 100644
index 000000000..286e89297
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-closed</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-closed" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M81.502,45.132 L79.577,45.132 L79.577,29.479 C79.479,10.285 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.182,46.109 81.727,45.132 81.502,45.132 L81.502,45.132 Z M59.334,86.055 L41.061,86.055 L46.024,71.489 C43.904,70.077 42.496,67.623 42.496,64.824 C42.496,60.44 45.938,56.886 50.183,56.886 C54.428,56.886 57.87,60.443 57.87,64.824 C57.87,67.619 56.466,70.077 54.348,71.485 L59.334,86.055 L59.334,86.055 Z M34.261,45.132 L34.277,29.686 C34.277,19.737 40.348,11.783 50.183,11.783 C59.924,11.783 66.088,18.741 66.088,29.686 L66.098,45.132 L34.261,45.132 L34.261,45.132 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.png
new file mode 100644
index 000000000..693b7c6f7
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.png
new file mode 100644
index 000000000..6dea7ba27
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.png
new file mode 100644
index 000000000..7cc3b343b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-closed@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.png
new file mode 100644
index 000000000..e2bd291d2
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg
new file mode 100644
index 000000000..d3c4e1d1d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-error</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-error" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M80.459,45.474 L78.533,45.474 L78.533,29.826 C78.435,10.633 65.344,0.183 49.433,0.183 C33.527,0.183 19.265,11.128 19.761,29.826 L19.745,45.454 C19.745,45.454 19.985,45.475 18.784,45.475 C17.514,45.475 9.145,46.946 9.145,54.407 L9.145,90.228 C9.145,98.187 18.565,100.171 18.784,100.171 L79.984,100.171 C80.203,100.171 89.138,99.178 89.138,90.228 L89.138,54.901 C89.139,46.452 80.684,45.474 80.459,45.474 L80.459,45.474 Z M33.234,30.033 C33.234,20.084 39.304,12.131 49.14,12.131 C58.881,12.131 65.045,19.088 65.045,30.033 L65.055,45.474 L33.218,45.474 L33.234,30.033 L33.234,30.033 Z M59.4033767,90.873 L48.4582822,79.9279055 L38.2296593,90.3644491 L31.6365,83.7568884 L42.5824946,72.8153942 L32.3439707,62.5939721 L38.7544118,56.1079235 L49.7013065,67.0503177 L60.1234487,56.7100837 L66.6365,63.2240351 L55.6896053,74.1673294 L66.0091373,84.4778605 L59.4033767,90.873 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.png
new file mode 100644
index 000000000..6a4ddf7ee
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.png
new file mode 100644
index 000000000..225a82f43
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.png
new file mode 100644
index 000000000..22c5cb14b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-error@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.png
new file mode 100644
index 000000000..30b57db5d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg
new file mode 100644
index 000000000..9beb127af
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-open</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-open" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M79.577,26.479 C79.577,10.833 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.181,46.107 81.726,45.13 81.5,45.13 L34.259,45.13 L34.275,29.684 C34.275,19.735 40.346,11.781 50.181,11.781 C59.922,11.781 66.664,18.164 66.664,29.164 L79.577,26.479 Z M59.334,86.055 L41.061,86.055 L46.024,71.49 C43.904,70.078 42.496,67.624 42.496,64.825 C42.496,60.44 45.938,56.887 50.183,56.887 C54.428,56.887 57.87,60.445 57.87,64.825 C57.87,67.62 56.466,70.078 54.348,71.485 L59.334,86.055 L59.334,86.055 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.png
new file mode 100644
index 000000000..056e1b64b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.png
new file mode 100644
index 000000000..a0a12eada
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.png b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.png
new file mode 100644
index 000000000..06a576338
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/encryption/lock-open@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.png
new file mode 100644
index 000000000..c4b79f295
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg
new file mode 100644
index 000000000..a5ad87050
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>icon-fingerprint</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="icon-fingerprint" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M46.214961,59.4153846 C46.6578855,58.1692308 46.0890176,56.7692308 44.9413761,56.2861538 C43.80081,55.8061538 42.5074138,56.4230769 42.0644893,57.6692308 C39.3673195,65.2492308 29.7984516,79.6507692 20.939961,84.9630769 C19.8644893,85.6107692 19.4739233,87.0815385 20.0668478,88.2507692 C20.4729799,89.0492308 21.2343006,89.5 22.0182629,89.5 C22.380527,89.5 22.7498667,89.4030769 23.0923195,89.2 C33.3475082,83.0492308 43.27581,67.6830769 46.214961,59.4153846 L46.214961,59.4153846 L46.214961,59.4153846 Z M54.2159044,64.0892308 C57.1465648,54.9215385 53.5069421,48.7230769 48.6036402,46.6476923 C43.6437346,44.5446154 36.7748667,46.3707692 33.7960931,53.8676923 C31.2630742,60.24 24.7225082,71.3646154 17.0824138,75.8953846 C16.0041119,76.5353846 15.6064704,78.0046154 16.1965648,79.1784615 C16.785244,80.3476923 18.1366591,80.7830769 19.2163761,80.1415385 C28.5135459,74.6230769 35.4913761,61.7969231 37.8814704,55.7861538 C39.7564704,51.0692308 43.9677912,49.8753846 46.9904327,51.1553846 C49.8220365,52.3523077 52.1031685,55.9630769 50.0144893,62.5015385 C46.7597723,72.6784615 34.6918478,86.4707692 26.8692063,91.1061538 C25.7894893,91.7446154 25.3918478,93.2138462 25.980527,94.3876923 C26.385244,95.1938462 27.1479799,95.6492308 27.9361874,95.6492308 C28.2942063,95.6492308 28.6593006,95.5553846 28.9989233,95.3538462 C37.6819421,90.2092308 50.5890176,75.4307692 54.2159044,64.0892308 L54.2159044,64.0892308 L54.2159044,64.0892308 Z M51.6319421,38.1907692 C48.2470365,36.76 44.5762817,36.3876923 41.0328855,37.1153846 C34.4385459,38.4661538 28.8701497,43.3830769 26.1319421,50.2676923 C23.2720365,57.4676923 19.0522251,62.6830769 13.5857157,65.7692308 C12.494678,66.3861538 12.0715648,67.8461538 12.6361874,69.0307692 C13.2050553,70.2138462 14.5437346,70.6753846 15.6361874,70.0630769 C22.035244,66.4492308 26.9413761,60.4323077 30.2159044,52.1846154 C32.3654327,46.7769231 36.7168478,42.92 41.8536402,41.8692308 C44.5932629,41.3092308 47.4135459,41.5923077 50.0173195,42.6969231 C56.4503384,45.4169231 61.6593006,53.7061538 57.8965648,65.4784615 C54.3064704,76.7061538 43.664961,89.4338462 35.0059987,96.2046154 C34.0083572,96.9846154 33.780527,98.4953846 34.499395,99.58 C34.9338289,100.236923 35.6144893,100.586154 36.3078855,100.586154 C36.7578855,100.586154 37.2121308,100.436923 37.605527,100.130769 C46.8319421,92.9184615 58.2078855,79.2430769 62.099395,67.0661538 C66.8328855,52.2692308 59.9909044,41.7246154 51.6319421,38.1907692 L51.6319421,38.1907692 L51.6319421,38.1907692 Z M54.6588289,29.7338462 C49.8291119,27.6892308 44.5833572,27.1584615 39.4876025,28.1984615 C30.1833572,30.1030769 22.9635459,35.76 19.1526968,44.1323077 L19.0465648,44.3646154 C17.7347723,47.2553846 16.3776968,50.2446154 14.114961,50.9969231 C12.9390176,51.3876923 12.2767534,52.74 12.6376025,54.0184615 C12.9956214,55.2953846 14.2451497,56.0138462 15.4140176,55.6246154 C19.5163761,54.26 21.4692063,49.9615385 23.0385459,46.5061538 L23.1432629,46.2753846 C26.3173195,39.3 32.414961,34.5676923 40.3126025,32.9523077 C44.580527,32.0753846 48.9871308,32.5230769 53.0470365,34.2415385 C63.4111874,38.6246154 71.1135459,51.7861538 65.7814704,68.4584615 C63.3475082,76.0676923 58.1286402,84.7061538 51.0843006,92.78 C50.2394893,93.7507692 50.2762817,95.2815385 51.1663761,96.2 C51.5965648,96.6446154 52.1484516,96.8630769 52.6989233,96.8630769 C53.2876025,96.8630769 53.8762817,96.6107692 54.3135459,96.1092308 C61.7838289,87.5430769 67.3479799,78.2892308 69.9843006,70.0446154 C76.2418478,50.4830769 67.0111874,34.9569231 54.6588289,29.7338462 L54.6588289,29.7338462 L54.6588289,29.7338462 Z M57.689961,21.2784615 C51.3998667,18.6123077 44.5635459,17.9261538 37.9437346,19.28 C28.2319421,21.2692308 20.1121308,27.2846154 14.4644893,36.6784615 C13.7909044,37.7969231 14.0795836,39.2953846 15.1097723,40.0276923 C16.139961,40.76 17.519678,40.4476923 18.1890176,39.3276923 C23.2295836,30.9446154 30.1522251,25.8 38.7659044,24.0369231 C44.580527,22.8461538 50.564961,23.4538462 56.0767534,25.7892308 C69.8215648,31.6015385 80.6597723,49.5784615 73.6677912,71.44 C72.6531685,74.6076923 71.2748667,77.8723077 69.5725082,81.14 C68.9668478,82.3015385 69.3418478,83.7784615 70.4116591,84.4384615 C70.7597723,84.6507692 71.1347723,84.7523077 71.5069421,84.7523077 C72.2824138,84.7523077 73.0338289,84.3107692 73.4456214,83.5261538 C75.2809987,80.0046154 76.769678,76.4753846 77.8720365,73.0307692 C85.7781685,48.3030769 73.3734516,27.9107692 57.689961,21.2784615 L57.689961,21.2784615 L57.689961,21.2784615 Z M62.7135459,13.4507692 C49.6706214,7.52 34.6904327,8.71384615 22.635244,16.6369231 C21.5838289,17.3292308 21.2470365,18.8153846 21.8838289,19.96 C22.5163761,21.1015385 23.880527,21.4676923 24.9390176,20.7784615 C35.7828855,13.6507692 49.2630742,12.5784615 60.9970365,17.9153846 C72.2003384,23.0107692 79.980527,33.14 82.9026968,46.4369231 C83.1460931,47.5476923 84.0588289,48.2938462 85.064961,48.2938462 C85.2361874,48.2938462 85.410244,48.2723077 85.5843006,48.2276923 C86.7800553,47.9169231 87.5159044,46.6123077 87.2314704,45.3123077 C83.969678,30.4738462 75.2611874,19.1584615 62.7135459,13.4507692 L62.7135459,13.4507692 L62.7135459,13.4507692 Z M35.639961,6.20461538 C62.7517534,1.18307692 75.60081,15.6230769 79.8673195,22.0646154 C80.3017534,22.7215385 80.9838289,23.0692308 81.67581,23.0692308 C82.12581,23.0692308 82.5800553,22.9215385 82.9734516,22.6138462 C83.9710931,21.8338462 84.1960931,20.3215385 83.4800553,19.2369231 C78.7239233,12.06 64.4597723,-4.04 34.8942063,1.43538462 C33.6814704,1.66 32.8692063,2.90923077 33.07581,4.22615385 C33.2809987,5.54461538 34.4343006,6.43230769 35.639961,6.20461538 L35.639961,6.20461538 L35.639961,6.20461538 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.png
new file mode 100644
index 000000000..305dac198
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.png
new file mode 100644
index 000000000..eff983359
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.png
new file mode 100644
index 000000000..310b224aa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-fingerprint@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.png
new file mode 100644
index 000000000..66f4708fc
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg
new file mode 100644
index 000000000..f584037c6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>icon-key</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="icon-key" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M95.053,43.711 L46.435,43.711 C43.412,34.064 34.404,26 23.76,26 C10.638,26 0,36.638 0,49.761 C0,62.884 10.638,73.52 23.76,73.52 C34.805,73.52 44.087,64.918 46.751,54.709 L63.875,54.709 L63.877,64.651 L70.098,64.651 L70.123,54.709 L74.783,54.709 L74.786,64.641 L81.006,64.641 L81.03,54.709 L85.167,54.709 L85.167,64.641 L91.392,64.641 L91.392,54.709 L95.057,54.709 C98.581,54.709 100.001,52.734 100.001,49.209 C99.997,45.689 98.58,43.711 95.053,43.711 L95.053,43.711 Z M23.596,62.794 C16.399,62.794 10.563,56.958 10.563,49.76 C10.563,42.562 16.399,36.727 23.596,36.727 C30.794,36.727 36.629,42.562 36.629,49.76 C36.629,56.958 30.794,62.794 23.596,62.794 L23.596,62.794 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.png
new file mode 100644
index 000000000..ccd7e10a1
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.png
new file mode 100644
index 000000000..551f2ae69
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.png b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.png
new file mode 100644
index 000000000..1a4320ec6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/keys/icon-key@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.png
new file mode 100644
index 000000000..661da48e2
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg
new file mode 100644
index 000000000..61ac8fdd0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M5.21763502,25.9334098 C4.62201801,24.5421709 5.31408066,20.2649627 6.50270803,18.5737297 C7.69394204,16.8824967 14.8139764,11.1118682 18.0827017,9.51888655 C21.3514269,7.92852492 25.1232335,10.7136228 25.1232335,10.7136228 L31.1693326,18.5737297 C21.6564037,21.754453 12.2477403,33.889148 12.2477403,33.889148 C12.2477403,33.889148 5.81325202,27.3259588 5.21763502,25.9334098 Z M50.7969868,98.0040129 C30.5564763,98.0040129 14.1592664,81.3860653 14.1592664,60.910451 C14.1592664,41.4138456 29.0387981,25.4459175 47.9423376,23.9707712 L47.9423376,18.3341735 L41.6333009,18.3341735 L41.6333009,9.02828206 L59.2561767,9.02828206 L59.2561767,18.3341735 L53.6477076,18.3341735 L53.6477076,23.9707712 C72.5460092,25.4445909 87.4333977,41.412519 87.4333977,60.910451 C87.4333977,81.3847387 71.0296405,98.0040129 50.7969868,98.0040129 Z M51.541054,71.6933659 C57.6539179,71.6933659 62.6093732,66.7455263 62.6093732,60.6420567 C62.6093732,54.5385872 57.6539179,49.5907476 51.541054,49.5907476 C45.4281901,49.5907476 40.4727348,54.5385872 40.4727348,60.6420567 C40.4727348,66.7455263 45.4281901,71.6933659 51.541054,71.6933659 Z M96.3766201,25.9341425 C95.7811759,27.3252533 89.3433427,33.889148 89.3433427,33.889148 C89.3433427,33.889148 79.9321974,21.7542607 70.4233315,18.5751403 L76.4676764,10.7157573 C76.4676764,10.7157573 80.2396916,7.92829614 83.5087714,9.5185113 C86.7765483,11.1152759 93.8997286,16.884063 95.0880111,18.5751403 C96.2775965,20.2635977 96.9694584,24.540412 96.3766201,25.9341425 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.png
new file mode 100644
index 000000000..965888294
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.png
new file mode 100644
index 000000000..8c722274a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.png
new file mode 100644
index 000000000..6c6038df0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.png
new file mode 100644
index 000000000..75a064188
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg
new file mode 100644
index 000000000..1d280572f
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.029,-0.384 L10,-0.384 C4.477,-0.384 0,4.094 0,9.616 L0,89.594 C0,95.117 4.477,99.594 10,99.594 L90.029,99.594 C95.556,99.594 100.035,95.117 100.035,89.594 L100.035,9.616 C100.035,4.094 95.557,-0.384 90.029,-0.384 L90.029,-0.384 Z M15.002,29.256 C14.545,28.194 15.076,24.929 15.988,23.638 C16.902,22.347 22.365,17.942 24.873,16.726 C27.381,15.512 30.275,17.638 30.275,17.638 L34.914,23.638 C27.615,26.066 20.396,35.329 20.396,35.329 C20.396,35.329 15.459,30.319 15.002,29.256 L15.002,29.256 Z M49.924,83.141 C34.467,83.141 21.945,70.614 21.945,55.179 C21.945,40.482 33.308,28.445 47.744,27.333 L47.744,23.084 L42.926,23.084 L42.926,16.069 L56.384,16.069 L56.384,23.084 L52.101,23.084 L52.101,27.333 C66.533,28.444 77.902,40.481 77.902,55.179 C77.902,70.613 65.375,83.141 49.924,83.141 L49.924,83.141 Z M85.035,29.258 C84.578,30.32 79.637,35.331 79.637,35.331 C79.637,35.331 72.414,26.067 65.116,23.64 L69.755,17.64 C69.755,17.64 72.65,15.512 75.159,16.726 C77.667,17.945 83.134,22.349 84.046,23.64 C84.959,24.929 85.49,28.194 85.035,29.258 L85.035,29.258 Z M49.459,63.892 C54.1307767,63.892 57.918,60.110597 57.918,55.446 C57.918,50.781403 54.1307767,47 49.459,47 C44.7872233,47 41,50.781403 41,55.446 C41,60.110597 44.7872233,63.892 49.459,63.892 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.png
new file mode 100644
index 000000000..492ac9e80
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.png
new file mode 100644
index 000000000..1ef0ac143
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.png
new file mode 100644
index 000000000..6b82cf9ab
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-expired@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.png
new file mode 100644
index 000000000..d2bfb07a0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg
new file mode 100644
index 000000000..61fd2ace0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M77.3119658,92 L50,64.6787909 L22.6865385,92 L8.00299145,77.3054987 L35.3149573,49.9977557 L8,22.6870202 L22.6850427,8.00149623 L50,35.3137279 L77.3149573,8 L92,22.6825315 L64.6850427,49.9977557 L91.9970085,77.3054987 L77.3119658,92 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.png
new file mode 100644
index 000000000..a46488d2b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.png
new file mode 100644
index 000000000..dbb3f0639
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.png
new file mode 100644
index 000000000..7a6966cbf
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.png
new file mode 100644
index 000000000..9bd4ee24a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg
new file mode 100644
index 000000000..3eb204fdd
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.169,0.282 L10.169,0.282 C4.647,0.282 0.169,4.76 0.169,10.282 L0.169,90.261 C0.169,95.782 4.647,100.261 10.169,100.261 L90.169,100.261 C95.692,100.261 100.169,95.782 100.169,90.261 L100.169,10.282 C100.169,4.76 95.692,0.282 90.169,0.282 L90.169,0.282 Z M80.248,68.521 L70.43,78.342 L52.17,60.082 L33.909,78.342 L24.092,68.521 L42.352,50.27 L24.09,32.017 L33.908,22.202 L52.17,40.456 L70.432,22.201 L80.25,32.014 L61.988,50.27 L80.248,68.521 L80.248,68.521 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.png
new file mode 100644
index 000000000..1033831aa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.png
new file mode 100644
index 000000000..78b62797a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.png
new file mode 100644
index 000000000..7de3afefc
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-invalid@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.png
new file mode 100644
index 000000000..a5ce2e419
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg
new file mode 100644
index 000000000..0421286fe
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M50.1457786,95.2902674 C25.2543974,95.2902674 5,75.0407401 5,50.1451337 C5,25.252107 25.2556872,5 50.1457786,5 C75.03587,5 95.2902674,25.252107 95.2902674,50.1451337 C95.2902674,75.0394503 75.0371599,95.2902674 50.1457786,95.2902674 Z M35.5297191,75.6701923 C39.8404345,78.1467253 44.8296167,79.569442 50.1464236,79.569442 C66.3793238,79.569442 79.5862102,66.3638454 79.5862102,50.1296554 C79.5862102,44.8115586 78.1622037,39.8223764 75.6843808,35.5116611 L35.5297191,75.6701923 Z M50.1464236,20.6911586 C33.9135233,20.6911586 20.7066369,33.8967551 20.7066369,50.1309452 C20.7066369,55.3523024 22.0803389,60.2563538 24.473031,64.512895 L64.5296632,24.4575526 C60.2718321,22.0635707 55.3690706,20.6911586 50.1464236,20.6911586 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.png
new file mode 100644
index 000000000..7da8f0888
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.png
new file mode 100644
index 000000000..73e769750
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.png
new file mode 100644
index 000000000..a07fbf223
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.png
new file mode 100644
index 000000000..66f74079a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg
new file mode 100644
index 000000000..5b6f7a420
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.087,-0.389 L10.087,-0.389 C4.564,-0.389 0.087,4.089 0.087,9.611 L0.087,89.589 C0.087,95.112 4.564,99.589 10.087,99.589 L90.087,99.589 C95.61,99.589 100.087,95.112 100.087,89.589 L100.087,9.611 C100.087,4.089 95.61,-0.389 90.087,-0.389 Z M38.756,69.4 C42.098,71.32 45.966,72.423 50.088,72.423 C62.673,72.423 72.912,62.185 72.912,49.599 C72.912,45.476 71.808,41.608 69.887,38.266 L38.756,69.4 Z M50.088,26.776 C37.503,26.776 27.264,37.014 27.264,49.6 C27.264,53.648 28.329,57.45 30.184,60.75 L61.239,29.696 C57.938,27.84 54.137,26.776 50.088,26.776 Z M50.088,84.6 C30.79,84.6 15.087,68.901 15.087,49.6 C15.087,30.301 30.791,14.6 50.088,14.6 C69.385,14.6 85.088,30.301 85.088,49.6 C85.088,68.9 69.386,84.6 50.088,84.6 L50.088,84.6 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.png
new file mode 100644
index 000000000..b9f891b97
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.png
new file mode 100644
index 000000000..2c333d4f9
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.png
new file mode 100644
index 000000000..4612b19db
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-revoked@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.png
new file mode 100644
index 000000000..1cef14805
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg
new file mode 100644
index 000000000..402bffcaa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M11.4743662,97.2253545 C1.98936285,97.2253571 -1.69987039,86.6466353 1.98936288,81.2764443 C2.36018089,80.2888073 37.5445854,9.4248374 37.6406733,9.21698534 C41.524789,0.483122973 56.8650161,0.0416071437 60.7924391,9.21698534 C60.7572519,9.19524917 98.2991929,81.8687547 97.9337883,81.2642177 C101.323931,86.2404407 96.9260512,97.2253571 88.8978453,97.2253545 C88.8978453,97.2253545 11.4756386,97.2879401 11.4743662,97.2253545 Z M50.5378687,73.3388569 C47.2443918,73.3388569 44.2703808,76.046195 44.2703808,79.5061732 C44.2703808,82.9729198 47.1388056,85.6802579 50.5378687,85.6802579 C53.9369317,85.6802579 56.8040029,82.9729198 56.8040029,79.5061732 C56.8053565,76.046195 53.8313455,73.3388569 50.5378687,73.3388569 Z M50.3063913,28.5 C46.5729719,28.5 42.719076,30.2990258 43.0805057,32.9143334 L45.8826007,65.934287 L54.7315355,65.934287 L57.5322768,32.9143334 C57.8937065,30.2990258 54.0398106,28.5 50.3063913,28.5 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.png
new file mode 100644
index 000000000..fcf3cb4a6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.png
new file mode 100644
index 000000000..33a093b71
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.png
new file mode 100644
index 000000000..011c503f0
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.png
new file mode 100644
index 000000000..0b04995b8
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg
new file mode 100644
index 000000000..f0494aa33
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.478,0 0,4.478 0,10 L0,89.977 C0,95.5 4.478,99.977 10,99.977 L90,99.977 C95.523,99.977 100,95.5 100,89.977 L100,10 C100,4.477 95.523,0 90,0 L90,0 Z M81.015,83.034 C80.993,83.04 17.896,83.083 17.895,83.034 C14.215,83.038 11.855,75.247 14.581,71.294 C14.855,70.567 40.853,18.404 40.924,18.251 C43.794,11.822 55.129,11.497 58.031,18.251 C58.005,18.235 85.745,71.73 85.475,71.285 C87.98,74.948 85.14,82.991 81.015,83.034 Z M50.046,65.331 C47.613,65.331 45.416,67.331 45.416,69.887 C45.416,72.448 47.535,74.448 50.046,74.448 C52.557,74.448 54.675,72.448 54.675,69.887 C54.676,67.331 52.479,65.331 50.046,65.331 Z M49.875,29.502 C47.117,29.502 44.27,30.961 44.537,33.082 L46.607,59.861 L53.144,59.861 L55.213,33.082 C55.48,30.961 52.633,29.502 49.875,29.502 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.png
new file mode 100644
index 000000000..bea9ecaf2
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.png
new file mode 100644
index 000000000..2baa3b700
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.png
new file mode 100644
index 000000000..54dac1caa
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unknown@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.png
new file mode 100644
index 000000000..51c2ed7eb
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg
new file mode 100644
index 000000000..ffa98580a
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M49.8900274,96.5521596 C75.8474106,96.5521596 96.8900274,75.5095428 96.8900274,49.5521596 C96.8900274,23.5947764 75.8474106,2.5521596 49.8900274,2.5521596 C23.9326441,2.5521596 2.89002736,23.5947764 2.89002736,49.5521596 C2.89002736,75.5095428 23.9326441,96.5521596 49.8900274,96.5521596 Z M42.9188472,79.4349375 L42.9188472,67.0146143 L55.3391704,67.0146143 L55.3391704,79.4349375 L42.9188472,79.4349375 Z M68.652586,41.8646591 C67.9712562,43.583078 67.1302842,45.0524085 66.1249189,46.2716815 C65.1167028,47.4919237 64.0039592,48.5318919 62.7800362,49.3906167 C61.5570634,50.25128 60.4006082,51.1080664 59.3135213,51.9677605 C58.2254842,52.829393 57.2609796,53.8121774 56.4181072,54.9209598 C55.5733342,56.0307115 55.0449948,57.4147511 54.8273874,59.0779247 L54.8273874,62.237567 L43.8168316,62.237567 L43.8168316,58.4954263 C43.9802747,56.114064 44.4278428,54.1165111 45.1623867,52.5095519 C45.89503,50.9025928 46.7531065,49.5292145 47.7309145,48.3923248 C48.7106231,47.2564044 49.7425954,46.2716815 50.8296823,45.4400947 C51.9177195,44.6104463 52.9230848,43.7769211 53.8476788,42.9463035 C54.7722728,42.1147166 55.5201202,41.2007465 56.0912209,40.2024546 C56.6623216,39.2041628 56.9188893,37.9577517 56.8647251,36.4612832 C56.8647251,33.9112774 56.2537138,32.0261534 55.030741,30.804942 C53.8058678,29.5876075 52.1058691,28.9760325 49.9326456,28.9760325 C48.4645081,28.9760325 47.2006746,29.2677664 46.1392445,29.8492956 C45.0797149,30.4317941 44.2092851,31.2071664 43.5298558,32.177351 C42.848526,33.1475357 42.3477439,34.2844253 42.0208576,35.5860816 C41.6939713,36.8906456 41.5305282,38.2901926 41.5305282,39.7866612 L29.5431146,39.7866612 C29.5953784,36.7917856 30.0999615,34.0479368 31.0521128,31.5541455 C32.0023636,29.0593851 33.3346152,26.8970655 35.0479174,25.0691252 C36.7612195,23.2382773 38.8270647,21.8115922 41.2464032,20.7832547 C43.6666919,19.7568556 46.3730062,19.2441406 49.3624951,19.2441406 C53.2224138,19.2441406 56.4447142,19.785932 59.0274958,20.8666072 C61.6093272,21.9482516 63.6894262,23.2935226 65.2668425,24.9024202 C66.8442588,26.5103485 67.9712562,28.2423365 68.6516357,30.0993532 C69.3310651,31.9573391 69.6703046,33.6883579 69.6703046,35.2962862 C69.6722051,37.9567825 69.3320153,40.1481786 68.652586,41.8646591 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.png
new file mode 100644
index 000000000..10d328081
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.png
new file mode 100644
index 000000000..b8fe6a994
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.png
new file mode 100644
index 000000000..93e562e3b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.png
new file mode 100644
index 000000000..00989a976
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg
new file mode 100644
index 000000000..6ce6d14dd
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.477,0 0,4.478 0,10 L0,89.978 C0,95.501 4.477,99.978 10,99.978 L90,99.978 C95.523,99.978 100,95.501 100,89.978 L100,10 C100,4.478 95.523,0 90,0 L90,0 Z M56.359,80.095 L42.883,80.095 L42.883,66.877 L56.359,66.877 L56.359,80.095 L56.359,80.095 Z M70.222,40.709 C69.505,42.482 68.62,43.998 67.562,45.256 C66.501,46.515 65.33,47.588 64.042,48.474 C62.755,49.362 61.538,50.246 60.394,51.133 C59.249,52.022 58.234,53.036 57.347,54.18 C56.458,55.325 55.902,56.753 55.673,58.469 L55.673,61.729 L44.086,61.729 L44.086,57.868 C44.258,55.411 44.729,53.35 45.502,51.692 C46.273,50.034 47.176,48.617 48.205,47.444 C49.236,46.272 50.322,45.256 51.466,44.398 C52.611,43.542 53.669,42.682 54.642,41.825 C55.615,40.967 56.402,40.024 57.003,38.994 C57.604,37.964 57.874,36.678 57.817,35.134 C57.817,32.503 57.174,30.558 55.887,29.298 C54.598,28.042 52.809,27.411 50.522,27.411 C48.977,27.411 47.647,27.712 46.53,28.312 C45.415,28.913 44.499,29.713 43.784,30.714 C43.067,31.715 42.54,32.888 42.196,34.231 C41.852,35.577 41.68,37.021 41.68,38.565 L29.065,38.565 C29.12,35.475 29.651,32.644 30.653,30.071 C31.653,27.497 33.055,25.266 34.858,23.38 C36.661,21.491 38.835,20.019 41.381,18.958 C43.928,17.899 46.776,17.37 49.922,17.37 C53.984,17.37 57.375,17.929 60.093,19.044 C62.81,20.16 64.999,21.548 66.659,23.208 C68.319,24.867 69.505,26.654 70.221,28.57 C70.936,30.487 71.293,32.273 71.293,33.932 C71.295,36.677 70.937,38.938 70.222,40.709 L70.222,40.709 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.png
new file mode 100644
index 000000000..2ee36bb15
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.png
new file mode 100644
index 000000000..b1f30b334
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.png
new file mode 100644
index 000000000..56193245c
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-unverified@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.png
new file mode 100644
index 000000000..9a8c5efcd
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg
new file mode 100644
index 000000000..04356a977
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M50,97 C75.9573832,97 97,75.9573832 97,50 C97,24.0426168 75.9573832,3 50,3 C24.0426168,3 3,24.0426168 3,50 C3,75.9573832 24.0426168,97 50,97 Z M46.2732912,77.5085 L20,57.830916 L27.9184401,47.6349702 L43.3096859,59.5152262 L70.31112,23 L80.867825,30.7782191 L46.2732912,77.5085 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.png
new file mode 100644
index 000000000..1adfc7fb6
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.png
new file mode 100644
index 000000000..227504e6b
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.png
new file mode 100644
index 000000000..cf8ad067d
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified-cutout@512x.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.png
new file mode 100644
index 000000000..e19125e3c
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg
new file mode 100644
index 000000000..197273c79
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.003,-0.017 L10.001,-0.017 C4.478,-0.017 0.001,4.46 0.001,9.983 L0.001,89.96 C0.001,95.483 4.478,99.96 10.001,99.96 L90.003,99.96 C95.526,99.96 100.003,95.483 100.003,89.96 L100.003,9.983 C100.003,4.46 95.526,-0.017 90.003,-0.017 L90.003,-0.017 Z M46.194,79.729 L17.364,58.104 L26.053,46.899 L42.942,59.955 L72.571,19.826 L84.155,28.374 L46.194,79.729 L46.194,79.729 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.png
new file mode 100644
index 000000000..9c3063010
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@200.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.png
new file mode 100644
index 000000000..5de04efe1
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@300.png
Binary files differ
diff --git a/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.png b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.png
new file mode 100644
index 000000000..d52221f6c
--- /dev/null
+++ b/Resources/graphics/originals/modernpgp-icons/signatures/signature-verified@512x.png
Binary files differ
diff --git a/Resources/graphics/status_lock_closed.svg b/Resources/graphics/status_lock_closed.svg
new file mode 100644
index 000000000..286e89297
--- /dev/null
+++ b/Resources/graphics/status_lock_closed.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-closed</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-closed" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M81.502,45.132 L79.577,45.132 L79.577,29.479 C79.479,10.285 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.182,46.109 81.727,45.132 81.502,45.132 L81.502,45.132 Z M59.334,86.055 L41.061,86.055 L46.024,71.489 C43.904,70.077 42.496,67.623 42.496,64.824 C42.496,60.44 45.938,56.886 50.183,56.886 C54.428,56.886 57.87,60.443 57.87,64.824 C57.87,67.619 56.466,70.077 54.348,71.485 L59.334,86.055 L59.334,86.055 Z M34.261,45.132 L34.277,29.686 C34.277,19.737 40.348,11.783 50.183,11.783 C59.924,11.783 66.088,18.741 66.088,29.686 L66.098,45.132 L34.261,45.132 L34.261,45.132 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_lock_error.svg b/Resources/graphics/status_lock_error.svg
new file mode 100644
index 000000000..d3c4e1d1d
--- /dev/null
+++ b/Resources/graphics/status_lock_error.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-error</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-error" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M80.459,45.474 L78.533,45.474 L78.533,29.826 C78.435,10.633 65.344,0.183 49.433,0.183 C33.527,0.183 19.265,11.128 19.761,29.826 L19.745,45.454 C19.745,45.454 19.985,45.475 18.784,45.475 C17.514,45.475 9.145,46.946 9.145,54.407 L9.145,90.228 C9.145,98.187 18.565,100.171 18.784,100.171 L79.984,100.171 C80.203,100.171 89.138,99.178 89.138,90.228 L89.138,54.901 C89.139,46.452 80.684,45.474 80.459,45.474 L80.459,45.474 Z M33.234,30.033 C33.234,20.084 39.304,12.131 49.14,12.131 C58.881,12.131 65.045,19.088 65.045,30.033 L65.055,45.474 L33.218,45.474 L33.234,30.033 L33.234,30.033 Z M59.4033767,90.873 L48.4582822,79.9279055 L38.2296593,90.3644491 L31.6365,83.7568884 L42.5824946,72.8153942 L32.3439707,62.5939721 L38.7544118,56.1079235 L49.7013065,67.0503177 L60.1234487,56.7100837 L66.6365,63.2240351 L55.6896053,74.1673294 L66.0091373,84.4778605 L59.4033767,90.873 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_lock_open.svg b/Resources/graphics/status_lock_open.svg
new file mode 100644
index 000000000..9beb127af
--- /dev/null
+++ b/Resources/graphics/status_lock_open.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>lock-open</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="lock-open" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M79.577,26.479 C79.577,10.833 66.387,-0.164 50.476,-0.164 C34.57,-0.164 20.304,10.782 20.801,29.479 L20.785,45.112 C20.785,45.112 21.025,45.133 19.825,45.133 C18.555,45.133 10.185,46.606 10.185,54.069 L10.185,89.893 C10.185,97.852 19.605,99.836 19.825,99.836 L81.027,99.836 C81.247,99.836 90.181,98.843 90.181,89.893 L90.181,54.564 C90.181,46.107 81.726,45.13 81.5,45.13 L34.259,45.13 L34.275,29.684 C34.275,19.735 40.346,11.781 50.181,11.781 C59.922,11.781 66.664,18.164 66.664,29.164 L79.577,26.479 Z M59.334,86.055 L41.061,86.055 L46.024,71.49 C43.904,70.078 42.496,67.624 42.496,64.825 C42.496,60.44 45.938,56.887 50.183,56.887 C54.428,56.887 57.87,60.445 57.87,64.825 C57.87,67.62 56.466,70.078 54.348,71.485 L59.334,86.055 L59.334,86.055 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_expired.svg b/Resources/graphics/status_signature_expired.svg
new file mode 100644
index 000000000..1d280572f
--- /dev/null
+++ b/Resources/graphics/status_signature_expired.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.029,-0.384 L10,-0.384 C4.477,-0.384 0,4.094 0,9.616 L0,89.594 C0,95.117 4.477,99.594 10,99.594 L90.029,99.594 C95.556,99.594 100.035,95.117 100.035,89.594 L100.035,9.616 C100.035,4.094 95.557,-0.384 90.029,-0.384 L90.029,-0.384 Z M15.002,29.256 C14.545,28.194 15.076,24.929 15.988,23.638 C16.902,22.347 22.365,17.942 24.873,16.726 C27.381,15.512 30.275,17.638 30.275,17.638 L34.914,23.638 C27.615,26.066 20.396,35.329 20.396,35.329 C20.396,35.329 15.459,30.319 15.002,29.256 L15.002,29.256 Z M49.924,83.141 C34.467,83.141 21.945,70.614 21.945,55.179 C21.945,40.482 33.308,28.445 47.744,27.333 L47.744,23.084 L42.926,23.084 L42.926,16.069 L56.384,16.069 L56.384,23.084 L52.101,23.084 L52.101,27.333 C66.533,28.444 77.902,40.481 77.902,55.179 C77.902,70.613 65.375,83.141 49.924,83.141 L49.924,83.141 Z M85.035,29.258 C84.578,30.32 79.637,35.331 79.637,35.331 C79.637,35.331 72.414,26.067 65.116,23.64 L69.755,17.64 C69.755,17.64 72.65,15.512 75.159,16.726 C77.667,17.945 83.134,22.349 84.046,23.64 C84.959,24.929 85.49,28.194 85.035,29.258 L85.035,29.258 Z M49.459,63.892 C54.1307767,63.892 57.918,60.110597 57.918,55.446 C57.918,50.781403 54.1307767,47 49.459,47 C44.7872233,47 41,50.781403 41,55.446 C41,60.110597 44.7872233,63.892 49.459,63.892 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_expired_cutout.svg b/Resources/graphics/status_signature_expired_cutout.svg
new file mode 100644
index 000000000..61ac8fdd0
--- /dev/null
+++ b/Resources/graphics/status_signature_expired_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-expired-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-expired-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M5.21763502,25.9334098 C4.62201801,24.5421709 5.31408066,20.2649627 6.50270803,18.5737297 C7.69394204,16.8824967 14.8139764,11.1118682 18.0827017,9.51888655 C21.3514269,7.92852492 25.1232335,10.7136228 25.1232335,10.7136228 L31.1693326,18.5737297 C21.6564037,21.754453 12.2477403,33.889148 12.2477403,33.889148 C12.2477403,33.889148 5.81325202,27.3259588 5.21763502,25.9334098 Z M50.7969868,98.0040129 C30.5564763,98.0040129 14.1592664,81.3860653 14.1592664,60.910451 C14.1592664,41.4138456 29.0387981,25.4459175 47.9423376,23.9707712 L47.9423376,18.3341735 L41.6333009,18.3341735 L41.6333009,9.02828206 L59.2561767,9.02828206 L59.2561767,18.3341735 L53.6477076,18.3341735 L53.6477076,23.9707712 C72.5460092,25.4445909 87.4333977,41.412519 87.4333977,60.910451 C87.4333977,81.3847387 71.0296405,98.0040129 50.7969868,98.0040129 Z M51.541054,71.6933659 C57.6539179,71.6933659 62.6093732,66.7455263 62.6093732,60.6420567 C62.6093732,54.5385872 57.6539179,49.5907476 51.541054,49.5907476 C45.4281901,49.5907476 40.4727348,54.5385872 40.4727348,60.6420567 C40.4727348,66.7455263 45.4281901,71.6933659 51.541054,71.6933659 Z M96.3766201,25.9341425 C95.7811759,27.3252533 89.3433427,33.889148 89.3433427,33.889148 C89.3433427,33.889148 79.9321974,21.7542607 70.4233315,18.5751403 L76.4676764,10.7157573 C76.4676764,10.7157573 80.2396916,7.92829614 83.5087714,9.5185113 C86.7765483,11.1152759 93.8997286,16.884063 95.0880111,18.5751403 C96.2775965,20.2635977 96.9694584,24.540412 96.3766201,25.9341425 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_invalid.svg b/Resources/graphics/status_signature_invalid.svg
new file mode 100644
index 000000000..3eb204fdd
--- /dev/null
+++ b/Resources/graphics/status_signature_invalid.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.169,0.282 L10.169,0.282 C4.647,0.282 0.169,4.76 0.169,10.282 L0.169,90.261 C0.169,95.782 4.647,100.261 10.169,100.261 L90.169,100.261 C95.692,100.261 100.169,95.782 100.169,90.261 L100.169,10.282 C100.169,4.76 95.692,0.282 90.169,0.282 L90.169,0.282 Z M80.248,68.521 L70.43,78.342 L52.17,60.082 L33.909,78.342 L24.092,68.521 L42.352,50.27 L24.09,32.017 L33.908,22.202 L52.17,40.456 L70.432,22.201 L80.25,32.014 L61.988,50.27 L80.248,68.521 L80.248,68.521 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_invalid_cutout.svg b/Resources/graphics/status_signature_invalid_cutout.svg
new file mode 100644
index 000000000..61fd2ace0
--- /dev/null
+++ b/Resources/graphics/status_signature_invalid_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-invalid-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-invalid-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M77.3119658,92 L50,64.6787909 L22.6865385,92 L8.00299145,77.3054987 L35.3149573,49.9977557 L8,22.6870202 L22.6850427,8.00149623 L50,35.3137279 L77.3149573,8 L92,22.6825315 L64.6850427,49.9977557 L91.9970085,77.3054987 L77.3119658,92 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_revoked.svg b/Resources/graphics/status_signature_revoked.svg
new file mode 100644
index 000000000..5b6f7a420
--- /dev/null
+++ b/Resources/graphics/status_signature_revoked.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.087,-0.389 L10.087,-0.389 C4.564,-0.389 0.087,4.089 0.087,9.611 L0.087,89.589 C0.087,95.112 4.564,99.589 10.087,99.589 L90.087,99.589 C95.61,99.589 100.087,95.112 100.087,89.589 L100.087,9.611 C100.087,4.089 95.61,-0.389 90.087,-0.389 Z M38.756,69.4 C42.098,71.32 45.966,72.423 50.088,72.423 C62.673,72.423 72.912,62.185 72.912,49.599 C72.912,45.476 71.808,41.608 69.887,38.266 L38.756,69.4 Z M50.088,26.776 C37.503,26.776 27.264,37.014 27.264,49.6 C27.264,53.648 28.329,57.45 30.184,60.75 L61.239,29.696 C57.938,27.84 54.137,26.776 50.088,26.776 Z M50.088,84.6 C30.79,84.6 15.087,68.901 15.087,49.6 C15.087,30.301 30.791,14.6 50.088,14.6 C69.385,14.6 85.088,30.301 85.088,49.6 C85.088,68.9 69.386,84.6 50.088,84.6 L50.088,84.6 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_revoked_cutout.svg b/Resources/graphics/status_signature_revoked_cutout.svg
new file mode 100644
index 000000000..0421286fe
--- /dev/null
+++ b/Resources/graphics/status_signature_revoked_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-revoked-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-revoked-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M50.1457786,95.2902674 C25.2543974,95.2902674 5,75.0407401 5,50.1451337 C5,25.252107 25.2556872,5 50.1457786,5 C75.03587,5 95.2902674,25.252107 95.2902674,50.1451337 C95.2902674,75.0394503 75.0371599,95.2902674 50.1457786,95.2902674 Z M35.5297191,75.6701923 C39.8404345,78.1467253 44.8296167,79.569442 50.1464236,79.569442 C66.3793238,79.569442 79.5862102,66.3638454 79.5862102,50.1296554 C79.5862102,44.8115586 78.1622037,39.8223764 75.6843808,35.5116611 L35.5297191,75.6701923 Z M50.1464236,20.6911586 C33.9135233,20.6911586 20.7066369,33.8967551 20.7066369,50.1309452 C20.7066369,55.3523024 22.0803389,60.2563538 24.473031,64.512895 L64.5296632,24.4575526 C60.2718321,22.0635707 55.3690706,20.6911586 50.1464236,20.6911586 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unknown.svg b/Resources/graphics/status_signature_unknown.svg
new file mode 100644
index 000000000..f0494aa33
--- /dev/null
+++ b/Resources/graphics/status_signature_unknown.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.478,0 0,4.478 0,10 L0,89.977 C0,95.5 4.478,99.977 10,99.977 L90,99.977 C95.523,99.977 100,95.5 100,89.977 L100,10 C100,4.477 95.523,0 90,0 L90,0 Z M81.015,83.034 C80.993,83.04 17.896,83.083 17.895,83.034 C14.215,83.038 11.855,75.247 14.581,71.294 C14.855,70.567 40.853,18.404 40.924,18.251 C43.794,11.822 55.129,11.497 58.031,18.251 C58.005,18.235 85.745,71.73 85.475,71.285 C87.98,74.948 85.14,82.991 81.015,83.034 Z M50.046,65.331 C47.613,65.331 45.416,67.331 45.416,69.887 C45.416,72.448 47.535,74.448 50.046,74.448 C52.557,74.448 54.675,72.448 54.675,69.887 C54.676,67.331 52.479,65.331 50.046,65.331 Z M49.875,29.502 C47.117,29.502 44.27,30.961 44.537,33.082 L46.607,59.861 L53.144,59.861 L55.213,33.082 C55.48,30.961 52.633,29.502 49.875,29.502 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unknown_cutout.svg b/Resources/graphics/status_signature_unknown_cutout.svg
new file mode 100644
index 000000000..402bffcaa
--- /dev/null
+++ b/Resources/graphics/status_signature_unknown_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unknown-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unknown-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M11.4743662,97.2253545 C1.98936285,97.2253571 -1.69987039,86.6466353 1.98936288,81.2764443 C2.36018089,80.2888073 37.5445854,9.4248374 37.6406733,9.21698534 C41.524789,0.483122973 56.8650161,0.0416071437 60.7924391,9.21698534 C60.7572519,9.19524917 98.2991929,81.8687547 97.9337883,81.2642177 C101.323931,86.2404407 96.9260512,97.2253571 88.8978453,97.2253545 C88.8978453,97.2253545 11.4756386,97.2879401 11.4743662,97.2253545 Z M50.5378687,73.3388569 C47.2443918,73.3388569 44.2703808,76.046195 44.2703808,79.5061732 C44.2703808,82.9729198 47.1388056,85.6802579 50.5378687,85.6802579 C53.9369317,85.6802579 56.8040029,82.9729198 56.8040029,79.5061732 C56.8053565,76.046195 53.8313455,73.3388569 50.5378687,73.3388569 Z M50.3063913,28.5 C46.5729719,28.5 42.719076,30.2990258 43.0805057,32.9143334 L45.8826007,65.934287 L54.7315355,65.934287 L57.5322768,32.9143334 C57.8937065,30.2990258 54.0398106,28.5 50.3063913,28.5 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unverified.svg b/Resources/graphics/status_signature_unverified.svg
new file mode 100644
index 000000000..6ce6d14dd
--- /dev/null
+++ b/Resources/graphics/status_signature_unverified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90,0 L10,0 C4.477,0 0,4.478 0,10 L0,89.978 C0,95.501 4.477,99.978 10,99.978 L90,99.978 C95.523,99.978 100,95.501 100,89.978 L100,10 C100,4.478 95.523,0 90,0 L90,0 Z M56.359,80.095 L42.883,80.095 L42.883,66.877 L56.359,66.877 L56.359,80.095 L56.359,80.095 Z M70.222,40.709 C69.505,42.482 68.62,43.998 67.562,45.256 C66.501,46.515 65.33,47.588 64.042,48.474 C62.755,49.362 61.538,50.246 60.394,51.133 C59.249,52.022 58.234,53.036 57.347,54.18 C56.458,55.325 55.902,56.753 55.673,58.469 L55.673,61.729 L44.086,61.729 L44.086,57.868 C44.258,55.411 44.729,53.35 45.502,51.692 C46.273,50.034 47.176,48.617 48.205,47.444 C49.236,46.272 50.322,45.256 51.466,44.398 C52.611,43.542 53.669,42.682 54.642,41.825 C55.615,40.967 56.402,40.024 57.003,38.994 C57.604,37.964 57.874,36.678 57.817,35.134 C57.817,32.503 57.174,30.558 55.887,29.298 C54.598,28.042 52.809,27.411 50.522,27.411 C48.977,27.411 47.647,27.712 46.53,28.312 C45.415,28.913 44.499,29.713 43.784,30.714 C43.067,31.715 42.54,32.888 42.196,34.231 C41.852,35.577 41.68,37.021 41.68,38.565 L29.065,38.565 C29.12,35.475 29.651,32.644 30.653,30.071 C31.653,27.497 33.055,25.266 34.858,23.38 C36.661,21.491 38.835,20.019 41.381,18.958 C43.928,17.899 46.776,17.37 49.922,17.37 C53.984,17.37 57.375,17.929 60.093,19.044 C62.81,20.16 64.999,21.548 66.659,23.208 C68.319,24.867 69.505,26.654 70.221,28.57 C70.936,30.487 71.293,32.273 71.293,33.932 C71.295,36.677 70.937,38.938 70.222,40.709 L70.222,40.709 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_unverified_cutout.svg b/Resources/graphics/status_signature_unverified_cutout.svg
new file mode 100644
index 000000000..ffa98580a
--- /dev/null
+++ b/Resources/graphics/status_signature_unverified_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="101px" height="100px" viewBox="0 0 101 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-unverified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-unverified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.915625, 0.000000)" fill="#000000">
+ <path d="M49.8900274,96.5521596 C75.8474106,96.5521596 96.8900274,75.5095428 96.8900274,49.5521596 C96.8900274,23.5947764 75.8474106,2.5521596 49.8900274,2.5521596 C23.9326441,2.5521596 2.89002736,23.5947764 2.89002736,49.5521596 C2.89002736,75.5095428 23.9326441,96.5521596 49.8900274,96.5521596 Z M42.9188472,79.4349375 L42.9188472,67.0146143 L55.3391704,67.0146143 L55.3391704,79.4349375 L42.9188472,79.4349375 Z M68.652586,41.8646591 C67.9712562,43.583078 67.1302842,45.0524085 66.1249189,46.2716815 C65.1167028,47.4919237 64.0039592,48.5318919 62.7800362,49.3906167 C61.5570634,50.25128 60.4006082,51.1080664 59.3135213,51.9677605 C58.2254842,52.829393 57.2609796,53.8121774 56.4181072,54.9209598 C55.5733342,56.0307115 55.0449948,57.4147511 54.8273874,59.0779247 L54.8273874,62.237567 L43.8168316,62.237567 L43.8168316,58.4954263 C43.9802747,56.114064 44.4278428,54.1165111 45.1623867,52.5095519 C45.89503,50.9025928 46.7531065,49.5292145 47.7309145,48.3923248 C48.7106231,47.2564044 49.7425954,46.2716815 50.8296823,45.4400947 C51.9177195,44.6104463 52.9230848,43.7769211 53.8476788,42.9463035 C54.7722728,42.1147166 55.5201202,41.2007465 56.0912209,40.2024546 C56.6623216,39.2041628 56.9188893,37.9577517 56.8647251,36.4612832 C56.8647251,33.9112774 56.2537138,32.0261534 55.030741,30.804942 C53.8058678,29.5876075 52.1058691,28.9760325 49.9326456,28.9760325 C48.4645081,28.9760325 47.2006746,29.2677664 46.1392445,29.8492956 C45.0797149,30.4317941 44.2092851,31.2071664 43.5298558,32.177351 C42.848526,33.1475357 42.3477439,34.2844253 42.0208576,35.5860816 C41.6939713,36.8906456 41.5305282,38.2901926 41.5305282,39.7866612 L29.5431146,39.7866612 C29.5953784,36.7917856 30.0999615,34.0479368 31.0521128,31.5541455 C32.0023636,29.0593851 33.3346152,26.8970655 35.0479174,25.0691252 C36.7612195,23.2382773 38.8270647,21.8115922 41.2464032,20.7832547 C43.6666919,19.7568556 46.3730062,19.2441406 49.3624951,19.2441406 C53.2224138,19.2441406 56.4447142,19.785932 59.0274958,20.8666072 C61.6093272,21.9482516 63.6894262,23.2935226 65.2668425,24.9024202 C66.8442588,26.5103485 67.9712562,28.2423365 68.6516357,30.0993532 C69.3310651,31.9573391 69.6703046,33.6883579 69.6703046,35.2962862 C69.6722051,37.9567825 69.3320153,40.1481786 68.652586,41.8646591 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_verified.svg b/Resources/graphics/status_signature_verified.svg
new file mode 100644
index 000000000..197273c79
--- /dev/null
+++ b/Resources/graphics/status_signature_verified.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified" sketch:type="MSArtboardGroup" fill="#000000">
+ <path d="M90.003,-0.017 L10.001,-0.017 C4.478,-0.017 0.001,4.46 0.001,9.983 L0.001,89.96 C0.001,95.483 4.478,99.96 10.001,99.96 L90.003,99.96 C95.526,99.96 100.003,95.483 100.003,89.96 L100.003,9.983 C100.003,4.46 95.526,-0.017 90.003,-0.017 L90.003,-0.017 Z M46.194,79.729 L17.364,58.104 L26.053,46.899 L42.942,59.955 L72.571,19.826 L84.155,28.374 L46.194,79.729 L46.194,79.729 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/status_signature_verified_cutout.svg b/Resources/graphics/status_signature_verified_cutout.svg
new file mode 100644
index 000000000..04356a977
--- /dev/null
+++ b/Resources/graphics/status_signature_verified_cutout.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="100px" height="100px" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+ <!-- Generator: Sketch 3.0.4 (8053) - http://www.bohemiancoding.com/sketch -->
+ <title>signature-verified-cutout</title>
+ <desc>Created with Sketch.</desc>
+ <defs></defs>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+ <g id="signature-verified-cutout" sketch:type="MSArtboardGroup" transform="translate(0.110156, 0.000000)" fill="#000000">
+ <path d="M50,97 C75.9573832,97 97,75.9573832 97,50 C97,24.0426168 75.9573832,3 50,3 C24.0426168,3 3,24.0426168 3,50 C3,75.9573832 24.0426168,97 50,97 Z M46.2732912,77.5085 L20,57.830916 L27.9184401,47.6349702 L43.3096859,59.5152262 L70.31112,23 L80.867825,30.7782191 L46.2732912,77.5085 Z" sketch:type="MSShapeGroup"></path>
+ </g>
+ </g>
+</svg> \ No newline at end of file
diff --git a/Resources/graphics/update-drawables.sh b/Resources/graphics/update-drawables.sh
index c1dfc77e6..ae77d313a 100755
--- a/Resources/graphics/update-drawables.sh
+++ b/Resources/graphics/update-drawables.sh
@@ -39,8 +39,27 @@ inkscape -w 512 -h 512 -e "$PLAY_DIR/$NAME.png" $NAME.svg
for NAME in "ic_action_nfc" "ic_action_qr_code"
do
+echo $NAME
inkscape -w 32 -h 32 -e "$MDPI_DIR/$NAME.png" $NAME.svg
inkscape -w 48 -h 48 -e "$HDPI_DIR/$NAME.png" $NAME.svg
inkscape -w 64 -h 64 -e "$XDPI_DIR/$NAME.png" $NAME.svg
inkscape -w 96 -h 96 -e "$XXDPI_DIR/$NAME.png" $NAME.svg
+done
+
+for NAME in status*.svg
+do
+echo $NAME
+inkscape -w 24 -h 24 -e "$MDPI_DIR/${NAME%%.*}.png" $NAME
+inkscape -w 32 -h 32 -e "$HDPI_DIR/${NAME%%.*}.png" $NAME
+inkscape -w 48 -h 48 -e "$XDPI_DIR/${NAME%%.*}.png" $NAME
+inkscape -w 64 -h 64 -e "$XXDPI_DIR/${NAME%%.*}.png" $NAME
+done
+
+for NAME in "create_key_robot"
+do
+echo $NAME
+inkscape -w 48 -h 48 -e "$MDPI_DIR/$NAME.png" $NAME.svg
+inkscape -w 64 -h 64 -e "$HDPI_DIR/$NAME.png" $NAME.svg
+inkscape -w 96 -h 96 -e "$XDPI_DIR/$NAME.png" $NAME.svg
+inkscape -w 128 -h 128 -e "$XXDPI_DIR/$NAME.png" $NAME.svg
done \ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 05e90eb17..2e7ab3d86 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
dependencies {
// NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information
- classpath 'com.android.tools.build:gradle:0.12.0'
+ classpath 'com.android.tools.build:gradle:0.12.2'
}
}
@@ -24,3 +24,12 @@ subprojects {
maxParallelForks = 1
}
}
+
+// Ignore tests for external dependency
+project(':extern:spongycastle') {
+ subprojects {
+ // Need to re-apply the plugin here otherwise the test property below can't be set.
+ apply plugin: 'java'
+ test.enabled = false
+ }
+}
diff --git a/extern/AppMsg b/extern/AppMsg
deleted file mode 160000
-Subproject 61e74741909a712db2e0d31ddffa5b7cf37c21f
diff --git a/settings.gradle b/settings.gradle
index e134c52a8..d568e09ce 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -9,7 +9,6 @@ include ':extern:spongycastle:core'
include ':extern:spongycastle:pg'
include ':extern:spongycastle:pkix'
include ':extern:spongycastle:prov'
-include ':extern:AppMsg:library'
include ':extern:SuperToasts:supertoasts'
include ':extern:minidns'
include ':extern:KeybaseLib:Lib'