aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/main
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-04-05 19:30:52 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-04-05 19:30:52 +0200
commitaa6f5118f5b88ed40e1318b59d47465bae6067df (patch)
treec00db3802cd6258073b16d197b1bd2e8d2d7e975 /OpenPGP-Keychain/src/main
parent5e4239a7b98a050b4312eee075f2fdac7f2b8af2 (diff)
parentdb25433890cfc5bbf0200eb488076df23cb44866 (diff)
downloadopen-keychain-aa6f5118f5b88ed40e1318b59d47465bae6067df.tar.gz
open-keychain-aa6f5118f5b88ed40e1318b59d47465bae6067df.tar.bz2
open-keychain-aa6f5118f5b88ed40e1318b59d47465bae6067df.zip
Merge remote-tracking branch 'origin/master' into certs
A lot of things are completely broken, but it compiles and doesn't crash right away. Good enough for me. Conflicts: OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml
Diffstat (limited to 'OpenPGP-Keychain/src/main')
-rw-r--r--OpenPGP-Keychain/src/main/AndroidManifest.xml65
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java25
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java15
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java8
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java1
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java40
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java85
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java26
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java290
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java38
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java85
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java239
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java768
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java54
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java45
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java35
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java254
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java288
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java1163
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java796
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java)31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java50
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java)142
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java)91
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java)2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java)45
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java201
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java198
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java135
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java108
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java)4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java)83
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java (renamed from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java)110
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java537
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java31
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java107
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java237
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java75
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java96
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java684
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java261
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java180
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java189
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java149
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java485
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java984
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java30
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java269
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java380
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java259
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java98
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java179
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java9
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java5
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java18
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java138
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java76
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java42
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java90
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java60
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java174
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java35
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java95
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java29
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java272
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java54
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java158
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java28
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java70
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java12
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java64
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java17
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java40
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java174
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java59
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java19
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java203
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java201
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java7
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java175
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java137
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java184
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java3
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java4
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java22
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java6
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java2
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java50
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java6
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_person.pngbin0 -> 573 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-hdpi/icon.pngbin5101 -> 5093 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.pngbin0 -> 2509 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-ldpi/icon.pngbin1969 -> 1967 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_person.pngbin0 -> 468 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-mdpi/icon.pngbin2906 -> 2896 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-xhdpi/ic_action_person.pngbin0 -> 781 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-xhdpi/icon.pngbin7895 -> 7870 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-xxhdpi/ic_action_person.pngbin0 -> 1004 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-xxhdpi/icon.pngbin14188 -> 14153 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable-xxxhdpi/icon.pngbin20919 -> 20825 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/drawable/revoked_key_small.pngbin0 -> 1793 bytes
-rw-r--r--OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml20
-rw-r--r--OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml19
-rw-r--r--OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml19
-rw-r--r--OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml21
-rw-r--r--OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml18
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml20
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml102
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml27
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml16
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml68
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml11
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml14
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml29
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_remote_error_message.xml (renamed from OpenPGP-Keychain/src/main/res/layout/api_app_error_message.xml)0
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_remote_register_app.xml (renamed from OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml)2
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/api_remote_select_pub_keys.xml (renamed from OpenPGP-Keychain/src/main/res/layout/api_app_select_pub_keys_activity.xml)0
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml7
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml201
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml23
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/decrypt_file_fragment.xml81
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/decrypt_message_fragment.xml66
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/decrypt_signature_include.xml62
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/drawer_list.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml63
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml394
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_asymmetric_fragment.xml79
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml37
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml63
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_file_fragment.xml86
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_message_fragment.xml62
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/encrypt_symmetric_fragment.xml52
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml41
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml54
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml50
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml30
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml11
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_content.xml14
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml5
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_list_item.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml38
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml64
-rw-r--r--OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml53
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml14
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/api_app_settings.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_edit.xml6
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_list.xml43
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml8
-rw-r--r--OpenPGP-Keychain/src/main/res/menu/key_view.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html49
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html136
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html19
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html11
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html40
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-de/help_start.html6
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-el/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-el/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-el/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-es/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-et/help_about.html49
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html136
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-et/help_start.html19
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html11
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-fr/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ja/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pl/help_about.html49
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html136
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pl/help_start.html19
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html11
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ru/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-ru/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-tr/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-uk/help_about.html14
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-uk/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html49
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html136
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html19
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html11
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_about.html20
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html28
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html10
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/help_start.html12
-rw-r--r--OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html8
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_about.html15
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_changelog.html31
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_faq.html5
-rw-r--r--OpenPGP-Keychain/src/main/res/raw/help_start.html2
-rw-r--r--OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml50
-rw-r--r--OpenPGP-Keychain/src/main/res/values-de/strings.xml119
-rw-r--r--OpenPGP-Keychain/src/main/res/values-el/strings.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml5
-rw-r--r--OpenPGP-Keychain/src/main/res/values-es/strings.xml90
-rw-r--r--OpenPGP-Keychain/src/main/res/values-et/strings.xml119
-rw-r--r--OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/values-fr/strings.xml83
-rw-r--r--OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml77
-rw-r--r--OpenPGP-Keychain/src/main/res/values-ja/strings.xml85
-rw-r--r--OpenPGP-Keychain/src/main/res/values-large/dimens.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml13
-rw-r--r--OpenPGP-Keychain/src/main/res/values-pl/strings.xml468
-rw-r--r--OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/values-ru/strings.xml79
-rw-r--r--OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/values-tr/strings.xml4
-rw-r--r--OpenPGP-Keychain/src/main/res/values-uk/strings.xml85
-rw-r--r--OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml28
-rw-r--r--OpenPGP-Keychain/src/main/res/values-zh/strings.xml14
-rw-r--r--OpenPGP-Keychain/src/main/res/values/arrays.xml2
-rw-r--r--OpenPGP-Keychain/src/main/res/values/attr.xml11
-rw-r--r--OpenPGP-Keychain/src/main/res/values/dimens.xml5
-rw-r--r--OpenPGP-Keychain/src/main/res/values/strings.xml64
-rw-r--r--OpenPGP-Keychain/src/main/res/xml/adv_preferences.xml4
265 files changed, 11672 insertions, 7136 deletions
diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml
index bd45def24..cf494ba08 100644
--- a/OpenPGP-Keychain/src/main/AndroidManifest.xml
+++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml
@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.sufficientlysecure.keychain"
android:installLocation="auto"
- android:versionCode="23104"
- android:versionName="2.3.1 beta4">
+ android:versionCode="25000"
+ android:versionName="2.5">
<!--
General remarks
@@ -50,7 +50,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<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.GET_ACCOUNTS" />
<!-- android:allowBackup="false": Don't allow backup over adb backup or other apps! -->
<application
@@ -107,14 +107,12 @@
android:name=".ui.SelectPublicKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_select_recipients"
- android:launchMode="singleTop">
- </activity>
+ android:launchMode="singleTop"></activity>
<activity
android:name=".ui.SelectSecretKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_select_secret_key"
- android:launchMode="singleTop">
- </activity>
+ android:launchMode="singleTop"></activity>
<activity
android:name=".ui.EncryptActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@@ -153,23 +151,23 @@
<!--&lt;!&ndash; VIEW with mimeType: TODO (from email app) &ndash;&gt;-->
<!--<intent-filter android:label="@string/intent_import_key">-->
- <!--<action android:name="android.intent.action.VIEW" />-->
+ <!--<action android:name="android.intent.action.VIEW" />-->
- <!--<category android:name="android.intent.category.BROWSABLE" />-->
- <!--<category android:name="android.intent.category.DEFAULT" />-->
+ <!--<category android:name="android.intent.category.BROWSABLE" />-->
+ <!--<category android:name="android.intent.category.DEFAULT" />-->
- <!--&lt;!&ndash; mime type as defined in http://tools.ietf.org/html/rfc3156 &ndash;&gt;-->
- <!--<data android:mimeType="application/pgp-signature" />-->
+ <!--&lt;!&ndash; mime type as defined in http://tools.ietf.org/html/rfc3156 &ndash;&gt;-->
+ <!--<data android:mimeType="application/pgp-signature" />-->
<!--</intent-filter>-->
<!--&lt;!&ndash; VIEW with mimeType: TODO (from email app) &ndash;&gt;-->
<!--<intent-filter android:label="@string/intent_import_key">-->
- <!--<action android:name="android.intent.action.VIEW" />-->
+ <!--<action android:name="android.intent.action.VIEW" />-->
- <!--<category android:name="android.intent.category.BROWSABLE" />-->
- <!--<category android:name="android.intent.category.DEFAULT" />-->
+ <!--<category android:name="android.intent.category.BROWSABLE" />-->
+ <!--<category android:name="android.intent.category.DEFAULT" />-->
- <!--&lt;!&ndash; mime type as defined in http://tools.ietf.org/html/rfc3156 &ndash;&gt;-->
- <!--<data android:mimeType="application/pgp-encrypted" />-->
+ <!--&lt;!&ndash; mime type as defined in http://tools.ietf.org/html/rfc3156 &ndash;&gt;-->
+ <!--<data android:mimeType="application/pgp-encrypted" />-->
<!--</intent-filter>-->
<!-- Keychain's own Actions -->
<!-- DECRYPT with text as extra -->
@@ -245,7 +243,7 @@
<activity
android:name=".ui.PreferencesActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
- android:label="@string/title_preferences" >
+ android:label="@string/title_preferences">
<intent-filter>
<action android:name="org.sufficientlysecure.keychain.ui.PREFS_GEN" />
<category android:name="android.intent.category.DEFAULT" />
@@ -383,44 +381,49 @@
android:exported="false"
android:process=":passphrase_cache" />
<service
- android:name="org.sufficientlysecure.keychain.service.KeychainIntentService"
+ android:name=".service.KeychainIntentService"
android:exported="false" />
<provider
- android:name="org.sufficientlysecure.keychain.provider.KeychainProvider"
+ android:name=".provider.KeychainProvider"
android:authorities="org.sufficientlysecure.keychain.provider"
android:exported="false" />
<!-- Internal classes of the remote APIs (not exported) -->
<activity
- android:name="org.sufficientlysecure.keychain.service.remote.RemoteServiceActivity"
+ android:name=".remote.ui.RemoteServiceActivity"
android:exported="false"
- android:label="@string/app_name" />
- <!--android:launchMode="singleTop"-->
- <!--android:process=":remote_api"-->
+ android:label="@string/app_name"
+ android:launchMode="singleTop"
+ android:process=":remote_api" />
<activity
- android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity"
+ android:name=".remote.ui.AppsListActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:exported="false"
android:label="@string/title_api_registered_apps" />
<activity
- android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsActivity"
+ android:name=".remote.ui.AppSettingsActivity"
+ android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
+ android:exported="false">
+
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".remote.ui.AppsListActivity" />
+ </activity>
+ <activity
+ android:name=".remote.ui.AccountSettingsActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:exported="false" />
<!-- OpenPGP Remote API -->
<service
- android:name="org.sufficientlysecure.keychain.service.remote.OpenPgpService"
+ android:name=".remote.OpenPgpService"
android:enabled="true"
android:exported="true"
android:process=":remote_api">
<intent-filter>
<action android:name="org.openintents.openpgp.IOpenPgpService" />
</intent-filter>
-
- <meta-data
- android:name="api_version"
- android:value="1" />
</service>
<!-- Extended Remote API -->
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 011cd9663..5b9f53b09 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -19,6 +19,11 @@ package org.sufficientlysecure.keychain;
import android.os.Environment;
import org.spongycastle.jce.provider.BouncyCastleProvider;
+import org.sufficientlysecure.keychain.remote.ui.AppsListActivity;
+import org.sufficientlysecure.keychain.ui.DecryptActivity;
+import org.sufficientlysecure.keychain.ui.EncryptActivity;
+import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
+import org.sufficientlysecure.keychain.ui.KeyListActivity;
public final class Constants {
@@ -42,7 +47,7 @@ public final class Constants {
public static final class Path {
public static final String APP_DIR = Environment.getExternalStorageDirectory()
- + "/OpenPGP-Keychain";
+ + "/OpenKeychain";
public static final String APP_DIR_FILE_SEC = APP_DIR + "/secexport.asc";
public static final String APP_DIR_FILE_PUB = APP_DIR + "/pubexport.asc";
}
@@ -50,10 +55,10 @@ public final class Constants {
public static final class Pref {
public static final String DEFAULT_ENCRYPTION_ALGORITHM = "defaultEncryptionAlgorithm";
public static final String DEFAULT_HASH_ALGORITHM = "defaultHashAlgorithm";
- public static final String DEFAULT_ASCII_ARMOUR = "defaultAsciiArmour";
+ public static final String DEFAULT_ASCII_ARMOR = "defaultAsciiArmor";
public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression";
public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression";
- public static final String PASS_PHRASE_CACHE_TTL = "passphraseCacheTtl";
+ public static final String PASSPHRASE_CACHE_TTL = "passphraseCacheTtl";
public static final String LANGUAGE = "language";
public static final String FORCE_V3_SIGNATURES = "forceV3Signatures";
public static final String KEY_SERVERS = "keyServers";
@@ -63,4 +68,18 @@ public final class Constants {
public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu";
}
+ public static final class DrawerItems {
+ public static final Class KEY_LIST = KeyListActivity.class;
+ public static final Class ENCRYPT = EncryptActivity.class;
+ public static final Class DECRYPT = DecryptActivity.class;
+ public static final Class IMPORT_KEYS = ImportKeysActivity.class;
+ public static final Class REGISTERED_APPS_LIST = AppsListActivity.class;
+ public static final Class[] ARRAY = new Class[]{
+ KEY_LIST,
+ ENCRYPT,
+ DECRYPT,
+ IMPORT_KEYS,
+ REGISTERED_APPS_LIST
+ };
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
index 1d79edd43..784ec340e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java
@@ -119,6 +119,7 @@ public final class Id {
public static final int secret_key = 0x21070002;
public static final int user_id = 0x21070003;
public static final int key = 0x21070004;
+ public static final int public_secret_key = 0x21070005;
}
public static final class choice {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 98b827542..9b80e76f3 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -17,16 +17,17 @@
package org.sufficientlysecure.keychain;
-import java.io.File;
-import java.security.Provider;
-import java.security.Security;
+import android.app.Application;
+import android.os.Environment;
import org.spongycastle.jce.provider.BouncyCastleProvider;
+
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PRNGFixes;
-import android.app.Application;
-import android.os.Environment;
+import java.io.File;
+import java.security.Provider;
+import java.security.Security;
public class KeychainApplication extends Application {
@@ -40,14 +41,14 @@ public class KeychainApplication extends Application {
/*
* Sets Bouncy (Spongy) Castle as preferred security provider
- *
+ *
* insertProviderAt() position starts from 1
*/
Security.insertProviderAt(new BouncyCastleProvider(), 1);
/*
* apply RNG fixes
- *
+ *
* among other things, executes Security.insertProviderAt(new
* LinuxPRNGSecureRandomProvider(), 1) for Android <= SDK 17
*/
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
index 3164de7d1..1cac5762d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/compatibility/ClipboardReflection.java
@@ -17,20 +17,20 @@
package org.sufficientlysecure.keychain.compatibility;
-import java.lang.reflect.Method;
-
import android.content.Context;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
+import java.lang.reflect.Method;
+
public class ClipboardReflection {
private static final String clipboardLabel = "Keychain";
/**
* Wrapper around ClipboardManager based on Android version using Reflection API
- *
+ *
* @param context
* @param text
*/
@@ -57,7 +57,7 @@ public class ClipboardReflection {
/**
* Wrapper around ClipboardManager based on Android version using Reflection API
- *
+ *
* @param context
*/
public static CharSequence getClipboardText(Context context) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
index 91e50637e..a26df556d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java
@@ -37,7 +37,6 @@ public class ActionBarHelper {
* @param activity
*/
public static void setBackButton(ActionBarActivity activity) {
- // set actionbar without home button if called from another app
final ActionBar actionBar = activity.getSupportActionBar();
Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)="
+ activity.getCallingPackage());
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
index 557d75dbf..810f22d8e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java
@@ -30,6 +30,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
@@ -47,23 +48,21 @@ public class ExportHelper {
this.mActivity = activity;
}
- public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) {
- long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment());
-
+ public void deleteKey(Uri dataUri, Handler deleteHandler) {
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler);
+ long masterKeyId = ProviderHelper.getMasterKeyId(mActivity, dataUri);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- new long[]{keyRingRowId}, keyType);
-
+ new long[]{ masterKeyId });
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
}
/**
* Show dialog where to export keys
*/
- public void showExportKeysDialog(final long[] rowIds, final int keyType,
- final String exportFilename) {
+ public void showExportKeysDialog(final long[] masterKeyIds, final String exportFilename,
+ final boolean showSecretCheckbox) {
mExportFilename = exportFilename;
// Message is received after file is selected
@@ -74,7 +73,7 @@ public class ExportHelper {
Bundle data = message.getData();
mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- exportKeys(rowIds, keyType);
+ exportKeys(masterKeyIds, data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED));
}
}
};
@@ -85,7 +84,7 @@ public class ExportHelper {
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
public void run() {
String title = null;
- if (rowIds == null) {
+ if (masterKeyIds == null) {
// export all keys
title = mActivity.getString(R.string.title_export_keys);
} else {
@@ -93,15 +92,12 @@ public class ExportHelper {
title = mActivity.getString(R.string.title_export_key);
}
- String message = null;
- if (keyType == Id.type.public_key) {
- message = mActivity.getString(R.string.specify_file_to_export_to);
- } else {
- message = mActivity.getString(R.string.specify_file_to_export_secret_keys_to);
- }
+ String message = mActivity.getString(R.string.specify_file_to_export_to);
+ String checkMsg = showSecretCheckbox ?
+ mActivity.getString(R.string.also_export_secret_keys) : null;
mFileDialog = FileDialogFragment.newInstance(messenger, title, message,
- exportFilename, null);
+ exportFilename, checkMsg);
mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog");
}
@@ -111,7 +107,7 @@ public class ExportHelper {
/**
* Export keys
*/
- public void exportKeys(long[] rowIds, int keyType) {
+ public void exportKeys(long[] masterKeyIds, boolean exportSecret) {
Log.d(Constants.TAG, "exportKeys started");
// Send all information needed to service to export key in other thread
@@ -123,17 +119,17 @@ public class ExportHelper {
Bundle data = new Bundle();
data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename);
- data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType);
+ data.putBoolean(KeychainIntentService.EXPORT_SECRET, exportSecret);
- if (rowIds == null) {
+ if (masterKeyIds == null) {
data.putBoolean(KeychainIntentService.EXPORT_ALL, true);
} else {
- data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_ROW_ID, rowIds);
+ data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
}
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after exporting is done in ApgService
+ // Message is received after exporting is done in KeychainIntentService
KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(mActivity,
mActivity.getString(R.string.progress_exporting),
ProgressDialog.STYLE_HORIZONTAL,
@@ -145,7 +141,7 @@ public class ExportHelper {
}
}) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
index 736bff02d..b31a889f0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java
@@ -17,18 +17,14 @@
package org.sufficientlysecure.keychain.helper;
-import android.graphics.Color;
import android.os.Bundle;
-import android.text.Spannable;
import android.text.SpannableStringBuilder;
-import android.text.style.ForegroundColorSpan;
+import android.text.Spanned;
+import android.text.style.StrikethroughSpan;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import java.security.DigestException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.Iterator;
import java.util.Set;
@@ -65,81 +61,10 @@ public class OtherHelper {
}
}
- public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
- SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint);
- try {
- // for each 4 characters of the fingerprint + 1 space
- for (int i = 0; i < fingerprint.length(); i += 5) {
- int spanEnd = Math.min(i + 4, fingerprint.length());
- String fourChars = fingerprint.substring(i, spanEnd);
-
- int raw = Integer.parseInt(fourChars, 16);
- byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)};
- int[] color = OtherHelper.getRgbForData(bytes);
- int r = color[0];
- int g = color[1];
- int b = color[2];
-
- // we cannot change black by multiplication, so adjust it to an almost-black grey,
- // which will then be brightened to the minimal brightness level
- if (r == 0 && g == 0 && b == 0) {
- r = 1;
- g = 1;
- b = 1;
- }
-
- // Convert rgb to brightness
- double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
-
- // If a color is too dark to be seen on black,
- // then brighten it up to a minimal brightness.
- if (brightness < 80) {
- double factor = 80.0 / brightness;
- r = Math.min(255, (int) (r * factor));
- g = Math.min(255, (int) (g * factor));
- b = Math.min(255, (int) (b * factor));
-
- // If it is too light, then darken it to a respective maximal brightness.
- } else if (brightness > 180) {
- double factor = 180.0 / brightness;
- r = (int) (r * factor);
- g = (int) (g * factor);
- b = (int) (b * factor);
- }
-
- // Create a foreground color with the 3 digest integers as RGB
- // and then converting that int to hex to use as a color
- sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)),
- i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- }
- } catch (Exception e) {
- Log.e(Constants.TAG, "Colorization failed", e);
- // if anything goes wrong, then just display the fingerprint without colour,
- // instead of partially correct colour or wrong colours
- return new SpannableStringBuilder(fingerprint);
- }
-
+ public static SpannableStringBuilder strikeOutText(CharSequence text) {
+ SpannableStringBuilder sb = new SpannableStringBuilder(text);
+ sb.setSpan(new StrikethroughSpan(), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
return sb;
}
- /**
- * Converts the given bytes to a unique RGB color using SHA1 algorithm
- *
- * @param bytes
- * @return an integer array containing 3 numeric color representations (Red, Green, Black)
- * @throws NoSuchAlgorithmException
- * @throws DigestException
- */
- public static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException {
- MessageDigest md = MessageDigest.getInstance("SHA1");
-
- md.update(bytes);
- byte[] digest = md.digest();
-
- int[] result = {((int) digest[0] + 256) % 256,
- ((int) digest[1] + 256) % 256,
- ((int) digest[2] + 256) % 256};
- return result;
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index 515201b92..ca5555fea 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -30,7 +30,7 @@ import java.util.Vector;
* Singleton Implementation of a Preference Helper
*/
public class Preferences {
- private static Preferences mPreferences;
+ private static Preferences sPreferences;
private SharedPreferences mSharedPreferences;
public static synchronized Preferences getPreferences(Context context) {
@@ -38,10 +38,10 @@ public class Preferences {
}
public static synchronized Preferences getPreferences(Context context, boolean forceNew) {
- if (mPreferences == null || forceNew) {
- mPreferences = new Preferences(context);
+ if (sPreferences == null || forceNew) {
+ sPreferences = new Preferences(context);
}
- return mPreferences;
+ return sPreferences;
}
private Preferences(Context context) {
@@ -58,8 +58,8 @@ public class Preferences {
editor.commit();
}
- public long getPassPhraseCacheTtl() {
- int ttl = mSharedPreferences.getInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, 180);
+ public long getPassphraseCacheTtl() {
+ int ttl = mSharedPreferences.getInt(Constants.Pref.PASSPHRASE_CACHE_TTL, 180);
// fix the value if it was set to "never" in previous versions, which currently is not
// supported
if (ttl == 0) {
@@ -68,9 +68,9 @@ public class Preferences {
return (long) ttl;
}
- public void setPassPhraseCacheTtl(int value) {
+ public void setPassphraseCacheTtl(int value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putInt(Constants.Pref.PASS_PHRASE_CACHE_TTL, value);
+ editor.putInt(Constants.Pref.PASSPHRASE_CACHE_TTL, value);
editor.commit();
}
@@ -118,13 +118,13 @@ public class Preferences {
editor.commit();
}
- public boolean getDefaultAsciiArmour() {
- return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, false);
+ public boolean getDefaultAsciiArmor() {
+ return mSharedPreferences.getBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, false);
}
- public void setDefaultAsciiArmour(boolean value) {
+ public void setDefaultAsciiArmor(boolean value) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
- editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOUR, value);
+ editor.putBoolean(Constants.Pref.DEFAULT_ASCII_ARMOR, value);
editor.commit();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
index fa9fcfccd..c6c62d649 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java
@@ -61,13 +61,32 @@ public class PgpConversionHelper {
* @return
*/
public static ArrayList<PGPSecretKey> BytesToPGPSecretKeyList(byte[] keysBytes) {
- PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes);
+ PGPObjectFactory factory = new PGPObjectFactory(keysBytes);
+ Object obj = null;
ArrayList<PGPSecretKey> keys = new ArrayList<PGPSecretKey>();
-
- @SuppressWarnings("unchecked")
- Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
- while (itr.hasNext()) {
- keys.add(itr.next());
+ try {
+ while ((obj = factory.nextObject()) != null) {
+ PGPSecretKey secKey = null;
+ if (obj instanceof PGPSecretKey) {
+ secKey = (PGPSecretKey) obj;
+ if (secKey == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ keys.add(secKey);
+ } else if (obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings
+ PGPSecretKeyRing keyRing = null;
+ keyRing = (PGPSecretKeyRing) obj;
+ if (keyRing == null) {
+ Log.e(Constants.TAG, "No keys given!");
+ }
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSecretKey> itr = keyRing.getSecretKeys();
+ while (itr.hasNext()) {
+ keys.add(itr.next());
+ }
+ }
+ }
+ } catch (IOException e) {
}
return keys;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 571729bc5..8a0bf99d7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -18,28 +18,58 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPCompressedData;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.spongycastle.openpgp.PGPEncryptedDataList;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPLiteralData;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPOnePassSignature;
+import org.spongycastle.openpgp.PGPOnePassSignatureList;
+import org.spongycastle.openpgp.PGPPBEEncryptedData;
+import org.spongycastle.openpgp.PGPPrivateKey;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureList;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
-import org.spongycastle.openpgp.operator.jcajce.*;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
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.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import java.io.*;
+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.security.SignatureException;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.Set;
/**
* This class uses a Builder pattern!
@@ -50,9 +80,9 @@ public class PgpDecryptVerify {
private OutputStream mOutStream;
private ProgressDialogUpdater mProgressDialogUpdater;
- private boolean mAssumeSymmetric;
+ private boolean mAllowSymmetricDecryption;
private String mPassphrase;
- private long mEnforcedKeyId;
+ private Set<Long> mAllowedKeyIds;
private PgpDecryptVerify(Builder builder) {
// private Constructor can only be called from Builder
@@ -61,9 +91,9 @@ public class PgpDecryptVerify {
this.mOutStream = builder.mOutStream;
this.mProgressDialogUpdater = builder.mProgressDialogUpdater;
- this.mAssumeSymmetric = builder.mAssumeSymmetric;
+ this.mAllowSymmetricDecryption = builder.mAllowSymmetricDecryption;
this.mPassphrase = builder.mPassphrase;
- this.mEnforcedKeyId = builder.mEnforcedKeyId;
+ this.mAllowedKeyIds = builder.mAllowedKeyIds;
}
public static class Builder {
@@ -74,9 +104,9 @@ public class PgpDecryptVerify {
// optional
private ProgressDialogUpdater mProgressDialogUpdater = null;
- private boolean mAssumeSymmetric = false;
- private String mPassphrase = "";
- private long mEnforcedKeyId = 0;
+ private boolean mAllowSymmetricDecryption = true;
+ private String mPassphrase = null;
+ private Set<Long> mAllowedKeyIds = null;
public Builder(Context context, InputData data, OutputStream outStream) {
this.mContext = context;
@@ -89,8 +119,8 @@ public class PgpDecryptVerify {
return this;
}
- public Builder assumeSymmetric(boolean assumeSymmetric) {
- this.mAssumeSymmetric = assumeSymmetric;
+ public Builder allowSymmetricDecryption(boolean allowSymmetricDecryption) {
+ this.mAllowSymmetricDecryption = allowSymmetricDecryption;
return this;
}
@@ -100,14 +130,14 @@ public class PgpDecryptVerify {
}
/**
- * Allow this key id alone for decryption.
- * This means only ciphertexts encrypted for this private key can be decrypted.
+ * Allow these key ids alone for decryption.
+ * This means only ciphertexts encrypted for one of these private key can be decrypted.
*
- * @param enforcedKeyId
+ * @param allowedKeyIds
* @return
*/
- public Builder enforcedKeyId(long enforcedKeyId) {
- this.mEnforcedKeyId = enforcedKeyId;
+ public Builder allowedKeyIds(Set<Long> allowedKeyIds) {
+ this.mAllowedKeyIds = allowedKeyIds;
return this;
}
@@ -128,35 +158,6 @@ public class PgpDecryptVerify {
}
}
- public static boolean hasSymmetricEncryption(Context context, InputStream inputStream)
- throws PgpGeneralException, IOException {
- InputStream in = PGPUtil.getDecoderStream(inputStream);
- PGPObjectFactory pgpF = new PGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
-
- // the first object might be a PGP marker packet.
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- throw new PgpGeneralException(context.getString(R.string.error_invalid_data));
- }
-
- Iterator<?> it = enc.getEncryptedDataObjects();
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- return true;
- }
- }
-
- return false;
- }
-
/**
* Decrypts and/or verifies data based on parameters of class
*
@@ -221,25 +222,82 @@ public class PgpDecryptVerify {
currentProgress += 5;
- // TODO: currently we always only look at the first known key or symmetric encryption,
- // there might be more...
- if (mAssumeSymmetric) {
- PGPPBEEncryptedData pbe = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPBEEncryptedData) {
- pbe = (PGPPBEEncryptedData) obj;
- break;
+ PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
+ PGPPBEEncryptedData encryptedDataSymmetric = null;
+ PGPSecretKey secretKey = null;
+ Iterator<?> it = enc.getEncryptedDataObjects();
+ boolean symmetricPacketFound = false;
+ // find secret key
+ while (it.hasNext()) {
+ Object obj = it.next();
+ if (obj instanceof PGPPublicKeyEncryptedData) {
+ updateProgress(R.string.progress_finding_key, currentProgress, 100);
+
+ PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
+ long masterKeyId = ProviderHelper.getMasterKeyId(mContext,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
+ );
+ PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRing(mContext, masterKeyId);
+ if (secretKeyRing == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
}
- }
+ secretKey = secretKeyRing.getSecretKey(encData.getKeyID());
+ if (secretKey == null) {
+ throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
+ }
+ // secret key exists in database
- if (pbe == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_symmetric_encryption_packet));
+ // allow only a specific key for decryption?
+ if (mAllowedKeyIds != null) {
+ Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
+ Log.d(Constants.TAG, "allowedKeyIds: " + mAllowedKeyIds);
+ Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
+
+ if (!mAllowedKeyIds.contains(masterKeyId)) {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_no_secret_key_found));
+ }
+ }
+
+ encryptedDataAsymmetric = encData;
+
+ // if no passphrase was explicitly set try to get it from the cache service
+ if (mPassphrase == null) {
+ // returns "" if key has no passphrase
+ mPassphrase =
+ PassphraseCacheService.getCachedPassphrase(mContext, masterKeyId);
+
+ // if passphrase was not cached, return here
+ // indicating that a passphrase is missing!
+ if (mPassphrase == null) {
+ returnData.setKeyIdPassphraseNeeded(masterKeyId);
+ returnData.setStatus(PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED);
+ return returnData;
+ }
+ }
+
+ // break out of while, only get first object here
+ // TODO???: There could be more pgp objects, which are not decrypted!
+ break;
+ } else if (mAllowSymmetricDecryption && obj instanceof PGPPBEEncryptedData) {
+ symmetricPacketFound = true;
+
+ encryptedDataSymmetric = (PGPPBEEncryptedData) obj;
+
+ // if no passphrase is given, return here
+ // indicating that a passphrase is missing!
+ if (mPassphrase == null) {
+ returnData.setStatus(PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED);
+ return returnData;
+ }
+
+ // break out of while, only get first object here
+ // TODO???: There could be more pgp objects, which are not decrypted!
+ break;
}
+ }
+ if (symmetricPacketFound) {
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder()
@@ -248,64 +306,11 @@ public class PgpDecryptVerify {
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mPassphrase.toCharArray());
- clear = pbe.getDataStream(decryptorFactory);
+ clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
- encryptedData = pbe;
+ encryptedData = encryptedDataSymmetric;
currentProgress += 5;
} else {
- updateProgress(R.string.progress_finding_key, currentProgress, 100);
-
- PGPPublicKeyEncryptedData pbe = null;
- PGPSecretKey secretKey = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- // find secret key
- while (it.hasNext()) {
- Object obj = it.next();
- if (obj instanceof PGPPublicKeyEncryptedData) {
- PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
- secretKey = ProviderHelper.getPGPSecretKeyByKeyId(mContext, encData.getKeyID());
- if (secretKey != null) {
- // secret key exists in database
-
- // allow only a specific key for decryption?
- if (mEnforcedKeyId != 0) {
- // TODO: improve this code! get master key directly!
- PGPSecretKeyRing secretKeyRing =
- ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, encData.getKeyID());
- long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID();
- Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID());
- Log.d(Constants.TAG, "enforcedKeyId: " + mEnforcedKeyId);
- Log.d(Constants.TAG, "masterKeyId: " + masterKeyId);
-
- if (mEnforcedKeyId != masterKeyId) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_no_secret_key_found));
- }
- }
-
- pbe = encData;
-
- // if no passphrase was explicitly set try to get it from the cache service
- if (mPassphrase == null) {
- // returns "" if key has no passphrase
- mPassphrase =
- PassphraseCacheService.getCachedPassphrase(mContext, encData.getKeyID());
-
- // if passphrase was not cached, return here
- // indicating that a passphrase is missing!
- if (mPassphrase == null) {
- returnData.setKeyPassphraseNeeded(true);
- return returnData;
- }
- }
-
- break;
- }
-
-
- }
- }
-
if (secretKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_no_secret_key_found));
}
@@ -331,9 +336,9 @@ public class PgpDecryptVerify {
PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
- clear = pbe.getDataStream(decryptorFactory);
+ clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
- encryptedData = pbe;
+ encryptedData = encryptedDataAsymmetric;
currentProgress += 5;
}
@@ -363,7 +368,7 @@ public class PgpDecryptVerify {
for (int i = 0; i < sigList.size(); ++i) {
signature = sigList.get(i);
signatureKey = ProviderHelper
- .getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
+ .getPGPPublicKeyRing(mContext, signature.getKeyID()).getPublicKey();
if (signatureKeyId == 0) {
signatureKeyId = signature.getKeyID();
}
@@ -373,10 +378,10 @@ public class PgpDecryptVerify {
signatureIndex = i;
signatureKeyId = signature.getKeyID();
String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingWithKeyId(
mContext, signatureKeyId);
if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
+ userId = PgpKeyHelper.getMainUserId(signKeyRing.getPublicKey());
}
signatureResult.setUserId(userId);
break;
@@ -388,7 +393,7 @@ public class PgpDecryptVerify {
if (signature != null) {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
} else {
@@ -546,25 +551,27 @@ public class PgpDecryptVerify {
long signatureKeyId = 0;
PGPPublicKey signatureKey = null;
for (int i = 0; i < sigList.size(); ++i) {
- signature = sigList.get(i);
- signatureKey = ProviderHelper.getPGPPublicKeyByKeyId(mContext, signature.getKeyID());
- if (signatureKeyId == 0) {
- signatureKeyId = signature.getKeyID();
- }
- if (signatureKey == null) {
+ signature = sigList.get(i);
+ signatureKeyId = signature.getKeyID();
+
+ // find data about this subkey
+ HashMap<String, Object> data = ProviderHelper.getGenericData(mContext,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(signature.getKeyID())),
+ new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
+ new int[] { ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_STRING });
+ // any luck? otherwise, try next.
+ if(data.get(KeyRings.MASTER_KEY_ID) == null) {
signature = null;
- } else {
- signatureKeyId = signature.getKeyID();
- String userId = null;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext,
- signatureKeyId);
- if (signKeyRing != null) {
- userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing));
- }
- signatureResult.setUserId(userId);
- break;
+ // do NOT reset signatureKeyId, that one is shown when no known one is found!
+ continue;
}
+
+ // this one can't fail now (yay database constraints)
+ signatureKey = ProviderHelper.getPGPPublicKeyRing(mContext, (Long) data.get(KeyRings.MASTER_KEY_ID)).getPublicKey();
+ signatureResult.setUserId((String) data.get(KeyRings.USER_ID));
+
+ break;
}
signatureResult.setKeyId(signatureKeyId);
@@ -621,11 +628,11 @@ public class PgpDecryptVerify {
long signatureKeyId = signature.getKeyID();
boolean validKeyBinding = false;
- PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context,
+ PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingWithKeyId(context,
signatureKeyId);
PGPPublicKey mKey = null;
if (signKeyRing != null) {
- mKey = PgpKeyHelper.getMasterKey(signKeyRing);
+ mKey = signKeyRing.getPublicKey();
}
if (signature.getKeyID() != mKey.getKeyID()) {
@@ -685,7 +692,8 @@ public class PgpDecryptVerify {
}
private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
- PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
+ PGPPublicKey masterPublicKey,
+ PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
index d4a4f6075..ad240e834 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java
@@ -19,27 +19,33 @@ package org.sufficientlysecure.keychain.pgp;
import android.os.Parcel;
import android.os.Parcelable;
+
import org.openintents.openpgp.OpenPgpSignatureResult;
public class PgpDecryptVerifyResult implements Parcelable {
- boolean mSymmetricPassphraseNeeded;
- boolean mKeyPassphraseNeeded;
+ public static final int SUCCESS = 1;
+ public static final int KEY_PASSHRASE_NEEDED = 2;
+ public static final int SYMMETRIC_PASSHRASE_NEEDED = 3;
+
+ int mStatus;
+ long mKeyIdPassphraseNeeded;
+
OpenPgpSignatureResult mSignatureResult;
- public boolean isSymmetricPassphraseNeeded() {
- return mSymmetricPassphraseNeeded;
+ public int getStatus() {
+ return mStatus;
}
- public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) {
- this.mSymmetricPassphraseNeeded = symmetricPassphraseNeeded;
+ public void setStatus(int mStatus) {
+ this.mStatus = mStatus;
}
- public boolean isKeyPassphraseNeeded() {
- return mKeyPassphraseNeeded;
+ public long getKeyIdPassphraseNeeded() {
+ return mKeyIdPassphraseNeeded;
}
- public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) {
- this.mKeyPassphraseNeeded = keyPassphraseNeeded;
+ public void setKeyIdPassphraseNeeded(long mKeyIdPassphraseNeeded) {
+ this.mKeyIdPassphraseNeeded = mKeyIdPassphraseNeeded;
}
public OpenPgpSignatureResult getSignatureResult() {
@@ -55,8 +61,8 @@ public class PgpDecryptVerifyResult implements Parcelable {
}
public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) {
- this.mSymmetricPassphraseNeeded = b.mSymmetricPassphraseNeeded;
- this.mKeyPassphraseNeeded = b.mKeyPassphraseNeeded;
+ this.mStatus = b.mStatus;
+ this.mKeyIdPassphraseNeeded = b.mKeyIdPassphraseNeeded;
this.mSignatureResult = b.mSignatureResult;
}
@@ -66,16 +72,16 @@ public class PgpDecryptVerifyResult implements Parcelable {
}
public void writeToParcel(Parcel dest, int flags) {
- dest.writeByte((byte) (mSymmetricPassphraseNeeded ? 1 : 0));
- dest.writeByte((byte) (mKeyPassphraseNeeded ? 1 : 0));
+ dest.writeInt(mStatus);
+ dest.writeLong(mKeyIdPassphraseNeeded);
dest.writeParcelable(mSignatureResult, 0);
}
public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() {
public PgpDecryptVerifyResult createFromParcel(final Parcel source) {
PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult();
- vr.mSymmetricPassphraseNeeded = source.readByte() == 1;
- vr.mKeyPassphraseNeeded = source.readByte() == 1;
+ vr.mStatus = source.readInt();
+ vr.mKeyIdPassphraseNeeded = source.readLong();
vr.mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
return vr;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 2680d77af..f884b1776 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -20,7 +20,14 @@ package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
-import org.spongycastle.openpgp.*;
+
+import org.spongycastle.openpgp.PGPEncryptedDataList;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPUtil;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -43,10 +50,10 @@ public class PgpHelper {
public static final Pattern PGP_MESSAGE = Pattern.compile(
".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", Pattern.DOTALL);
- public static final Pattern PGP_SIGNED_MESSAGE = Pattern
- .compile(
- ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
- Pattern.DOTALL);
+ public static final Pattern PGP_CLEARTEXT_SIGNATURE = Pattern
+ .compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----" +
+ "BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
+ Pattern.DOTALL);
public static final Pattern PGP_PUBLIC_KEY = Pattern.compile(
".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
@@ -96,7 +103,7 @@ public class PgpHelper {
if (obj instanceof PGPPublicKeyEncryptedData) {
gotAsymmetricEncryption = true;
PGPPublicKeyEncryptedData pbe = (PGPPublicKeyEncryptedData) obj;
- secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, pbe.getKeyID());
+ secretKey = ProviderHelper.getPGPSecretKeyRing(context, pbe.getKeyID()).getSecretKey();
if (secretKey != null) {
break;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index dbfa521e5..d03f3ccc2 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -20,8 +20,14 @@ package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
+
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -30,8 +36,12 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.*;
+import org.sufficientlysecure.keychain.util.HkpKeyServer;
+import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException;
+import org.sufficientlysecure.keychain.util.KeychainServiceListener;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -158,60 +168,69 @@ public class PgpImportExport {
return returnData;
}
- public Bundle exportKeyRings(ArrayList<Long> keyRingRowIds, int keyType,
+ public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds,
+ ArrayList<Long> secretKeyRingMasterIds,
OutputStream outStream) throws PgpGeneralException,
PGPException, IOException {
Bundle returnData = new Bundle();
- int rowIdsSize = keyRingRowIds.size();
+ int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size();
+ int progress = 0;
updateProgress(
mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
- rowIdsSize), 0, 100);
+ masterKeyIdsSize), 0, 100);
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
throw new PgpGeneralException(
mContext.getString(R.string.error_external_storage_not_ready));
}
- // For each row id
- for (int i = 0; i < rowIdsSize; ++i) {
+ // For each public masterKey id
+ for (long pubKeyMasterId : publicKeyRingMasterIds) {
+ progress++;
// Create an output stream
ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
- // If the keyType is secret get the PGPSecretKeyRing
- // based on the row id and encode it to the output
- if (keyType == Id.type.secret_key) {
- updateProgress(i * 100 / rowIdsSize / 2, 100);
- PGPSecretKeyRing secretKeyRing =
- ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i));
+ updateProgress(progress * 100 / masterKeyIdsSize, 100);
+ PGPPublicKeyRing publicKeyRing =
+ ProviderHelper.getPGPPublicKeyRing(mContext, pubKeyMasterId);
- if (secretKeyRing != null) {
- secretKeyRing.encode(arOutStream);
- }
- if (mKeychainServiceListener.hasServiceStopped()) {
- arOutStream.close();
- return null;
- }
- } else {
- updateProgress(i * 100 / rowIdsSize, 100);
- PGPPublicKeyRing publicKeyRing =
- ProviderHelper.getPGPPublicKeyRingByRowId(mContext, keyRingRowIds.get(i));
+ if (publicKeyRing != null) {
+ publicKeyRing.encode(arOutStream);
+ }
- if (publicKeyRing != null) {
- publicKeyRing.encode(arOutStream);
- }
+ if (mKeychainServiceListener.hasServiceStopped()) {
+ arOutStream.close();
+ return null;
+ }
- if (mKeychainServiceListener.hasServiceStopped()) {
- arOutStream.close();
- return null;
- }
+ arOutStream.close();
+ }
+
+ // For each secret masterKey id
+ for (long secretKeyMasterId : secretKeyRingMasterIds) {
+ progress++;
+ // Create an output stream
+ ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
+ arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext));
+
+ updateProgress(progress * 100 / masterKeyIdsSize, 100);
+ PGPSecretKeyRing secretKeyRing =
+ ProviderHelper.getPGPSecretKeyRing(mContext, secretKeyMasterId);
+
+ if (secretKeyRing != null) {
+ secretKeyRing.encode(arOutStream);
+ }
+ if (mKeychainServiceListener.hasServiceStopped()) {
+ arOutStream.close();
+ return null;
}
arOutStream.close();
}
- returnData.putInt(KeychainIntentService.RESULT_EXPORT, rowIdsSize);
+ returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize);
updateProgress(R.string.progress_done, 100, 100);
@@ -241,7 +260,6 @@ public class PgpImportExport {
}
if (save) {
- ProviderHelper.saveKeyRing(mContext, secretKeyRing);
// TODO: preserve certifications
// (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
PGPPublicKeyRing newPubRing = null;
@@ -256,6 +274,7 @@ public class PgpImportExport {
if (newPubRing != null) {
ProviderHelper.saveKeyRing(mContext, newPubRing);
}
+ ProviderHelper.saveKeyRing(mContext, secretKeyRing);
// TODO: remove status returns, use exceptions!
status = Id.return_value.ok;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index 586eb0776..4c786f555 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -18,8 +18,18 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+import android.graphics.Color;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@@ -27,7 +37,14 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.*;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -44,34 +61,6 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
- public static PGPPublicKey getMasterKey(PGPPublicKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
- public static PGPSecretKey getMasterKey(PGPSecretKeyRing keyRing) {
- if (keyRing == null) {
- return null;
- }
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- if (key.isMasterKey()) {
- return key;
- }
- }
-
- return null;
- }
-
- @SuppressWarnings("unchecked")
public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
long cnt = 0;
if (keyRing == null) {
@@ -202,9 +191,8 @@ public class PgpKeyHelper {
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(creationDate);
calendar.add(Calendar.DATE, key.getValidDays());
- Date expiryDate = calendar.getTime();
- return expiryDate;
+ return calendar.getTime();
}
public static Date getExpiryDate(PGPSecretKey key) {
@@ -212,8 +200,7 @@ public class PgpKeyHelper {
}
public static PGPPublicKey getEncryptPublicKey(Context context, long masterKeyId) {
- PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context,
- masterKeyId);
+ PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRing(context, masterKeyId);
if (keyRing == null) {
Log.e(Constants.TAG, "keyRing is null!");
return null;
@@ -227,8 +214,7 @@ public class PgpKeyHelper {
}
public static PGPSecretKey getCertificationKey(Context context, long masterKeyId) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context,
- masterKeyId);
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRing(context, masterKeyId);
if (keyRing == null) {
return null;
}
@@ -240,8 +226,7 @@ public class PgpKeyHelper {
}
public static PGPSecretKey getSigningKey(Context context, long masterKeyId) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context,
- masterKeyId);
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRing(context, masterKeyId);
if (keyRing == null) {
return null;
}
@@ -284,6 +269,33 @@ public class PgpKeyHelper {
return userId;
}
+ public static int getKeyUsage(PGPSecretKey key) {
+ return getKeyUsage(key.getPublicKey());
+ }
+
+ @SuppressWarnings("unchecked")
+ private static int getKeyUsage(PGPPublicKey key) {
+ int usage = 0;
+ if (key.getVersion() >= 4) {
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+ if (hashed != null) {
+ usage |= hashed.getKeyFlags();
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+ if (unhashed != null) {
+ usage |= unhashed.getKeyFlags();
+ }
+ }
+ }
+ return usage;
+ }
+
@SuppressWarnings("unchecked")
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
@@ -390,6 +402,36 @@ public class PgpKeyHelper {
return false;
}
+ public static boolean isAuthenticationKey(PGPSecretKey key) {
+ return isAuthenticationKey(key.getPublicKey());
+ }
+
+ @SuppressWarnings("unchecked")
+ public static boolean isAuthenticationKey(PGPPublicKey key) {
+ if (key.getVersion() <= 3) {
+ return true;
+ }
+
+ for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
+ if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
+ continue;
+ }
+ PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
+
+ if (hashed != null && (hashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
+ return true;
+ }
+
+ PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
+
+ if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public static boolean isCertificationKey(PGPSecretKey key) {
return isCertificationKey(key.getPublicKey());
}
@@ -403,7 +445,7 @@ public class PgpKeyHelper {
}
public static String getAlgorithmInfo(int algorithm, int keySize) {
- String algorithmStr = null;
+ String algorithmStr;
switch (algorithm) {
case PGPPublicKey.RSA_ENCRYPT:
@@ -434,21 +476,6 @@ public class PgpKeyHelper {
return algorithmStr;
}
- public static String getFingerPrint(Context context, long keyId) {
- PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
- // if it is no public key get it from your own keys...
- if (key == null) {
- PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
- if (secretKey == null) {
- Log.e(Constants.TAG, "Key could not be found!");
- return null;
- }
- key = secretKey.getPublicKey();
- }
-
- return convertFingerprintToHex(key.getFingerprint(), true);
- }
-
/**
* Converts fingerprint to hex (optional: with whitespaces after 4 characters)
* <p/>
@@ -456,14 +483,10 @@ public class PgpKeyHelper {
* better differentiate between numbers and letters when letters are lowercase.
*
* @param fingerprint
- * @param split split into 4 character chunks
* @return
*/
- public static String convertFingerprintToHex(byte[] fingerprint, boolean split) {
+ public static String convertFingerprintToHex(byte[] fingerprint) {
String hexString = Hex.toHexString(fingerprint);
- if (split) {
- hexString = hexString.replaceAll("(.{4})(?!$)", "$1 ");
- }
return hexString;
}
@@ -479,9 +502,18 @@ public class PgpKeyHelper {
* @return
*/
public static String convertKeyIdToHex(long keyId) {
+ long upper = keyId >> 32;
+ if (upper == 0) {
+ // this is a short key id
+ return convertKeyIdToHexShort(keyId);
+ }
return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId);
}
+ public static String convertKeyIdToHexShort(long keyId) {
+ return "0x" + convertKeyIdToHex32bit(keyId);
+ }
+
private static String convertKeyIdToHex32bit(long keyId) {
String hexString = Long.toHexString(keyId & 0xffffffffL).toLowerCase(Locale.US);
while (hexString.length() < 8) {
@@ -490,17 +522,90 @@ public class PgpKeyHelper {
return hexString;
}
+
+ public static SpannableStringBuilder colorizeFingerprint(String fingerprint) {
+ // split by 4 characters
+ fingerprint = fingerprint.replaceAll("(.{4})(?!$)", "$1 ");
+
+ // add line breaks to have a consistent "image" that can be recognized
+ char[] chars = fingerprint.toCharArray();
+ chars[24] = '\n';
+ fingerprint = String.valueOf(chars);
+
+ SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint);
+ try {
+ // for each 4 characters of the fingerprint + 1 space
+ for (int i = 0; i < fingerprint.length(); i += 5) {
+ int spanEnd = Math.min(i + 4, fingerprint.length());
+ String fourChars = fingerprint.substring(i, spanEnd);
+
+ int raw = Integer.parseInt(fourChars, 16);
+ byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)};
+ int[] color = getRgbForData(bytes);
+ int r = color[0];
+ int g = color[1];
+ int b = color[2];
+
+ // we cannot change black by multiplication, so adjust it to an almost-black grey,
+ // which will then be brightened to the minimal brightness level
+ if (r == 0 && g == 0 && b == 0) {
+ r = 1;
+ g = 1;
+ b = 1;
+ }
+
+ // Convert rgb to brightness
+ double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b;
+
+ // If a color is too dark to be seen on black,
+ // then brighten it up to a minimal brightness.
+ if (brightness < 80) {
+ double factor = 80.0 / brightness;
+ r = Math.min(255, (int) (r * factor));
+ g = Math.min(255, (int) (g * factor));
+ b = Math.min(255, (int) (b * factor));
+
+ // If it is too light, then darken it to a respective maximal brightness.
+ } else if (brightness > 180) {
+ double factor = 180.0 / brightness;
+ r = (int) (r * factor);
+ g = (int) (g * factor);
+ b = (int) (b * factor);
+ }
+
+ // Create a foreground color with the 3 digest integers as RGB
+ // and then converting that int to hex to use as a color
+ sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)),
+ i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+ } catch (Exception e) {
+ Log.e(Constants.TAG, "Colorization failed", e);
+ // if anything goes wrong, then just display the fingerprint without colour,
+ // instead of partially correct colour or wrong colours
+ return new SpannableStringBuilder(fingerprint);
+ }
+
+ return sb;
+ }
+
/**
- * Used in HkpKeyServer to convert hex encoded key ids back to long.
+ * Converts the given bytes to a unique RGB color using SHA1 algorithm
*
- * @param hexString
- * @return
+ * @param bytes
+ * @return an integer array containing 3 numeric color representations (Red, Green, Black)
+ * @throws java.security.NoSuchAlgorithmException
+ * @throws java.security.DigestException
*/
- public static long convertHexToKeyId(String hexString) {
- int len = hexString.length();
- String s2 = hexString.substring(len - 8);
- String s1 = hexString.substring(0, len - 8);
- return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
+ private static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException {
+ MessageDigest md = MessageDigest.getInstance("SHA1");
+
+ md.update(bytes);
+ byte[] digest = md.digest();
+
+ int[] result = {((int) digest[0] + 256) % 256,
+ ((int) digest[1] + 256) % 256,
+ ((int) digest[2] + 256) % 256};
+ return result;
}
/**
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 40a0b72ce..48b959738 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -17,33 +17,54 @@
package org.sufficientlysecure.keychain.pgp;
-import android.content.Context;
+import android.util.Pair;
+
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPEncryptedData;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyPair;
+import org.spongycastle.openpgp.PGPKeyRingGenerator;
+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.PGPSignature;
+import org.spongycastle.openpgp.PGPSignatureGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
+import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.PGPDigestCalculator;
-import org.spongycastle.openpgp.operator.jcajce.*;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import java.io.IOException;
import java.math.BigInteger;
-import java.security.*;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
@@ -51,8 +72,16 @@ import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
+/** This class is the single place where ALL operations that actually modify a PGP public or secret
+ * key take place.
+ *
+ * Note that no android specific stuff should be done here, ie no imports from com.android.
+ *
+ * All operations support progress reporting to a ProgressDialogUpdater passed on initialization.
+ * This indicator may be null.
+ *
+ */
public class PgpKeyOperation {
- private Context mContext;
private ProgressDialogUpdater mProgress;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
@@ -65,19 +94,18 @@ public class PgpKeyOperation {
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP};
- public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
+ public PgpKeyOperation(ProgressDialogUpdater progress) {
super();
- this.mContext = context;
this.mProgress = progress;
}
- public void updateProgress(int message, int current, int total) {
+ void updateProgress(int message, int current, int total) {
if (mProgress != null) {
mProgress.setProgress(message, current, total);
}
}
- public void updateProgress(int current, int total) {
+ void updateProgress(int current, int total) {
if (mProgress != null) {
mProgress.setProgress(current, total);
}
@@ -90,11 +118,11 @@ public class PgpKeyOperation {
* @param keySize
* @param passphrase
* @param isMasterKey
- * @return
+ * @return A newly created PGPSecretKey
* @throws NoSuchAlgorithmException
* @throws PGPException
* @throws NoSuchProviderException
- * @throws PgpGeneralException
+ * @throws PgpGeneralMsgIdException
* @throws InvalidAlgorithmParameterException
*/
@@ -102,18 +130,18 @@ public class PgpKeyOperation {
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
- PgpGeneralException, InvalidAlgorithmParameterException {
+ PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
if (keySize < 512) {
- throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
+ throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
}
if (passphrase == null) {
passphrase = "";
}
- int algorithm = 0;
- KeyPairGenerator keyGen = null;
+ int algorithm;
+ KeyPairGenerator keyGen;
switch (algorithmChoice) {
case Id.choice.algorithm.dsa: {
@@ -125,8 +153,7 @@ public class PgpKeyOperation {
case Id.choice.algorithm.elgamal: {
if (isMasterKey) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
+ throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
}
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
@@ -148,8 +175,7 @@ public class PgpKeyOperation {
}
default: {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_unknown_algorithm_choice));
+ throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
}
}
@@ -165,193 +191,114 @@ public class PgpKeyOperation {
PGPEncryptedData.CAST5, sha1Calc)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPSecretKey secKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
- sha1Calc, isMasterKey, keyEncryptor);
-
- return secKey;
+ return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
+ sha1Calc, isMasterKey, keyEncryptor);
}
- public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
- String newPassPhrase) throws IOException, PGPException,
- NoSuchProviderException {
+ public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassphrase,
+ String newPassphrase)
+ throws IOException, PGPException, NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);
- if (oldPassPhrase == null) {
- oldPassPhrase = "";
+ if (oldPassphrase == null) {
+ oldPassphrase = "";
}
- if (newPassPhrase == null) {
- newPassPhrase = "";
+ if (newPassphrase == null) {
+ newPassphrase = "";
}
PGPSecretKeyRing newKeyRing = PGPSecretKeyRing.copyWithNewPassword(
keyRing,
new JcePBESecretKeyDecryptorBuilder(new JcaPGPDigestCalculatorProviderBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build()).setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()),
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray()),
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
- .getKeyEncryptionAlgorithm()).build(newPassPhrase.toCharArray()));
-
- updateProgress(R.string.progress_saving_key_ring, 50, 100);
-
- ProviderHelper.saveKeyRing(mContext, newKeyRing);
+ .getKeyEncryptionAlgorithm()).build(newPassphrase.toCharArray()));
- updateProgress(R.string.progress_done, 100, 100);
+ return newKeyRing;
}
- public void buildSecretKey(ArrayList<String> userIds, ArrayList<PGPSecretKey> keys,
- ArrayList<Integer> keysUsages, ArrayList<GregorianCalendar> keysExpiryDates,
- PGPPublicKey oldPublicKey, String oldPassPhrase,
- String newPassPhrase) throws PgpGeneralException, NoSuchProviderException,
- PGPException, NoSuchAlgorithmException, SignatureException, IOException {
-
- Log.d(Constants.TAG, "userIds: " + userIds.toString());
-
- updateProgress(R.string.progress_building_key, 0, 100);
+ private Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildNewSecretKey(
+ ArrayList<String> userIds, ArrayList<PGPSecretKey> keys,
+ ArrayList<GregorianCalendar> keysExpiryDates,
+ ArrayList<Integer> keysUsages,
+ String newPassphrase, String oldPassphrase)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
- if (oldPassPhrase == null) {
- oldPassPhrase = "";
- }
- if (newPassPhrase == null) {
- newPassPhrase = "";
- }
+ int usageId = keysUsages.get(0);
+ boolean canSign;
+ String mainUserId = userIds.get(0);
- updateProgress(R.string.progress_preparing_master_key, 10, 100);
-
- // prepare keyring generator with given master public and secret key
- PGPKeyRingGenerator keyGen;
- PGPPublicKey masterPublicKey; {
-
- String mainUserId = userIds.get(0);
-
- // prepare the master key pair
- PGPKeyPair masterKeyPair; {
-
- PGPSecretKey masterKey = keys.get(0);
-
- // this removes all userIds and certifications previously attached to the masterPublicKey
- PGPPublicKey tmpKey = masterKey.getPublicKey();
- masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(),
- tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime());
-
- // already done by code above:
- // PGPPublicKey masterPublicKey = masterKey.getPublicKey();
- // // Somehow, the PGPPublicKey already has an empty certification attached to it when the
- // // keyRing is generated the first time, we remove that when it exists, before adding the
- // new
- // // ones
- // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey,
- // "");
- // if (masterPublicKeyRmCert != null) {
- // masterPublicKey = masterPublicKeyRmCert;
- // }
-
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray());
- PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
-
- updateProgress(R.string.progress_certifying_master_key, 20, 100);
-
- // re-add old certificates, or create new ones for new uids
- for (String userId : userIds) {
- // re-add certs for this uid, take a note if self-signed cert is in there
- boolean foundSelfSign = false;
- Iterator<PGPSignature> it = tmpKey.getSignaturesForID(userId);
- if(it != null) for(PGPSignature sig : new IterableIterator<PGPSignature>(it)) {
- if(sig.getKeyID() == masterPublicKey.getKeyID()) {
- // already have a self sign? skip this other one, then.
- // note: PGPKeyRingGenerator adds one cert for the main user id, which
- // will lead to duplicates. unfortunately, if we add any other here
- // first, that will change the main user id order...
- if(foundSelfSign)
- continue;
- foundSelfSign = true;
- }
- Log.d(Constants.TAG, "adding old sig for " + userId + " from "
- + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()));
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, sig);
- }
+ PGPSecretKey masterKey = keys.get(0);
- // there was an old self-signed certificate for this uid
- if(foundSelfSign)
- continue;
+ // this removes all userIds and certifications previously attached to the masterPublicKey
+ PGPPublicKey masterPublicKey = masterKey.getPublicKey();
- Log.d(Constants.TAG, "generating self-signed cert for " + userId);
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassphrase.toCharArray());
+ PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
- PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ updateProgress(R.string.progress_certifying_master_key, 20, 100);
- sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ for (String userId : userIds) {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
- PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
- masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
- }
-
- masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
- }
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification);
+ }
- PGPSignatureSubpacketGenerator hashedPacketsGen;
- PGPSignatureSubpacketGenerator unhashedPacketsGen; {
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
- hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
- int usageId = keysUsages.get(0);
- boolean canEncrypt =
- (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt);
+ hashedPacketsGen.setKeyFlags(true, usageId);
- int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA;
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- hashedPacketsGen.setKeyFlags(true, keyFlags);
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
- hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
- hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
- hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
-
- if (keysExpiryDates.get(0) != null) {
- GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
- creationDate.setTime(masterPublicKey.getCreationTime());
- GregorianCalendar expiryDate = keysExpiryDates.get(0);
- //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
- //here we purposefully ignore partial days in each date - long type has no fractional part!
- long numDays =
- (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
- if (numDays <= 0) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_expiry_must_come_after_creation));
- }
- hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
- } else {
- //do this explicitly, although since we're rebuilding,
- hashedPacketsGen.setKeyExpirationTime(false, 0);
- //this happens anyway
- }
+ if (keysExpiryDates.get(0) != null) {
+ GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(masterPublicKey.getCreationTime());
+ GregorianCalendar expiryDate = keysExpiryDates.get(0);
+ //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
+ //here we purposefully ignore partial days in each date - long type has no fractional part!
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
}
+ hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
+ }
- updateProgress(R.string.progress_building_master_key, 30, 100);
-
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(
- HashAlgorithmTags.SHA1);
- PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder(
- masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1);
+ updateProgress(R.string.progress_building_master_key, 30, 100);
- // Build key encrypter based on passphrase
- PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- newPassPhrase.toCharArray());
+ // define hashing and signing algos
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(
+ HashAlgorithmTags.SHA1);
+ PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder(
+ masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1);
- keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
- masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(),
- unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
+ // Build key encrypter based on passphrase
+ PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ newPassphrase.toCharArray());
- }
+ PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(),
+ unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
updateProgress(R.string.progress_adding_sub_keys, 40, 100);
@@ -361,27 +308,21 @@ public class PgpKeyOperation {
PGPSecretKey subKey = keys.get(i);
PGPPublicKey subPublicKey = subKey.getPublicKey();
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
+ PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
- oldPassPhrase.toCharArray());
- PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor);
+ oldPassphrase.toCharArray());
+ PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2);
// TODO: now used without algorithm and creation time?! (APG 1)
PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey);
- PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
- int keyFlags = 0;
-
- int usageId = keysUsages.get(i);
- boolean canSign =
- (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt);
- boolean canEncrypt =
- (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt);
+ usageId = keysUsages.get(i);
+ canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this
if (canSign) {
Date todayDate = new Date(); //both sig times the same
- keyFlags |= KeyFlags.SIGN_DATA;
// cross-certify signing keys
hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
@@ -396,10 +337,7 @@ public class PgpKeyOperation {
subPublicKey);
unhashedPacketsGen.setEmbeddedSignature(false, certification);
}
- if (canEncrypt) {
- keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- hashedPacketsGen.setKeyFlags(false, keyFlags);
+ hashedPacketsGen.setKeyFlags(false, usageId);
if (keysExpiryDates.get(i) != null) {
GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
@@ -407,17 +345,16 @@ public class PgpKeyOperation {
GregorianCalendar expiryDate = keysExpiryDates.get(i);
//note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
//here we purposefully ignore partial days in each date - long type has no fractional part!
- long numDays =
- (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0) {
- throw new PgpGeneralException
- (mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
}
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
- //do this explicitly, although since we're rebuilding,
hashedPacketsGen.setKeyExpirationTime(false, 0);
- //this happens anyway
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
}
keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate());
@@ -426,102 +363,407 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- updateProgress(R.string.progress_re_adding_certs, 80, 100);
-
- // re-add certificates from old public key
- // TODO: this only takes care of user id certificates, what about others?
- PGPPublicKey pubkey = publicKeyRing.getPublicKey();
- for(String uid : new IterableIterator<String>(pubkey.getUserIDs())) {
- for(PGPSignature sig : new IterableIterator<PGPSignature>(oldPublicKey.getSignaturesForID(uid), true)) {
- // but skip self certificates
- if(sig.getKeyID() == pubkey.getKeyID())
- continue;
- pubkey = PGPPublicKey.addCertification(pubkey, uid, sig);
+ return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
+
+ }
+
+ public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing mKR,
+ PGPPublicKeyRing pKR,
+ SaveKeyringParcel saveParcel)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
+
+ updateProgress(R.string.progress_building_key, 0, 100);
+ PGPSecretKey masterKey = saveParcel.keys.get(0);
+
+ if (saveParcel.oldPassphrase == null) {
+ saveParcel.oldPassphrase = "";
+ }
+ if (saveParcel.newPassphrase == null) {
+ saveParcel.newPassphrase = "";
+ }
+
+ if (mKR == null) {
+ return buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
+ saveParcel.keysUsages, saveParcel.newPassphrase, saveParcel.oldPassphrase); //new Keyring
+ }
+
+ /*
+ IDs - NB This might not need to happen later, if we change the way the primary ID is chosen
+ remove deleted ids
+ if the primary ID changed we need to:
+ remove all of the IDs from the keyring, saving their certifications
+ add them all in again, updating certs of IDs which have changed
+ else
+ remove changed IDs and add in with new certs
+
+ if the master key changed, we need to remove the primary ID certification, so we can add
+ the new one when it is generated, and they don't conflict
+
+ Keys
+ remove deleted keys
+ if a key is modified, re-sign it
+ do we need to remove and add in?
+
+ Todo
+ identify more things which need to be preserved - e.g. trust levels?
+ user attributes
+ */
+
+ if (saveParcel.deletedKeys != null) {
+ for (PGPSecretKey dKey : saveParcel.deletedKeys) {
+ mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey);
+ }
+ }
+
+ masterKey = mKR.getSecretKey();
+ PGPPublicKey masterPublicKey = masterKey.getPublicKey();
+
+ int usageId = saveParcel.keysUsages.get(0);
+ boolean canSign;
+ String mainUserId = saveParcel.userIDs.get(0);
+
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(saveParcel.oldPassphrase.toCharArray());
+ PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
+
+ updateProgress(R.string.progress_certifying_master_key, 20, 100);
+
+ boolean anyIDChanged = false;
+ for (String delID : saveParcel.deletedIDs) {
+ anyIDChanged = true;
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, delID);
+ }
+
+ int userIDIndex = 0;
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ hashedPacketsGen.setKeyFlags(true, usageId);
+
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
+ hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
+ hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
+
+ if (saveParcel.keysExpiryDates.get(0) != null) {
+ GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(masterPublicKey.getCreationTime());
+ GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(0);
+ //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
+ //here we purposefully ignore partial days in each date - long type has no fractional part!
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
+ hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
+ }
+
+ if (saveParcel.primaryIDChanged ||
+ !saveParcel.originalIDs.get(0).equals(saveParcel.userIDs.get(0))) {
+ anyIDChanged = true;
+ ArrayList<Pair<String, PGPSignature>> sigList = new ArrayList<Pair<String, PGPSignature>>();
+ for (String userId : saveParcel.userIDs) {
+ String origID = saveParcel.originalIDs.get(userIDIndex);
+ if (origID.equals(userId) && !saveParcel.newIDs[userIDIndex] &&
+ !userId.equals(saveParcel.originalPrimaryID) && userIDIndex != 0) {
+ Iterator<PGPSignature> origSigs = masterPublicKey.getSignaturesForID(origID);
+ // TODO: make sure this iterator only has signatures we are interested in
+ while (origSigs.hasNext()) {
+ PGPSignature origSig = origSigs.next();
+ sigList.add(new Pair<String, PGPSignature>(origID, origSig));
+ }
+ } else {
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ if (userIDIndex == 0) {
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
+ }
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ sigList.add(new Pair<String, PGPSignature>(userId, certification));
+ }
+ if (!saveParcel.newIDs[userIDIndex]) {
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID);
+ }
+ userIDIndex++;
+ }
+ for (Pair<String, PGPSignature> toAdd : sigList) {
+ masterPublicKey =
+ PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second);
+ }
+ } else {
+ for (String userId : saveParcel.userIDs) {
+ String origID = saveParcel.originalIDs.get(userIDIndex);
+ if (!origID.equals(userId) || saveParcel.newIDs[userIDIndex]) {
+ anyIDChanged = true;
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+
+ sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
+ if (userIDIndex == 0) {
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
+ sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
+ }
+ PGPSignature certification = sGen.generateCertification(userId, masterPublicKey);
+ if (!saveParcel.newIDs[userIDIndex]) {
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, origID);
+ }
+ masterPublicKey =
+ PGPPublicKey.addCertification(masterPublicKey, userId, certification);
+ }
+ userIDIndex++;
+ }
+ }
+
+ ArrayList<Pair<String, PGPSignature>> sigList = new ArrayList<Pair<String, PGPSignature>>();
+ if (saveParcel.moddedKeys[0]) {
+ userIDIndex = 0;
+ for (String userId : saveParcel.userIDs) {
+ String origID = saveParcel.originalIDs.get(userIDIndex);
+ if (!(origID.equals(saveParcel.originalPrimaryID) && !saveParcel.primaryIDChanged)) {
+ Iterator<PGPSignature> sigs = masterPublicKey.getSignaturesForID(userId);
+ // TODO: make sure this iterator only has signatures we are interested in
+ while (sigs.hasNext()) {
+ PGPSignature sig = sigs.next();
+ sigList.add(new Pair<String, PGPSignature>(userId, sig));
+ }
+ }
+ masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, userId);
+ userIDIndex++;
+ }
+ anyIDChanged = true;
+ }
+
+ //update the keyring with the new ID information
+ if (anyIDChanged) {
+ pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey);
+ mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR);
+ }
+
+ PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey);
+
+ updateProgress(R.string.progress_building_master_key, 30, 100);
+
+ // define hashing and signing algos
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(
+ HashAlgorithmTags.SHA1);
+ PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder(
+ masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1);
+
+ // Build key encryptor based on old passphrase, as some keys may be unchanged
+ PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.oldPassphrase.toCharArray());
+
+ //this generates one more signature than necessary...
+ PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION,
+ masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(),
+ unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor);
+
+ for (int i = 1; i < saveParcel.keys.size(); ++i) {
+ updateProgress(40 + 50 * i / saveParcel.keys.size(), 100);
+ if (saveParcel.moddedKeys[i]) {
+ PGPSecretKey subKey = saveParcel.keys.get(i);
+ PGPPublicKey subPublicKey = subKey.getPublicKey();
+
+ PBESecretKeyDecryptor keyDecryptor2;
+ if (saveParcel.newKeys[i]) {
+ keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ "".toCharArray());
+ } else {
+ keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.oldPassphrase.toCharArray());
+ }
+ PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2);
+ PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey);
+
+ hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
+
+ usageId = saveParcel.keysUsages.get(i);
+ canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this
+ if (canSign) {
+ Date todayDate = new Date(); //both sig times the same
+ // cross-certify signing keys
+ hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time
+ PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time
+ PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
+ subPublicKey.getAlgorithm(), PGPUtil.SHA1)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
+ sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
+ sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+ PGPSignature certification = sGen.generateCertification(masterPublicKey,
+ subPublicKey);
+ unhashedPacketsGen.setEmbeddedSignature(false, certification);
+ }
+ hashedPacketsGen.setKeyFlags(false, usageId);
+
+ if (saveParcel.keysExpiryDates.get(i) != null) {
+ GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ creationDate.setTime(subPublicKey.getCreationTime());
+ GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(i);
+ // note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
+ // here we purposefully ignore partial days in each date - long type has
+ // no fractional part!
+ long numDays = (expiryDate.getTimeInMillis() / 86400000) -
+ (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
+ hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
+ } else {
+ hashedPacketsGen.setKeyExpirationTime(false, 0);
+ // do this explicitly, although since we're rebuilding,
+ // this happens anyway
+ }
+
+ keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate());
+ // certifications will be discarded if the key is changed, because I think, for a start,
+ // they will be invalid. Binding certs are regenerated anyway, and other certs which
+ // need to be kept are on IDs and attributes
+ // TODO: don't let revoked keys be edited, other than removed - changing one would
+ // result in the revocation being wrong?
}
}
- publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, pubkey);
- updateProgress(R.string.progress_saving_key_ring, 90, 100);
+ PGPSecretKeyRing updatedSecretKeyRing = keyGen.generateSecretKeyRing();
+ //finally, update the keyrings
+ Iterator<PGPSecretKey> itr = updatedSecretKeyRing.getSecretKeys();
+ while (itr.hasNext()) {
+ PGPSecretKey theNextKey = itr.next();
+ if ((theNextKey.isMasterKey() && saveParcel.moddedKeys[0]) || !theNextKey.isMasterKey()) {
+ mKR = PGPSecretKeyRing.insertSecretKey(mKR, theNextKey);
+ pKR = PGPPublicKeyRing.insertPublicKey(pKR, theNextKey.getPublicKey());
+ }
+ }
+
+ //replace lost IDs
+ if (saveParcel.moddedKeys[0]) {
+ masterPublicKey = mKR.getPublicKey();
+ for (Pair<String, PGPSignature> toAdd : sigList) {
+ masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, toAdd.first, toAdd.second);
+ }
+ pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey);
+ mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR);
+ }
+
+ // Build key encryptor based on new passphrase
+ PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
+ PGPEncryptedData.CAST5, sha1Calc)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
+ saveParcel.newPassphrase.toCharArray());
+
+ //update the passphrase
+ mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
/* additional handy debug info
+
Log.d(Constants.TAG, " ------- in private key -------");
+
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
- for(PGPSignature sig : new IterableIterator<PGPSignature>(secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
- Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
- }
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(
+ secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ Log.d(Constants.TAG, "sig: " +
+ PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
+ }
+
}
+
Log.d(Constants.TAG, " ------- in public key -------");
+
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
- for(PGPSignature sig : new IterableIterator<PGPSignature>(publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
- Log.d(Constants.TAG, "sig: " + PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
+ for(PGPSignature sig : new IterableIterator<PGPSignature>(
+ publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
+ Log.d(Constants.TAG, "sig: " +
+ PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
}
}
+
*/
- ProviderHelper.saveKeyRing(mContext, secretKeyRing);
- ProviderHelper.saveKeyRing(mContext, publicKeyRing);
+ return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(mKR, pKR);
- updateProgress(R.string.progress_done, 100, 100);
}
/**
* Certify the given pubkeyid with the given masterkeyid.
*
- * @param masterKeyId Certifying key, must be available as secret key
- * @param pubKeyId ID of public key to certify
+ * @param certificationKey Certifying key
+ * @param publicKey public key to certify
* @param userIds User IDs to certify, must not be null or empty
* @param passphrase Passphrase of the secret key
* @return A keyring with added certifications
*/
- public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, List<String> userIds, String passphrase)
- throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException,
- PGPException, SignatureException {
- if (passphrase == null) {
- throw new PgpGeneralException("Unable to obtain passphrase");
- } else {
+ public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey,
+ List<String> userIds, String passphrase)
+ throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
+ PGPException, SignatureException {
- // create a signatureGenerator from the supplied masterKeyId and passphrase
- PGPSignatureGenerator signatureGenerator; {
+ // create a signatureGenerator from the supplied masterKeyId and passphrase
+ PGPSignatureGenerator signatureGenerator; {
- PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
- if (certificationKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
+ if (certificationKey == null) {
+ throw new PgpGeneralMsgIdException(R.string.error_signature_failed);
+ }
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_key));
- }
+ PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
+ PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
+ if (signaturePrivateKey == null) {
+ throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key);
+ }
- // TODO: SHA256 fixed?
- JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
- certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ // TODO: SHA256 fixed?
+ JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
+ certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
- signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
- }
+ signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
+ signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
+ }
- { // supply signatureGenerator with a SubpacketVector
- PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- PGPSignatureSubpacketVector packetVector = spGen.generate();
- signatureGenerator.setHashedSubpackets(packetVector);
- }
+ { // supply signatureGenerator with a SubpacketVector
+ PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
+ PGPSignatureSubpacketVector packetVector = spGen.generate();
+ signatureGenerator.setHashedSubpackets(packetVector);
+ }
- // fetch public key ring, add the certification and return it
- PGPPublicKeyRing pubring = ProviderHelper
- .getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
- PGPPublicKey signedKey = pubring.getPublicKey(pubKeyId);
- for(String userId : new IterableIterator<String>(userIds.iterator())) {
- PGPSignature sig = signatureGenerator.generateCertification(userId, signedKey);
- signedKey = PGPPublicKey.addCertification(signedKey, userId, sig);
- }
- pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
+ // fetch public key ring, add the certification and return it
+ for (String userId : new IterableIterator<String>(userIds.iterator())) {
+ PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
+ publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
+ }
+
+ return publicKey;
+ }
- return pubring;
+ /** Simple static subclass that stores two values.
+ *
+ * This is only used to return a pair of values in one function above. We specifically don't use
+ * com.android.Pair to keep this class free from android dependencies.
+ */
+ public static class Pair<K, V> {
+ public final K first;
+ public final V second;
+ public Pair(K first, V second) {
+ this.first = first;
+ this.second = second;
}
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 737e9c75d..a864a165d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -18,11 +18,28 @@
package org.sufficientlysecure.keychain.pgp;
import android.content.Context;
+
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.BCPGOutputStream;
-import org.spongycastle.openpgp.*;
+import org.spongycastle.openpgp.PGPCompressedDataGenerator;
+import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPLiteralData;
+import org.spongycastle.openpgp.PGPLiteralDataGenerator;
+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.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
-import org.spongycastle.openpgp.operator.jcajce.*;
+import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
+import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
+import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -32,7 +49,11 @@ import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
@@ -50,7 +71,7 @@ public class PgpSignEncrypt {
private boolean mEnableAsciiArmorOutput;
private int mCompressionId;
private long[] mEncryptionKeyIds;
- private String mEncryptionPassphrase;
+ private String mSymmetricPassphrase;
private int mSymmetricEncryptionAlgorithm;
private long mSignatureKeyId;
private int mSignatureHashAlgorithm;
@@ -67,7 +88,7 @@ public class PgpSignEncrypt {
this.mEnableAsciiArmorOutput = builder.mEnableAsciiArmorOutput;
this.mCompressionId = builder.mCompressionId;
this.mEncryptionKeyIds = builder.mEncryptionKeyIds;
- this.mEncryptionPassphrase = builder.mEncryptionPassphrase;
+ this.mSymmetricPassphrase = builder.mSymmetricPassphrase;
this.mSymmetricEncryptionAlgorithm = builder.mSymmetricEncryptionAlgorithm;
this.mSignatureKeyId = builder.mSignatureKeyId;
this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm;
@@ -85,8 +106,8 @@ public class PgpSignEncrypt {
private ProgressDialogUpdater mProgress = null;
private boolean mEnableAsciiArmorOutput = false;
private int mCompressionId = Id.choice.compression.none;
- private long[] mEncryptionKeyIds = new long[0];
- private String mEncryptionPassphrase = null;
+ private long[] mEncryptionKeyIds = null;
+ private String mSymmetricPassphrase = null;
private int mSymmetricEncryptionAlgorithm = 0;
private long mSignatureKeyId = Id.key.none;
private int mSignatureHashAlgorithm = 0;
@@ -119,8 +140,8 @@ public class PgpSignEncrypt {
return this;
}
- public Builder encryptionPassphrase(String encryptionPassphrase) {
- this.mEncryptionPassphrase = encryptionPassphrase;
+ public Builder symmetricPassphrase(String symmetricPassphrase) {
+ this.mSymmetricPassphrase = symmetricPassphrase;
return this;
}
@@ -181,7 +202,8 @@ public class PgpSignEncrypt {
NoSuchAlgorithmException, SignatureException {
boolean enableSignature = mSignatureKeyId != Id.key.none;
- boolean enableEncryption = (mEncryptionKeyIds.length != 0 || mEncryptionPassphrase != null);
+ boolean enableEncryption = ((mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0)
+ || mSymmetricPassphrase != null);
boolean enableCompression = (enableEncryption && mCompressionId != Id.choice.compression.none);
Log.d(Constants.TAG, "enableSignature:" + enableSignature
@@ -212,7 +234,7 @@ public class PgpSignEncrypt {
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
if (enableSignature) {
- signingKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
+ signingKeyRing = ProviderHelper.getPGPSecretKeyRingWithKeyId(mContext, mSignatureKeyId);
signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
if (signingKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
@@ -246,12 +268,12 @@ public class PgpSignEncrypt {
cPk = new PGPEncryptedDataGenerator(encryptorBuilder);
- if (mEncryptionKeyIds.length == 0) {
+ if (mSymmetricPassphrase != null) {
// Symmetric encryption
Log.d(Constants.TAG, "encryptionKeyIds length is 0 -> symmetric encryption");
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
- new JcePBEKeyEncryptionMethodGenerator(mEncryptionPassphrase.toCharArray());
+ new JcePBEKeyEncryptionMethodGenerator(mSymmetricPassphrase.toCharArray());
cPk.addMethod(symmetricEncryptionGenerator);
} else {
// Asymmetric encryption
@@ -284,7 +306,7 @@ public class PgpSignEncrypt {
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(signatureType, signaturePrivateKey);
- String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
+ String userId = PgpKeyHelper.getMainUserId(signingKeyRing.getSecretKey());
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, userId);
signatureGenerator.setHashedSubpackets(spGen.generate());
@@ -442,7 +464,7 @@ public class PgpSignEncrypt {
}
PGPSecretKeyRing signingKeyRing =
- ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, mSignatureKeyId);
+ ProviderHelper.getPGPSecretKeyRingWithKeyId(mContext, mSignatureKeyId);
PGPSecretKey signingKey = PgpKeyHelper.getSigningKey(mContext, mSignatureKeyId);
if (signingKey == null) {
throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
@@ -483,7 +505,7 @@ public class PgpSignEncrypt {
signatureGenerator.init(type, signaturePrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
- String userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signingKeyRing));
+ String userId = PgpKeyHelper.getMainUserId(signingKeyRing.getSecretKey());
spGen.setSignerUserID(false, userId);
signatureGenerator.setHashedSubpackets(spGen.generate());
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
index 54601173d..5bb1665b6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpToX509.java
@@ -18,7 +18,13 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.asn1.DERObjectIdentifier;
-import org.spongycastle.asn1.x509.*;
+import org.spongycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.spongycastle.asn1.x509.BasicConstraints;
+import org.spongycastle.asn1.x509.GeneralName;
+import org.spongycastle.asn1.x509.GeneralNames;
+import org.spongycastle.asn1.x509.SubjectKeyIdentifier;
+import org.spongycastle.asn1.x509.X509Extensions;
+import org.spongycastle.asn1.x509.X509Name;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -29,13 +35,14 @@ import org.spongycastle.x509.extension.SubjectKeyIdentifierStructure;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.math.BigInteger;
-import java.security.*;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
@@ -43,6 +50,11 @@ import java.util.Date;
import java.util.Iterator;
import java.util.Vector;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
public class PgpToX509 {
public static final String DN_COMMON_PART_O = "OpenPGP to X.509 Bridge";
public static final String DN_COMMON_PART_OU = "OpenPGP Keychain cert";
@@ -71,9 +83,10 @@ public class PgpToX509 {
* @throws Exception
* @author Bruno Harbulot
*/
- public static X509Certificate createSelfSignedCert(PublicKey pubKey, PrivateKey privKey,
- X509Name subject, Date startDate, Date endDate, String subjAltNameURI)
- throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException,
+ public static X509Certificate createSelfSignedCert(
+ PublicKey pubKey, PrivateKey privKey, X509Name subject, Date startDate, Date endDate,
+ String subjAltNameURI)
+ throws InvalidKeyException, IllegalStateException, NoSuchAlgorithmException,
SignatureException, CertificateException, NoSuchProviderException {
X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
@@ -170,10 +183,10 @@ public class PgpToX509 {
/**
* Creates a self-signed certificate from a PGP Secret Key.
*
- * @param pgpSecKey PGP Secret Key (from which one can extract the public and private keys and other
- * attributes).
- * @param pgpPrivKey PGP Private Key corresponding to the Secret Key (password callbacks should be done
- * before calling this method)
+ * @param pgpSecKey PGP Secret Key (from which one can extract the public and private
+ * keys and other attributes).
+ * @param pgpPrivKey PGP Private Key corresponding to the Secret Key (password callbacks
+ * should be done before calling this method)
* @param subjAltNameURI optional URI to embed in the subject alternative-name
* @return self-signed certificate
* @throws PGPException
@@ -184,9 +197,9 @@ public class PgpToX509 {
* @throws CertificateException
* @author Bruno Harbulot
*/
- public static X509Certificate createSelfSignedCert(PGPSecretKey pgpSecKey,
- PGPPrivateKey pgpPrivKey, String subjAltNameURI) throws PGPException,
- NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException,
+ public static X509Certificate createSelfSignedCert(
+ PGPSecretKey pgpSecKey, PGPPrivateKey pgpPrivKey, String subjAltNameURI)
+ throws PGPException, NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException,
SignatureException, CertificateException {
// get public key from secret key
PGPPublicKey pgpPubKey = pgpSecKey.getPublicKey();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
index bb80d27ee..418445367 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralException.java
@@ -23,4 +23,7 @@ public class PgpGeneralException extends Exception {
public PgpGeneralException(String message) {
super(message);
}
+ public PgpGeneralException(String message, Throwable cause) {
+ super(message, cause);
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
new file mode 100644
index 000000000..caa7842db
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.sufficientlysecure.keychain.pgp.exception;
+
+import android.content.Context;
+
+public class PgpGeneralMsgIdException extends Exception {
+ static final long serialVersionUID = 0xf812773343L;
+
+ private final int mMessageId;
+
+ public PgpGeneralMsgIdException(int messageId) {
+ super("msg[" + messageId + "]");
+ mMessageId = messageId;
+ }
+
+ public PgpGeneralException getContextualized(Context context) {
+ return new PgpGeneralException(context.getString(mMessageId), this);
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 404c128a1..2b40300d7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -19,46 +19,47 @@ package org.sufficientlysecure.keychain.provider;
import android.net.Uri;
import android.provider.BaseColumns;
+
import org.sufficientlysecure.keychain.Constants;
public class KeychainContract {
interface KeyRingsColumns {
String MASTER_KEY_ID = "master_key_id"; // not a database id
- String TYPE = "type"; // see KeyTypes
String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob
}
interface KeysColumns {
+ String MASTER_KEY_ID = "master_key_id"; // not a database id
+ String RANK = "rank";
+
String KEY_ID = "key_id"; // not a database id
- String TYPE = "type"; // see KeyTypes
- String IS_MASTER_KEY = "is_master_key";
String ALGORITHM = "algorithm";
+ String FINGERPRINT = "fingerprint";
+
String KEY_SIZE = "key_size";
- String CAN_CERTIFY = "can_certify";
String CAN_SIGN = "can_sign";
String CAN_ENCRYPT = "can_encrypt";
+ String CAN_CERTIFY = "can_certify";
String IS_REVOKED = "is_revoked";
+
String CREATION = "creation";
String EXPIRY = "expiry";
- String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID
- String KEY_DATA = "key_data"; // PGPPublicKey/PGPSecretKey blob
- String RANK = "rank";
- String FINGERPRINT = "fingerprint";
}
interface UserIdsColumns {
- String KEY_RING_ROW_ID = "key_ring_row_id"; // foreign key to key_rings._ID
+ String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
String USER_ID = "user_id"; // not a database id
- String RANK = "rank";
+ String RANK = "rank"; // ONLY used for sorting! no key, no nothing!
+ String IS_PRIMARY = "is_primary";
}
interface CertsColumns {
- String KEY_RING_ROW_ID = "key_ring_row_id"; // verified id, foreign key to key_rings._ID
+ String MASTER_KEY_ID = "master_key_id"; // verified id, foreign key to key_rings._ID
String RANK = "rank"; // rank of verified key
- String KEY_ID = "key_id"; // verified id, not a database id
String KEY_ID_CERTIFIER = "key_id_certifier"; // verifying id, not a database id
String CREATION = "creation";
+ String EXPIRY = "expiry";
String VERIFIED = "verified";
String KEY_DATA = "key_data"; // certification blob
}
@@ -66,10 +67,15 @@ public class KeychainContract {
interface ApiAppsColumns {
String PACKAGE_NAME = "package_name";
String PACKAGE_SIGNATURE = "package_signature";
+ }
+
+ interface ApiAppsAccountsColumns {
+ String ACCOUNT_NAME = "account_name";
String KEY_ID = "key_id"; // not a database id
String ENCRYPTION_ALGORITHM = "encryption_algorithm";
String HASH_ALORITHM = "hash_algorithm";
String COMPRESSION = "compression";
+ String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
}
public static final class KeyTypes {
@@ -85,97 +91,81 @@ public class KeychainContract {
public static final String BASE_KEY_RINGS = "key_rings";
public static final String BASE_DATA = "data";
- public static final String PATH_PUBLIC = "public";
- public static final String PATH_SECRET = "secret";
+ public static final String PATH_UNIFIED = "unified";
- public static final String PATH_BY_MASTER_KEY_ID = "master_key_id";
- public static final String PATH_BY_KEY_ID = "key_id";
- public static final String PATH_BY_KEY_ROW_ID = "key_row_id";
- public static final String PATH_BY_CERTIFIER_ID = "certifier_id";
- public static final String PATH_BY_EMAILS = "emails";
- public static final String PATH_BY_LIKE_EMAIL = "like_email";
+ public static final String PATH_FIND = "find";
+ public static final String PATH_BY_EMAIL = "email";
+ public static final String PATH_BY_SUBKEY = "subkey";
+ public static final String PATH_PUBLIC = "public";
+ public static final String PATH_SECRET = "secret";
public static final String PATH_USER_IDS = "user_ids";
public static final String PATH_KEYS = "keys";
+ public static final String PATH_CERTS = "certs";
public static final String BASE_API_APPS = "api_apps";
- public static final String PATH_BY_PACKAGE_NAME = "package_name";
+ public static final String PATH_ACCOUNTS = "accounts";
- public static final String BASE_CERTS = "certs";
+ public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns {
+ public static final String MASTER_KEY_ID = "master_key_id";
+ public static final String HAS_SECRET = "has_secret";
- public static class KeyRings implements KeyRingsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
- /**
- * Use if multiple items get returned
- */
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring";
-
- /**
- * Use if a single item is returned
- */
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key_ring";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key_ring";
public static Uri buildUnifiedKeyRingsUri() {
- return CONTENT_URI;
+ return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
}
- public static Uri buildPublicKeyRingsUri() {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
+ public static Uri buildGenericKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}
-
- public static Uri buildPublicKeyRingsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId).build();
+ public static Uri buildUnifiedKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build();
}
-
- public static Uri buildPublicKeyRingsByMasterKeyIdUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC)
- .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build();
+ public static Uri buildUnifiedKeyRingUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build();
}
- public static Uri buildPublicKeyRingsByKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_KEY_ID)
- .appendPath(keyId).build();
+ public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build();
}
-
- public static Uri buildPublicKeyRingsByEmailsUri(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_EMAILS)
- .appendPath(emails).build();
+ public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) {
+ return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build();
}
- public static Uri buildPublicKeyRingsByLikeEmailUri(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(PATH_BY_LIKE_EMAIL)
- .appendPath(emails).build();
- }
+ }
- public static Uri buildSecretKeyRingsUri() {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build();
- }
+ public static class KeyRingData implements KeyRingsColumns, BaseColumns {
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_KEY_RINGS).build();
- public static Uri buildSecretKeyRingsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId).build();
- }
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key_ring_data";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key_ring_data";
- public static Uri buildSecretKeyRingsByMasterKeyIdUri(String masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET)
- .appendPath(PATH_BY_MASTER_KEY_ID).appendPath(masterKeyId).build();
+ public static Uri buildPublicKeyRingUri() {
+ return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build();
}
-
- public static Uri buildSecretKeyRingsByKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_KEY_ID)
- .appendPath(keyId).build();
+ public static Uri buildPublicKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_PUBLIC).build();
}
-
- public static Uri buildSecretKeyRingsByEmailsUri(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS)
- .appendPath(emails).build();
+ public static Uri buildPublicKeyRingUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_PUBLIC).build();
}
- public static Uri buildSecretKeyRingsByLikeEmails(String emails) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_LIKE_EMAIL)
- .appendPath(emails).build();
+ public static Uri buildSecretKeyRingUri() {
+ return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).build();
+ }
+ public static Uri buildSecretKeyRingUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_SECRET).build();
+ }
+ public static Uri buildSecretKeyRingUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_SECRET).build();
}
+
}
public static class Keys implements KeysColumns, BaseColumns {
@@ -185,131 +175,101 @@ public class KeychainContract {
/**
* Use if multiple items get returned
*/
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.key";
/**
* Use if a single item is returned
*/
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key";
-
- public static Uri buildPublicKeysUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).build();
- }
-
- public static Uri buildPublicKeysUri(String keyRingRowId, String keyRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).appendPath(keyRowId).build();
- }
-
- public static Uri buildSecretKeysUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).build();
- }
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.key";
- public static Uri buildSecretKeysUri(String keyRingRowId, String keyRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_KEYS).appendPath(keyRowId).build();
+ public static Uri buildKeysUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_KEYS).build();
}
-
- public static Uri buildKeysUri(Uri keyRingUri) {
- return keyRingUri.buildUpon().appendPath(PATH_KEYS).build();
+ public static Uri buildKeysUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_KEYS).build();
}
- public static Uri buildKeysUri(Uri keyRingUri, String keyRowId) {
- return keyRingUri.buildUpon().appendPath(PATH_KEYS).appendPath(keyRowId).build();
- }
}
public static class UserIds implements UserIdsColumns, BaseColumns {
+ public static final String VERIFIED = "verified";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
/**
* Use if multiple items get returned
*/
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.user_id";
/**
* Use if a single item is returned
*/
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.user_id";
- public static Uri buildPublicUserIdsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).build();
+ public static Uri buildUserIdsUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_USER_IDS).build();
}
-
- public static Uri buildPublicUserIdsUri(String keyRingRowId, String userIdRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
+ public static Uri buildUserIdsUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_USER_IDS).build();
}
+ }
- public static Uri buildSecretUserIdsUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).build();
- }
+ public static class ApiApps implements ApiAppsColumns, BaseColumns {
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_API_APPS).build();
- public static Uri buildSecretUserIdsUri(String keyRingRowId, String userIdRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(keyRingRowId)
- .appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
- }
+ /**
+ * Use if multiple items get returned
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.api_apps";
- public static Uri buildUserIdsUri(Uri keyRingUri) {
- return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).build();
- }
+ /**
+ * Use if a single item is returned
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.api_app";
- public static Uri buildUserIdsUri(Uri keyRingUri, String userIdRowId) {
- return keyRingUri.buildUpon().appendPath(PATH_USER_IDS).appendPath(userIdRowId).build();
+ public static Uri buildByPackageNameUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build();
}
}
- public static class ApiApps implements ApiAppsColumns, BaseColumns {
+ public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_API_APPS).build();
/**
* Use if multiple items get returned
*/
- public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps";
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.sufficientlysecure.openkeychain.api_app.accounts";
/**
* Use if a single item is returned
*/
- public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps";
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.sufficientlysecure.openkeychain.api_app.account";
- public static Uri buildIdUri(String rowId) {
- return CONTENT_URI.buildUpon().appendPath(rowId).build();
+ public static Uri buildBaseUri(String packageName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
+ .build();
}
- public static Uri buildByPackageNameUri(String packageName) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName)
- .build();
+ public static Uri buildByPackageAndAccountUri(String packageName, String accountName) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS)
+ .appendEncodedPath(accountName).build();
}
}
public static class Certs implements CertsColumns, BaseColumns {
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_CERTS).build();
+ public static final String USER_ID = UserIdsColumns.USER_ID;
+ public static final String SIGNER_UID = "signer_user_id";
- // do we even need this one...? just using it as default for database insert notifications~
- public static Uri buildCertsUri(String rowId) {
- return CONTENT_URI.buildUpon().appendPath(rowId).build();
- }
-
- public static Uri buildCertsByKeyRowIdUri(String keyRingRowId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ROW_ID)
- .appendPath(keyRingRowId).build();
- }
+ public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
+ .appendPath(BASE_KEY_RINGS).build();
- public static Uri buildCertsByKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(keyId)
- .build();
+ public static Uri buildCertsUri(String masterKeyId) {
+ return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_CERTS).build();
}
-
- public static Uri buildCertsByCertifierKeyIdUri(String keyId) {
- return CONTENT_URI.buildUpon().appendPath(PATH_BY_CERTIFIER_ID).appendPath(keyId)
- .build();
+ public static Uri buildCertsUri(Uri uri) {
+ return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_CERTS).build();
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index cc6a1f1e1..fda1783cb 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -18,97 +18,156 @@
package org.sufficientlysecure.keychain.provider;
import android.content.Context;
+import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
+
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
import org.sufficientlysecure.keychain.util.Log;
+import java.io.IOException;
+
public class KeychainDatabase extends SQLiteOpenHelper {
- private static final String DATABASE_NAME = "apg.db";
- private static final int DATABASE_VERSION = 8;
+ private static final String DATABASE_NAME = "openkeychain.db";
+ private static final int DATABASE_VERSION = 1;
+ static Boolean apg_hack = false;
public interface Tables {
- String KEY_RINGS = "key_rings";
+ String KEY_RINGS_PUBLIC = "keyrings_public";
+ String KEY_RINGS_SECRET = "keyrings_secret";
String KEYS = "keys";
String USER_IDS = "user_ids";
- String API_APPS = "api_apps";
String CERTS = "certs";
+ String API_APPS = "api_apps";
+ String API_ACCOUNTS = "api_accounts";
}
- private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS
- + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + KeyRingsColumns.MASTER_KEY_ID + " INT64, "
- + KeyRingsColumns.TYPE + " INTEGER, "
- + KeyRingsColumns.KEY_RING_DATA + " BLOB)";
-
- private static final String CREATE_KEYS = "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " ("
- + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + KeysColumns.KEY_ID + " INT64, "
- + KeysColumns.TYPE + " INTEGER, "
- + KeysColumns.IS_MASTER_KEY + " INTEGER, "
- + KeysColumns.ALGORITHM + " INTEGER, "
- + KeysColumns.KEY_SIZE + " INTEGER, "
- + KeysColumns.CAN_CERTIFY + " INTEGER, "
- + KeysColumns.CAN_SIGN + " INTEGER, "
- + KeysColumns.CAN_ENCRYPT + " INTEGER, "
- + KeysColumns.IS_REVOKED + " INTEGER, "
- + KeysColumns.CREATION + " INTEGER, "
- + KeysColumns.EXPIRY + " INTEGER, "
- + KeysColumns.KEY_DATA + " BLOB,"
- + KeysColumns.RANK + " INTEGER, "
- + KeysColumns.FINGERPRINT + " BLOB, "
- + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
- + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
- + BaseColumns._ID + ") ON DELETE CASCADE)";
-
- private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS
- + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + UserIdsColumns.USER_ID + " TEXT, "
- + UserIdsColumns.RANK + " INTEGER, "
- + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY("
- + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "("
- + BaseColumns._ID + ") ON DELETE CASCADE)";
+ private static final String CREATE_KEYRINGS_PUBLIC =
+ "CREATE TABLE IF NOT EXISTS keyrings_public ("
+ + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY,"
+ + KeyRingsColumns.KEY_RING_DATA + " BLOB"
+ + ")";
+
+ private static final String CREATE_KEYRINGS_SECRET =
+ "CREATE TABLE IF NOT EXISTS keyrings_secret ("
+ + KeyRingsColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY,"
+ + KeyRingsColumns.KEY_RING_DATA + " BLOB,"
+ + "FOREIGN KEY(" + KeyRingsColumns.MASTER_KEY_ID + ") "
+ + "REFERENCES keyrings_public(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ + ")";
+
+ private static final String CREATE_KEYS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.KEYS + " ("
+ + KeysColumns.MASTER_KEY_ID + " INTEGER, "
+ + KeysColumns.RANK + " INTEGER, "
+
+ + KeysColumns.KEY_ID + " INTEGER, "
+ + KeysColumns.KEY_SIZE + " INTEGER, "
+ + KeysColumns.ALGORITHM + " INTEGER, "
+ + KeysColumns.FINGERPRINT + " BLOB, "
+
+ + KeysColumns.CAN_CERTIFY + " BOOLEAN, "
+ + KeysColumns.CAN_SIGN + " BOOLEAN, "
+ + KeysColumns.CAN_ENCRYPT + " BOOLEAN, "
+ + KeysColumns.IS_REVOKED + " BOOLEAN, "
+
+ + KeysColumns.CREATION + " INTEGER, "
+ + KeysColumns.EXPIRY + " INTEGER, "
+
+ + "PRIMARY KEY(" + KeysColumns.MASTER_KEY_ID + ", " + KeysColumns.RANK + "),"
+ + "FOREIGN KEY(" + KeysColumns.MASTER_KEY_ID + ") REFERENCES "
+ + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ + ")";
+
+ private static final String CREATE_USER_IDS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + "("
+ + UserIdsColumns.MASTER_KEY_ID + " INTEGER, "
+ + UserIdsColumns.USER_ID + " CHARMANDER, "
+
+ + UserIdsColumns.IS_PRIMARY + " BOOLEAN, "
+ + UserIdsColumns.RANK+ " INTEGER, "
+
+ + "PRIMARY KEY(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.USER_ID + "), "
+ + "UNIQUE (" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + "), "
+ + "FOREIGN KEY(" + UserIdsColumns.MASTER_KEY_ID + ") REFERENCES "
+ + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ + ")";
+
+ private static final String CREATE_CERTS =
+ "CREATE TABLE IF NOT EXISTS " + Tables.CERTS + "("
+ + CertsColumns.MASTER_KEY_ID + " INTEGER,"
+ + CertsColumns.RANK + " INTEGER, " // rank of certified uid
+
+ + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key
+ + CertsColumns.CREATION + " INTEGER, "
+ + CertsColumns.EXPIRY + " INTEGER, "
+ + CertsColumns.VERIFIED + " INTEGER, "
+
+ + CertsColumns.KEY_DATA + " BLOB,"
+ + "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", "
+ + CertsColumns.KEY_ID_CERTIFIER + "), "
+ + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES "
+ + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE,"
+ + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ") REFERENCES "
+ + Tables.USER_IDS + "(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + ") ON DELETE CASCADE"
+ + ")";
private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS
+ " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, "
- + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, "
- + ApiAppsColumns.KEY_ID + " INT64, "
- + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
- + ApiAppsColumns.HASH_ALORITHM + " INTEGER, "
- + ApiAppsColumns.COMPRESSION + " INTEGER)";
-
- private static final String CREATE_CERTS = "CREATE TABLE IF NOT EXISTS " + Tables.CERTS
- + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
- + CertsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL "
- + " REFERENCES " + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE, "
- + CertsColumns.KEY_ID + " INTEGER, " // certified key
- + CertsColumns.RANK + " INTEGER, " // key rank of certified uid
- + CertsColumns.KEY_ID_CERTIFIER + " INTEGER, " // certifying key
- + CertsColumns.CREATION + " INTEGER, "
- + CertsColumns.VERIFIED + " INTEGER, "
- + CertsColumns.KEY_DATA + " BLOB)";
+ + ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, "
+ + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)";
+ private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS
+ + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT NOT NULL, "
+ + ApiAppsAccountsColumns.KEY_ID + " INT64, "
+ + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, "
+ + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, "
+ + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, "
+ + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, "
+ + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", "
+ + ApiAppsAccountsColumns.PACKAGE_NAME + "), "
+ + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES "
+ + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)";
KeychainDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
+
+ // make sure this is only done once, on the first instance!
+ boolean iAmIt = false;
+ synchronized(apg_hack) {
+ if(!apg_hack) {
+ iAmIt = true;
+ apg_hack = true;
+ }
+ }
+ // if it's us, do the import
+ if(iAmIt)
+ checkAndImportApg(context);
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.w(Constants.TAG, "Creating database...");
- db.execSQL(CREATE_KEY_RINGS);
+ db.execSQL(CREATE_KEYRINGS_PUBLIC);
+ db.execSQL(CREATE_KEYRINGS_SECRET);
db.execSQL(CREATE_KEYS);
db.execSQL(CREATE_USER_IDS);
- db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_CERTS);
+ db.execSQL(CREATE_API_APPS);
+ db.execSQL(CREATE_API_APPS_ACCOUNTS);
}
@Override
@@ -117,47 +176,100 @@ public class KeychainDatabase extends SQLiteOpenHelper {
if (!db.isReadOnly()) {
// Enable foreign key constraints
db.execSQL("PRAGMA foreign_keys=ON;");
+ // TODO remove, once we remove the "always migrate" debug stuff
+ // db.execSQL("DROP TABLE certs;");
+ // db.execSQL("DROP TABLE user_ids;");
+ db.execSQL(CREATE_USER_IDS);
+ db.execSQL(CREATE_CERTS);
}
}
@Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w(Constants.TAG, "Upgrading database from version " + oldVersion + " to " + newVersion);
-
- // Upgrade from oldVersion through all cases to newest one
- for (int version = oldVersion; version < newVersion; ++version) {
- Log.w(Constants.TAG, "Upgrading database to version " + version);
-
- switch (version) {
- case 3:
- db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.CAN_CERTIFY
- + " INTEGER DEFAULT 0;");
- db.execSQL("UPDATE " + Tables.KEYS + " SET " + KeysColumns.CAN_CERTIFY
- + " = 1 WHERE " + KeysColumns.IS_MASTER_KEY + "= 1;");
- break;
- case 4:
- db.execSQL(CREATE_API_APPS);
- break;
- case 5:
- // new column: package_signature
- db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS);
- db.execSQL(CREATE_API_APPS);
- break;
- case 6:
- // new column: fingerprint
- db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT
- + " BLOB;");
- break;
- case 7:
- // new table: certs
- db.execSQL(CREATE_CERTS);
+ public void onUpgrade(SQLiteDatabase db, int old, int nu) {
+ // don't care (this is version 1)
+ }
+ /** This method tries to import data from a provided database.
+ *
+ * The sole assumptions made on this db are that there is a key_rings table
+ * with a key_ring_data and a type column, the latter of which should be bigger
+ * for secret keys.
+ */
+ public void checkAndImportApg(Context context) {
+
+ boolean hasApgDb = false; {
+ // It's the Java way =(
+ String[] dbs = context.databaseList();
+ for(String db : dbs) {
+ if(db.equals("apg.db")) {
+ hasApgDb = true;
break;
- default:
- break;
+ }
+ }
+ }
+
+ if(!hasApgDb)
+ return;
+ Log.d(Constants.TAG, "apg.db exists! Importing...");
+
+ SQLiteDatabase db = new SQLiteOpenHelper(context, "apg.db", null, 1) {
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ // should never happen
+ assert false;
+ }
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int old, int nu) {
+ // don't care
+ }
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int old, int nu) {
+ // don't care either
}
+ }.getReadableDatabase();
+
+ // kill current!
+ { // TODO don't kill current.
+ Log.d(Constants.TAG, "Truncating db...");
+ SQLiteDatabase d = getWritableDatabase();
+ d.execSQL("DELETE FROM keyrings_public");
+ d.close();
+ Log.d(Constants.TAG, "Ok.");
}
+
+ Cursor c = db.rawQuery("SELECT key_ring_data FROM key_rings ORDER BY type ASC", null);
+ try {
+ // import from old database
+ Log.d(Constants.TAG, "Importing " + c.getCount() + " keyrings from apg.db...");
+ for(int i = 0; i < c.getCount(); i++) {
+ c.moveToPosition(i);
+ byte[] data = c.getBlob(0);
+ PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
+ if(ring instanceof PGPPublicKeyRing)
+ ProviderHelper.saveKeyRing(context, (PGPPublicKeyRing) ring);
+ else if(ring instanceof PGPSecretKeyRing)
+ ProviderHelper.saveKeyRing(context, (PGPSecretKeyRing) ring);
+ else {
+ Log.e(Constants.TAG, "Unknown blob data type!");
+ }
+ }
+
+ } catch(IOException e) {
+ Log.e(Constants.TAG, "Error importing apg db!", e);
+ return;
+ } finally {
+ if(c != null)
+ c.close();
+ if(db != null)
+ db.close();
+ }
+
+ // TODO delete old db, if we are sure this works
+ // context.deleteDatabase("apg.db");
+ Log.d(Constants.TAG, "All done, (not) deleting apg.db");
+
+
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index f34423a71..83b3dd744 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,10 +26,16 @@ import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
-import android.provider.BaseColumns;
import android.text.TextUtils;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.provider.KeychainContract.*;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
+import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log;
@@ -37,54 +43,29 @@ import java.util.Arrays;
import java.util.HashMap;
public class KeychainProvider extends ContentProvider {
- // public static final String ACTION_BROADCAST_DATABASE_CHANGE = Constants.PACKAGE_NAME
- // + ".action.DATABASE_CHANGE";
- //
- // public static final String EXTRA_BROADCAST_KEY_TYPE = "key_type";
- // public static final String EXTRA_BROADCAST_CONTENT_ITEM_TYPE = "contentItemType";
-
- private static final int PUBLIC_KEY_RING = 101;
- private static final int PUBLIC_KEY_RING_BY_ROW_ID = 102;
- private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID = 103;
- private static final int PUBLIC_KEY_RING_BY_KEY_ID = 104;
- private static final int PUBLIC_KEY_RING_BY_EMAILS = 105;
- private static final int PUBLIC_KEY_RING_BY_LIKE_EMAIL = 106;
-
- private static final int PUBLIC_KEY_RING_KEY = 111;
- private static final int PUBLIC_KEY_RING_KEY_BY_ROW_ID = 112;
-
- private static final int PUBLIC_KEY_RING_USER_ID = 121;
- private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122;
- private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID = 123;
-
- private static final int SECRET_KEY_RING = 201;
- private static final int SECRET_KEY_RING_BY_ROW_ID = 202;
- private static final int SECRET_KEY_RING_BY_MASTER_KEY_ID = 203;
- private static final int SECRET_KEY_RING_BY_KEY_ID = 204;
- private static final int SECRET_KEY_RING_BY_EMAILS = 205;
- private static final int SECRET_KEY_RING_BY_LIKE_EMAIL = 206;
-
- private static final int SECRET_KEY_RING_KEY = 211;
- private static final int SECRET_KEY_RING_KEY_BY_ROW_ID = 212;
-
- private static final int SECRET_KEY_RING_USER_ID = 221;
- private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222;
+
+ private static final int KEY_RINGS_UNIFIED = 101;
+ private static final int KEY_RINGS_PUBLIC = 102;
+ private static final int KEY_RINGS_SECRET = 103;
+
+ private static final int KEY_RING_UNIFIED = 200;
+ private static final int KEY_RING_KEYS = 201;
+ private static final int KEY_RING_USER_IDS = 202;
+ private static final int KEY_RING_PUBLIC = 203;
+ private static final int KEY_RING_SECRET = 204;
+ private static final int KEY_RING_CERTS = 205;
private static final int API_APPS = 301;
- private static final int API_APPS_BY_ROW_ID = 302;
private static final int API_APPS_BY_PACKAGE_NAME = 303;
+ private static final int API_ACCOUNTS = 304;
+ private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306;
- private static final int UNIFIED_KEY_RING = 401;
+ private static final int KEY_RINGS_FIND_BY_EMAIL = 400;
+ private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
- private static final int CERTS = 401;
- private static final int CERTS_BY_KEY_ID = 402;
- private static final int CERTS_BY_ROW_ID = 403;
- private static final int CERTS_BY_KEY_ROW_ID = 404;
- private static final int CERTS_BY_KEY_ROW_ID_ALL = 405;
- private static final int CERTS_BY_CERTIFIER_ID = 406;
- private static final int CERTS_BY_KEY_ROW_ID_HAS_SECRET = 407;
+ private static final int CERTS_FIND_BY_CERTIFIER_ID = 501;
- // private static final int DATA_STREAM = 401;
+ // private static final int DATA_STREAM = 501;
protected UriMatcher mUriMatcher;
@@ -98,171 +79,85 @@ public class KeychainProvider extends ContentProvider {
String authority = KeychainContract.CONTENT_AUTHORITY;
/**
- * unified key rings
- *
- * <pre>
- * key_rings
- * </pre>
- */
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS, UNIFIED_KEY_RING);
-
- /**
- * public key rings
+ * list key_rings
*
* <pre>
+ * key_rings/unified
* key_rings/public
- * key_rings/public/#
- * key_rings/public/master_key_id/_
- * key_rings/public/key_id/_
- * key_rings/public/emails/_
- * key_rings/public/like_email/_
* </pre>
*/
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC, PUBLIC_KEY_RING);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#", PUBLIC_KEY_RING_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_MASTER_KEY_ID
- + "/*", PUBLIC_KEY_RING_BY_MASTER_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_KEY_ID + "/*",
- PUBLIC_KEY_RING_BY_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_EMAILS + "/*",
- PUBLIC_KEY_RING_BY_EMAILS);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_EMAILS,
- PUBLIC_KEY_RING_BY_EMAILS); // without emails specified
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/" + KeychainContract.PATH_BY_LIKE_EMAIL + "/*",
- PUBLIC_KEY_RING_BY_LIKE_EMAIL);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ + "/" + KeychainContract.PATH_UNIFIED,
+ KEY_RINGS_UNIFIED);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ + "/" + KeychainContract.PATH_PUBLIC,
+ KEY_RINGS_PUBLIC);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS
+ + "/" + KeychainContract.PATH_SECRET,
+ KEY_RINGS_SECRET);
/**
- * public keys
+ * find by criteria other than master key id
*
- * <pre>
- * key_rings/public/#/keys
- * key_rings/public/#/keys/#
- * </pre>
- */
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_KEYS,
- PUBLIC_KEY_RING_KEY);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_KEYS + "/#",
- PUBLIC_KEY_RING_KEY_BY_ROW_ID);
-
- /**
- * public user ids
+ * key_rings/find/email/_
+ * key_rings/find/subkey/_
*
- * <pre>
- * key_rings/public/#/user_ids
- * key_rings/public/#/user_ids/#
- * key_rings/public/master_key_id/#/user_ids
- * </pre>
*/
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS,
- PUBLIC_KEY_RING_USER_ID);
+ + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_EMAIL + "/*",
+ KEY_RINGS_FIND_BY_EMAIL);
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS + "/#",
- PUBLIC_KEY_RING_USER_ID_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_PUBLIC + "/"
- + KeychainContract.PATH_BY_MASTER_KEY_ID + "/*/" + KeychainContract.PATH_USER_IDS,
- PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID);
+ + KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_SUBKEY + "/*",
+ KEY_RINGS_FIND_BY_SUBKEY);
/**
- * secret key rings
+ * list key_ring specifics
*
* <pre>
- * key_rings/secret
- * key_rings/secret/#
- * key_rings/secret/master_key_id/_
- * key_rings/secret/key_id/_
- * key_rings/secret/emails/_
- * key_rings/secret/like_email/_
+ * key_rings/_/unified
+ * key_rings/_/keys
+ * key_rings/_/user_ids
+ * key_rings/_/public
+ * key_rings/_/secret
* </pre>
*/
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET, SECRET_KEY_RING);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#", SECRET_KEY_RING_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_MASTER_KEY_ID
- + "/*", SECRET_KEY_RING_BY_MASTER_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_KEY_ID + "/*",
- SECRET_KEY_RING_BY_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_EMAILS + "/*",
- SECRET_KEY_RING_BY_EMAILS);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_EMAILS,
- SECRET_KEY_RING_BY_EMAILS); // without emails specified
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/" + KeychainContract.PATH_BY_LIKE_EMAIL + "/*",
- SECRET_KEY_RING_BY_LIKE_EMAIL);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_UNIFIED,
+ KEY_RING_UNIFIED);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_KEYS,
+ KEY_RING_KEYS);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_USER_IDS,
+ KEY_RING_USER_IDS);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_PUBLIC,
+ KEY_RING_PUBLIC);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_SECRET,
+ KEY_RING_SECRET);
+ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
+ + KeychainContract.PATH_CERTS,
+ KEY_RING_CERTS);
/**
- * secret keys
+ * API apps
*
* <pre>
- * key_rings/secret/#/keys
- * key_rings/secret/#/keys/#
- * </pre>
- */
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_KEYS,
- SECRET_KEY_RING_KEY);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_KEYS + "/#",
- SECRET_KEY_RING_KEY_BY_ROW_ID);
-
- /**
- * secret user ids
+ * api_apps
+ * api_apps/_ (package name)
*
- * <pre>
- * key_rings/secret/#/user_ids
- * key_rings/secret/#/user_ids/#
+ * api_apps/_/accounts
+ * api_apps/_/accounts/_ (account name)
* </pre>
*/
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_USER_IDS,
- SECRET_KEY_RING_USER_ID);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
- + KeychainContract.PATH_SECRET + "/#/" + KeychainContract.PATH_USER_IDS + "/#",
- SECRET_KEY_RING_USER_ID_BY_ROW_ID);
-
- /**
- * API apps
- */
matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS);
- matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/"
- + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
- /**
- /**
- * certifications
- *
- * <pre>
- * </pre>
- *
- */
- matcher.addURI(authority, KeychainContract.BASE_CERTS, CERTS);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/#", CERTS_BY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ROW_ID + "/#", CERTS_BY_KEY_ROW_ID);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/all", CERTS_BY_KEY_ROW_ID_ALL);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ROW_ID + "/#/has_secret", CERTS_BY_KEY_ROW_ID_HAS_SECRET);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_KEY_ID + "/#", CERTS_BY_KEY_ID);
- matcher.addURI(authority, KeychainContract.BASE_CERTS + "/"
- + KeychainContract.PATH_BY_CERTIFIER_ID + "/#", CERTS_BY_CERTIFIER_ID);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS);
+ matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/"
+ + KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME);
/**
* data stream
@@ -276,7 +171,7 @@ public class KeychainProvider extends ContentProvider {
return matcher;
}
- private KeychainDatabase mApgDatabase;
+ private KeychainDatabase mKeychainDatabase;
/**
* {@inheritDoc}
@@ -284,10 +179,15 @@ public class KeychainProvider extends ContentProvider {
@Override
public boolean onCreate() {
mUriMatcher = buildUriMatcher();
- mApgDatabase = new KeychainDatabase(getContext());
return true;
}
+ public KeychainDatabase getDb() {
+ if(mKeychainDatabase == null)
+ mKeychainDatabase = new KeychainDatabase(getContext());
+ return mKeychainDatabase;
+ }
+
/**
* {@inheritDoc}
*/
@@ -295,247 +195,44 @@ public class KeychainProvider extends ContentProvider {
public String getType(Uri uri) {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_BY_EMAILS:
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- return KeyRings.CONTENT_TYPE;
-
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_KEY_ID:
+ case KEY_RING_PUBLIC:
return KeyRings.CONTENT_ITEM_TYPE;
- case PUBLIC_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY:
+ case KEY_RING_KEYS:
return Keys.CONTENT_TYPE;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- return Keys.CONTENT_ITEM_TYPE;
-
- case PUBLIC_KEY_RING_USER_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
- case SECRET_KEY_RING_USER_ID:
+ case KEY_RING_USER_IDS:
return UserIds.CONTENT_TYPE;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- return UserIds.CONTENT_ITEM_TYPE;
+ case KEY_RING_SECRET:
+ return KeyRings.CONTENT_ITEM_TYPE;
case API_APPS:
return ApiApps.CONTENT_TYPE;
- case API_APPS_BY_ROW_ID:
case API_APPS_BY_PACKAGE_NAME:
return ApiApps.CONTENT_ITEM_TYPE;
- default:
- throw new UnsupportedOperationException("Unknown uri: " + uri);
- }
- }
+ case API_ACCOUNTS:
+ return ApiAccounts.CONTENT_TYPE;
- /**
- * Returns type of the query (secret/public)
- *
- * @param match
- * @return
- */
- private int getKeyType(int match) {
- int type;
- switch (match) {
- case PUBLIC_KEY_RING:
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- case PUBLIC_KEY_RING_KEY:
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case PUBLIC_KEY_RING_USER_ID:
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- type = KeyTypes.PUBLIC;
- break;
-
- case SECRET_KEY_RING:
- case SECRET_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_KEY_ID:
- case SECRET_KEY_RING_BY_EMAILS:
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- case SECRET_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- type = KeyTypes.SECRET;
- break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ return ApiAccounts.CONTENT_ITEM_TYPE;
default:
- Log.e(Constants.TAG, "Unknown match " + match);
- type = -1;
- break;
- }
-
- return type;
- }
-
- /**
- * Set result of query to specific columns, don't show blob column
- *
- * @return
- */
- private HashMap<String, String> getProjectionMapForKeyRings() {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
-
- projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID);
- projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "."
- + KeyRingsColumns.KEY_RING_DATA);
- projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
- + KeyRingsColumns.MASTER_KEY_ID);
- // TODO: deprecated master key id
- //projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID);
-
- projectionMap.put(KeysColumns.ALGORITHM, Tables.KEYS + "." + KeysColumns.ALGORITHM);
- projectionMap.put(KeysColumns.KEY_SIZE, Tables.KEYS + "." + KeysColumns.KEY_SIZE);
- projectionMap.put(KeysColumns.CREATION, Tables.KEYS + "." + KeysColumns.CREATION);
- projectionMap.put(KeysColumns.EXPIRY, Tables.KEYS + "." + KeysColumns.EXPIRY);
- projectionMap.put(KeysColumns.KEY_RING_ROW_ID, Tables.KEYS + "." + KeysColumns.KEY_RING_ROW_ID);
- projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT);
- projectionMap.put(KeysColumns.IS_REVOKED, Tables.KEYS + "." + KeysColumns.IS_REVOKED);
-
- projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
-
- // type attribute is special: if there is any grouping, choose secret over public type
- projectionMap.put(KeyRingsColumns.TYPE,
- "MAX(" + Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + ") AS " + KeyRingsColumns.TYPE);
-
- return projectionMap;
- }
-
- /**
- * Set result of query to specific columns, don't show blob column
- *
- * @return
- */
- private HashMap<String, String> getProjectionMapForKeys() {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
-
- projectionMap.put(BaseColumns._ID, BaseColumns._ID);
- projectionMap.put(KeysColumns.KEY_ID, KeysColumns.KEY_ID);
- projectionMap.put(KeysColumns.IS_MASTER_KEY, KeysColumns.IS_MASTER_KEY);
- projectionMap.put(KeysColumns.ALGORITHM, KeysColumns.ALGORITHM);
- projectionMap.put(KeysColumns.KEY_SIZE, KeysColumns.KEY_SIZE);
- projectionMap.put(KeysColumns.CAN_CERTIFY, KeysColumns.CAN_CERTIFY);
- projectionMap.put(KeysColumns.CAN_SIGN, KeysColumns.CAN_SIGN);
- projectionMap.put(KeysColumns.CAN_ENCRYPT, KeysColumns.CAN_ENCRYPT);
- projectionMap.put(KeysColumns.IS_REVOKED, KeysColumns.IS_REVOKED);
- projectionMap.put(KeysColumns.CREATION, KeysColumns.CREATION);
- projectionMap.put(KeysColumns.EXPIRY, KeysColumns.EXPIRY);
- projectionMap.put(KeysColumns.KEY_RING_ROW_ID, KeysColumns.KEY_RING_ROW_ID);
- projectionMap.put(KeysColumns.KEY_DATA, KeysColumns.KEY_DATA);
- projectionMap.put(KeysColumns.RANK, KeysColumns.RANK);
- projectionMap.put(KeysColumns.FINGERPRINT, KeysColumns.FINGERPRINT);
-
- return projectionMap;
- }
-
- private HashMap<String, String> getProjectionMapForUserIds() {
- HashMap<String, String> projectionMap = new HashMap<String, String>();
-
- projectionMap.put(BaseColumns._ID, Tables.USER_IDS + "." + BaseColumns._ID);
- projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID);
- projectionMap.put(UserIdsColumns.RANK, Tables.USER_IDS + "." + UserIdsColumns.RANK);
- projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "."
- + KeyRingsColumns.MASTER_KEY_ID);
-
- // this is the count of known secret keys who certified this uid
- projectionMap.put("verified", "COUNT(" + Tables.KEYS + "." + Keys._ID + ") AS verified");
-
- return projectionMap;
- }
- private HashMap<String, String> getProjectionMapForCerts() {
-
- HashMap<String, String> pmap = new HashMap<String, String>();
- pmap.put(Certs._ID, Tables.CERTS + "." + Certs._ID);
- pmap.put(Certs.KEY_ID, Tables.CERTS + "." + Certs.KEY_ID);
- pmap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK);
- pmap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
- pmap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
- pmap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA);
- pmap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
- // verified key data
- pmap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
- // verifying key data
- pmap.put("signer_uid", "signer." + UserIds.USER_ID + " AS signer_uid");
-
- return pmap;
- }
-
- /**
- * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
- */
- private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) {
- if (match != UNIFIED_KEY_RING) {
- // public or secret keyring
- qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
}
-
- // join keyrings with keys and userIds
- // Only get user id and key with rank 0 (main user id and main key)
- qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "("
- + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "."
- + KeysColumns.KEY_RING_ROW_ID + " AND " + Tables.KEYS + "."
- + KeysColumns.RANK + " = '0') " + " INNER JOIN " + Tables.USER_IDS + " ON "
- + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
- + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "."
- + UserIdsColumns.RANK + " = '0')");
-
- qb.setProjectionMap(getProjectionMapForKeyRings());
-
- return qb;
- }
-
- /**
- * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys
- * <p/>
- * Here only one key should be selected in the query to return a single keyring!
- */
- private SQLiteQueryBuilder buildKeyRingQueryWithSpecificKey(SQLiteQueryBuilder qb, int match) {
- // public or secret keyring
- qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
-
- // join keyrings with keys and userIds to every keyring
- qb.setTables(Tables.KEY_RINGS + " INNER JOIN " + Tables.KEYS + " ON " + "("
- + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.KEYS + "."
- + KeysColumns.KEY_RING_ROW_ID + ") " + " INNER JOIN " + Tables.USER_IDS + " ON "
- + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "."
- + UserIdsColumns.KEY_RING_ROW_ID + " AND " + Tables.USER_IDS + "."
- + UserIdsColumns.RANK + " = '0')");
-
- qb.setProjectionMap(getProjectionMapForKeyRings());
-
- return qb;
}
/**
* {@inheritDoc}
*/
- @SuppressWarnings("deprecation")
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
- SQLiteDatabase db = mApgDatabase.getReadableDatabase();
int match = mUriMatcher.match(uri);
@@ -543,227 +240,224 @@ public class KeychainProvider extends ContentProvider {
String groupBy = null, having = null;
switch (match) {
- case UNIFIED_KEY_RING:
- qb = buildKeyRingQuery(qb, match);
-
- // GROUP BY so we don't get duplicates
- groupBy = Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID;
-
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = KeyRings.TYPE + " DESC, " +
- Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ case KEY_RING_UNIFIED:
+ case KEY_RINGS_UNIFIED:
+ case KEY_RINGS_FIND_BY_EMAIL:
+ case KEY_RINGS_FIND_BY_SUBKEY: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
+ projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
+ projectionMap.put(KeyRings.KEY_ID, Keys.KEY_ID);
+ projectionMap.put(KeyRings.KEY_SIZE, Keys.KEY_SIZE);
+ projectionMap.put(KeyRings.IS_REVOKED, Keys.IS_REVOKED);
+ projectionMap.put(KeyRings.CAN_CERTIFY, Keys.CAN_CERTIFY);
+ projectionMap.put(KeyRings.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
+ projectionMap.put(KeyRings.CAN_SIGN, Keys.CAN_SIGN);
+ projectionMap.put(KeyRings.CREATION, Keys.CREATION);
+ projectionMap.put(KeyRings.EXPIRY, Keys.EXPIRY);
+ projectionMap.put(KeyRings.ALGORITHM, Keys.ALGORITHM);
+ projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT);
+ projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
+ projectionMap.put(KeyRings.HAS_SECRET, "(" + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NOT NULL) AS " + KeyRings.HAS_SECRET);
+ qb.setProjectionMap(projectionMap);
+
+ qb.setTables(
+ Tables.KEYS
+ + " INNER JOIN " + Tables.USER_IDS + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = 0"
+ + ") LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " = "
+ + Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID
+ + ")"
+ );
+ qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
+
+ switch(match) {
+ case KEY_RING_UNIFIED: {
+ qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ break;
+ }
+ case KEY_RINGS_FIND_BY_SUBKEY: {
+ try {
+ String subkey = Long.valueOf(uri.getLastPathSegment()).toString();
+ qb.appendWhere(" AND EXISTS ("
+ + " SELECT 1 FROM " + Tables.KEYS + " AS tmp"
+ + " WHERE tmp." + UserIds.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND tmp." + Keys.KEY_ID + " = " + subkey + ""
+ + ")");
+ } catch(NumberFormatException e) {
+ Log.e(Constants.TAG, "Malformed find by subkey query!", e);
+ qb.appendWhere(" AND 0");
+ }
+ break;
+ }
+ case KEY_RINGS_FIND_BY_EMAIL: {
+ String chunks[] = uri.getLastPathSegment().split(" *, *");
+ boolean gotCondition = false;
+ String emailWhere = "";
+ // JAVA ♥
+ for (int i = 0; i < chunks.length; ++i) {
+ if (chunks[i].length() == 0) {
+ continue;
+ }
+ if (i != 0) {
+ emailWhere += " OR ";
+ }
+ emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
+ // match '*<email>', so it has to be at the *end* of the user id
+ emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
+ gotCondition = true;
+ }
+ if(gotCondition) {
+ qb.appendWhere(" AND EXISTS ("
+ + " SELECT 1 FROM " + Tables.USER_IDS + " AS tmp"
+ + " WHERE tmp." + UserIds.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND (" + emailWhere + ")"
+ + ")");
+ } else {
+ // TODO better way to do this?
+ Log.e(Constants.TAG, "Malformed find by email query!");
+ qb.appendWhere(" AND 0");
+ }
+ break;
+ }
}
- break;
-
- case PUBLIC_KEY_RING:
- case SECRET_KEY_RING:
- qb = buildKeyRingQuery(qb, match);
-
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ sortOrder =
+ Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NULL ASC, "
+ + Tables.USER_IDS + "." + UserIds.USER_ID + " ASC";
}
- break;
-
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- qb = buildKeyRingQuery(qb, match);
-
- qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
-
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
+ // uri to watch is all /key_rings/
+ uri = KeyRings.CONTENT_URI;
break;
+ }
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- qb = buildKeyRingQuery(qb, match);
-
- qb.appendWhere(" AND " + Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ case KEY_RING_KEYS: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(Keys._ID, Tables.KEYS + ".oid AS _id");
+ projectionMap.put(Keys.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
+ projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK);
+ projectionMap.put(Keys.KEY_ID, Keys.KEY_ID);
+ projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
+ projectionMap.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
+ projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY);
+ projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
+ projectionMap.put(Keys.CAN_SIGN, Keys.CAN_SIGN);
+ projectionMap.put(Keys.CREATION, Keys.CREATION);
+ projectionMap.put(Keys.EXPIRY, Keys.EXPIRY);
+ projectionMap.put(Keys.ALGORITHM, Keys.ALGORITHM);
+ projectionMap.put(Keys.FINGERPRINT, Keys.FINGERPRINT);
+ qb.setProjectionMap(projectionMap);
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
- }
+ qb.setTables(Tables.KEYS);
+ qb.appendWhere(Keys.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
+ }
- case SECRET_KEY_RING_BY_KEY_ID:
- case PUBLIC_KEY_RING_BY_KEY_ID:
- qb = buildKeyRingQueryWithSpecificKey(qb, match);
+ case KEY_RING_USER_IDS: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id");
+ projectionMap.put(UserIds.MASTER_KEY_ID, UserIds.MASTER_KEY_ID);
+ projectionMap.put(UserIds.USER_ID, UserIds.USER_ID);
+ projectionMap.put(UserIds.RANK, UserIds.RANK);
+ projectionMap.put(UserIds.IS_PRIMARY, UserIds.IS_PRIMARY);
+ projectionMap.put(UserIds.VERIFIED, "0 AS " + UserIds.VERIFIED);
+ qb.setProjectionMap(projectionMap);
- qb.appendWhere(" AND " + Tables.KEYS + "." + KeysColumns.KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
+ qb.setTables(Tables.USER_IDS);
+ qb.appendWhere(UserIds.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC";
+ sortOrder = UserIds.RANK + " ASC";
}
break;
- case SECRET_KEY_RING_BY_EMAILS:
- case PUBLIC_KEY_RING_BY_EMAILS:
- qb = buildKeyRingQuery(qb, match);
-
- String emails = uri.getLastPathSegment();
- String chunks[] = emails.split(" *, *");
- boolean gotCondition = false;
- String emailWhere = "";
- for (int i = 0; i < chunks.length; ++i) {
- if (chunks[i].length() == 0) {
- continue;
- }
- if (i != 0) {
- emailWhere += " OR ";
- }
- emailWhere += "tmp." + UserIdsColumns.USER_ID + " LIKE ";
- // match '*<email>', so it has to be at the *end* of the user id
- emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
- gotCondition = true;
- }
-
- if (gotCondition) {
- qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
- + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
- + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + emailWhere
- + "))");
- }
-
- break;
-
- case SECRET_KEY_RING_BY_LIKE_EMAIL:
- case PUBLIC_KEY_RING_BY_LIKE_EMAIL:
- qb = buildKeyRingQuery(qb, match);
-
- String likeEmail = uri.getLastPathSegment();
-
- String likeEmailWhere = "tmp." + UserIdsColumns.USER_ID + " LIKE "
- + DatabaseUtils.sqlEscapeString("%<%" + likeEmail + "%>");
-
- qb.appendWhere(" AND EXISTS (SELECT tmp." + BaseColumns._ID + " FROM "
- + Tables.USER_IDS + " AS tmp WHERE tmp." + UserIdsColumns.KEY_RING_ROW_ID
- + " = " + Tables.KEY_RINGS + "." + BaseColumns._ID + " AND (" + likeEmailWhere
- + "))");
-
- break;
+ }
- case PUBLIC_KEY_RING_KEY:
- case SECRET_KEY_RING_KEY:
- qb.setTables(Tables.KEYS);
- qb.appendWhere(KeysColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
+ case KEY_RINGS_PUBLIC:
+ case KEY_RING_PUBLIC: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_PUBLIC + ".oid AS _id");
+ projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID);
+ projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA);
+ qb.setProjectionMap(projectionMap);
- qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ qb.setTables(Tables.KEY_RINGS_PUBLIC);
- qb.setProjectionMap(getProjectionMapForKeys());
+ if(match == KEY_RING_PUBLIC) {
+ qb.appendWhere(KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+ }
break;
+ }
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- qb.setTables(Tables.KEYS);
- qb.appendWhere(KeysColumns.TYPE + " = ");
- qb.appendWhereEscapeString(Integer.toString(getKeyType(match)));
-
- qb.appendWhere(" AND " + KeysColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
-
- qb.appendWhere(" AND " + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
-
- qb.setProjectionMap(getProjectionMapForKeys());
+ case KEY_RINGS_SECRET:
+ case KEY_RING_SECRET: {
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(KeyRingData._ID, Tables.KEY_RINGS_SECRET + ".oid AS _id");
+ projectionMap.put(KeyRingData.MASTER_KEY_ID, KeyRingData.MASTER_KEY_ID);
+ projectionMap.put(KeyRingData.KEY_RING_DATA, KeyRingData.KEY_RING_DATA);
+ qb.setProjectionMap(projectionMap);
- break;
+ qb.setTables(Tables.KEY_RINGS_SECRET);
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID:
- case PUBLIC_KEY_RING_USER_ID:
- case SECRET_KEY_RING_USER_ID:
- qb.setTables(Tables.USER_IDS
- + " INNER JOIN " + Tables.KEY_RINGS + " ON ("
- + Tables.KEY_RINGS + "." + BaseColumns._ID + " = "
- + Tables.USER_IDS + "." + KeysColumns.KEY_RING_ROW_ID
- + ") LEFT JOIN " + Tables.CERTS + " ON ("
- + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID + " = "
- + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID
- + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = "
- + Tables.CERTS + "." + Certs.RANK
- + ") LEFT JOIN " + Tables.KEYS + " ON ("
- + Tables.KEYS + "." + Keys.KEY_ID + " = "
- + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER
- // might introduce a "trusted" flag later? for now, we simply assume
- // every private key's signature is good.
- + " AND " + Tables.KEYS + "." + Keys.TYPE
- + " == " + KeyTypes.SECRET
- + ")");
-
- groupBy = Tables.USER_IDS + "." + UserIds.RANK;
-
- qb.setProjectionMap(getProjectionMapForUserIds());
-
- if(match == PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID) {
- qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(3));
- } else {
- qb.appendWhere(Tables.USER_IDS + "." + UserIdsColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ if(match == KEY_RING_SECRET) {
+ qb.appendWhere(KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
}
break;
+ }
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- qb.setTables(Tables.USER_IDS);
- qb.appendWhere(UserIdsColumns.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
-
- qb.appendWhere(" AND " + BaseColumns._ID + " = ");
- qb.appendWhereEscapeString(uri.getLastPathSegment());
-
- break;
+ case KEY_RING_CERTS:
+ HashMap<String, String> projectionMap = new HashMap<String, String>();
+ projectionMap.put(Certs._ID, Tables.CERTS + ".oid AS " + Certs._ID);
+ projectionMap.put(Certs.MASTER_KEY_ID, Tables.CERTS + "." + Certs.MASTER_KEY_ID);
+ projectionMap.put(Certs.RANK, Tables.CERTS + "." + Certs.RANK);
+ projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
+ projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
+ projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
+ projectionMap.put(Certs.KEY_DATA, Tables.CERTS + "." + Certs.KEY_DATA);
+ // verified key data
+ projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
+ // verifying key data
+ projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID);
+ qb.setProjectionMap(projectionMap);
- case CERTS_BY_ROW_ID:
- case CERTS_BY_KEY_ROW_ID_ALL:
- case CERTS_BY_KEY_ROW_ID_HAS_SECRET:
- case CERTS_BY_KEY_ROW_ID:
qb.setTables(Tables.CERTS
+ " JOIN " + Tables.USER_IDS + " ON ("
- + Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = "
- + Tables.USER_IDS + "." + UserIds.KEY_RING_ROW_ID
+ + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = "
+ + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID
+ " AND "
+ Tables.CERTS + "." + Certs.RANK + " = "
+ Tables.USER_IDS + "." + UserIds.RANK
- // noooooooot sure about this~ database design
- + ")" + (match == CERTS_BY_KEY_ROW_ID_ALL ? " LEFT" : "")
- + " JOIN " + Tables.KEYS + " ON ("
- + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = "
- + Tables.KEYS + "." + Keys.KEY_ID
- + (match == CERTS_BY_KEY_ROW_ID_HAS_SECRET ?
- " AND " + Tables.KEYS + "." + Keys.TYPE + " = " + KeyTypes.SECRET : ""
- )
+ ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON ("
- + Tables.KEYS + "." + Keys.KEY_RING_ROW_ID + " = "
- + "signer." + UserIds.KEY_RING_ROW_ID
+ + Tables.CERTS + "." + Keys.MASTER_KEY_ID + " = "
+ + "signer." + UserIds.MASTER_KEY_ID
+ " AND "
+ "signer." + Keys.RANK + " = 0"
+ ")");
- qb.setProjectionMap(getProjectionMapForCerts());
-
groupBy = Tables.CERTS + "." + Certs.RANK + ", "
+ Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER;
- if(match == CERTS_BY_ROW_ID) {
- qb.appendWhere(Tables.CERTS + "." + Certs._ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(1));
- } else {
- qb.appendWhere(Tables.CERTS + "." + Certs.KEY_RING_ROW_ID + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
- }
+ qb.appendWhere(Tables.CERTS + "." + KeyRings.MASTER_KEY_ID + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
@@ -771,22 +465,29 @@ public class KeychainProvider extends ContentProvider {
qb.setTables(Tables.API_APPS);
break;
- case API_APPS_BY_ROW_ID:
+ case API_APPS_BY_PACKAGE_NAME:
qb.setTables(Tables.API_APPS);
-
- qb.appendWhere(BaseColumns._ID + " = ");
+ qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(uri.getLastPathSegment());
break;
- case API_APPS_BY_PACKAGE_NAME:
- qb.setTables(Tables.API_APPS);
- qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
- qb.appendWhereEscapeString(uri.getPathSegments().get(2));
+ case API_ACCOUNTS:
+ qb.setTables(Tables.API_ACCOUNTS);
+ qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ qb.setTables(Tables.API_ACCOUNTS);
+ qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getPathSegments().get(1));
+
+ qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = ");
+ qb.appendWhereEscapeString(uri.getLastPathSegment());
+ break;
default:
- throw new IllegalArgumentException("Unknown URI " + uri);
+ throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
}
@@ -798,6 +499,7 @@ public class KeychainProvider extends ContentProvider {
orderBy = sortOrder;
}
+ SQLiteDatabase db = getDb().getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy);
// Tell the cursor what uri to watch, so it knows when its source data changes
@@ -821,78 +523,72 @@ public class KeychainProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = getDb().getWritableDatabase();
Uri rowUri = null;
- long rowId = -1;
+ Long keyId = null;
try {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING:
- values.put(KeyRings.TYPE, KeyTypes.PUBLIC);
-
- rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
- rowUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
+ case KEY_RING_PUBLIC:
+ db.insertOrThrow(Tables.KEY_RINGS_PUBLIC, null, values);
+ keyId = values.getAsLong(KeyRings.MASTER_KEY_ID);
break;
- case PUBLIC_KEY_RING_KEY:
- values.put(Keys.TYPE, KeyTypes.PUBLIC);
- rowId = db.insertOrThrow(Tables.KEYS, null, values);
- rowUri = Keys.buildPublicKeysUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case KEY_RING_SECRET:
+ db.insertOrThrow(Tables.KEY_RINGS_SECRET, null, values);
+ keyId = values.getAsLong(KeyRings.MASTER_KEY_ID);
+ break;
+ case KEY_RING_KEYS:
+ Log.d(Constants.TAG, "keys");
+ db.insertOrThrow(Tables.KEYS, null, values);
+ keyId = values.getAsLong(Keys.MASTER_KEY_ID);
break;
- case PUBLIC_KEY_RING_USER_ID:
- rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
- rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case KEY_RING_USER_IDS:
+ db.insertOrThrow(Tables.USER_IDS, null, values);
+ keyId = values.getAsLong(UserIds.MASTER_KEY_ID);
break;
- case SECRET_KEY_RING:
- values.put(KeyRings.TYPE, KeyTypes.SECRET);
- rowId = db.insertOrThrow(Tables.KEY_RINGS, null, values);
- rowUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case KEY_RING_CERTS:
+ db.insertOrThrow(Tables.CERTS, null, values);
+ keyId = values.getAsLong(Certs.MASTER_KEY_ID);
+ break;
+ case API_APPS:
+ db.insertOrThrow(Tables.API_APPS, null, values);
break;
- case SECRET_KEY_RING_KEY:
- values.put(Keys.TYPE, KeyTypes.SECRET);
- rowId = db.insertOrThrow(Tables.KEYS, null, values);
- rowUri = Keys.buildSecretKeysUri(Long.toString(rowId));
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ case API_ACCOUNTS:
+ // set foreign key automatically based on given uri
+ // e.g., api_apps/com.example.app/accounts/
+ String packageName = uri.getPathSegments().get(1);
+ values.put(ApiAccounts.PACKAGE_NAME, packageName);
- break;
- case SECRET_KEY_RING_USER_ID:
- rowId = db.insertOrThrow(Tables.USER_IDS, null, values);
- rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId));
+ Log.d(Constants.TAG, "provider packageName: " + packageName);
- break;
- case API_APPS:
- rowId = db.insertOrThrow(Tables.API_APPS, null, values);
- rowUri = ApiApps.buildIdUri(Long.toString(rowId));
+ db.insertOrThrow(Tables.API_ACCOUNTS, null, values);
+ // TODO: this is wrong:
+// rowUri = ApiAccounts.buildIdUri(Long.toString(rowId));
break;
- case CERTS_BY_KEY_ROW_ID:
- rowId = db.insertOrThrow(Tables.CERTS, null, values);
- // kinda useless.. should this be buildCertsByKeyRowIdUri?
- // rowUri = Certs.buildCertsUri(Long.toString(rowId));
- rowUri = uri;
- break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
}
+ if(keyId != null) {
+ uri = KeyRings.buildGenericKeyRingUri(keyId.toString());
+ rowUri = uri;
+ }
+
// notify of changes in db
getContext().getContentResolver().notifyChange(uri, null);
} catch (SQLiteConstraintException e) {
- Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?");
+ Log.e(Constants.TAG, "Constraint exception on insert! Entry already existing?", e);
}
return rowUri;
@@ -902,51 +598,43 @@ public class KeychainProvider extends ContentProvider {
* {@inheritDoc}
*/
@Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
+ public int delete(Uri uri, String additionalSelection, String[] selectionArgs) {
Log.v(Constants.TAG, "delete(uri=" + uri + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = getDb().getWritableDatabase();
int count;
final int match = mUriMatcher.match(uri);
- String defaultSelection = null;
switch (match) {
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
- // corresponding keys and userIds are deleted by ON DELETE CASCADE
- count = db.delete(Tables.KEY_RINGS,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
- break;
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
+ case KEY_RING_PUBLIC: {
+ @SuppressWarnings("ConstantConditions") // ensured by uriMatcher above
+ String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1);
+ if (!TextUtils.isEmpty(additionalSelection)) {
+ selection += " AND (" + additionalSelection + ")";
+ }
// corresponding keys and userIds are deleted by ON DELETE CASCADE
- count = db.delete(Tables.KEY_RINGS,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
- break;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- count = db.delete(Tables.KEYS,
- buildDefaultKeysSelection(uri, getKeyType(match), selection), selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
+ count = db.delete(Tables.KEY_RINGS_PUBLIC, selection, selectionArgs);
+ uri = KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1));
break;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection),
- selectionArgs);
+ }
+ case KEY_RING_SECRET: {
+ @SuppressWarnings("ConstantConditions") // ensured by uriMatcher above
+ String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1);
+ if (!TextUtils.isEmpty(additionalSelection)) {
+ selection += " AND (" + additionalSelection + ")";
+ }
+ count = db.delete(Tables.KEY_RINGS_SECRET, selection, selectionArgs);
+ uri = KeyRings.buildGenericKeyRingUri(uri.getPathSegments().get(1));
break;
- case API_APPS_BY_ROW_ID:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection),
+ }
+
+ case API_APPS_BY_PACKAGE_NAME:
+ count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, additionalSelection),
selectionArgs);
break;
- case API_APPS_BY_PACKAGE_NAME:
- count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection),
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection),
selectionArgs);
break;
default:
@@ -966,58 +654,20 @@ public class KeychainProvider extends ContentProvider {
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")");
- final SQLiteDatabase db = mApgDatabase.getWritableDatabase();
+ final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase();
String defaultSelection = null;
int count = 0;
try {
final int match = mUriMatcher.match(uri);
switch (match) {
- case PUBLIC_KEY_RING_BY_ROW_ID:
- case SECRET_KEY_RING_BY_ROW_ID:
- defaultSelection = BaseColumns._ID + "=" + uri.getLastPathSegment();
-
- count = db.update(
- Tables.KEY_RINGS,
- values,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
- selection), selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_BY_MASTER_KEY_ID:
- case SECRET_KEY_RING_BY_MASTER_KEY_ID:
- defaultSelection = KeyRings.MASTER_KEY_ID + "=" + uri.getLastPathSegment();
-
- count = db.update(
- Tables.KEY_RINGS,
- values,
- buildDefaultKeyRingsSelection(defaultSelection, getKeyType(match),
- selection), selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_KEY_BY_ROW_ID:
- case SECRET_KEY_RING_KEY_BY_ROW_ID:
- count = db
- .update(Tables.KEYS, values,
- buildDefaultKeysSelection(uri, getKeyType(match), selection),
- selectionArgs);
- sendBroadcastDatabaseChange(getKeyType(match), getType(uri));
-
- break;
- case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID:
- case SECRET_KEY_RING_USER_ID_BY_ROW_ID:
- count = db.update(Tables.USER_IDS, values,
- buildDefaultUserIdsSelection(uri, selection), selectionArgs);
- break;
- case API_APPS_BY_ROW_ID:
- count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, false, selection), selectionArgs);
- break;
case API_APPS_BY_PACKAGE_NAME:
count = db.update(Tables.API_APPS, values,
- buildDefaultApiAppsSelection(uri, true, selection), selectionArgs);
+ buildDefaultApiAppsSelection(uri, selection), selectionArgs);
+ break;
+ case API_ACCOUNTS_BY_ACCOUNT_NAME:
+ count = db.update(Tables.API_ACCOUNTS, values,
+ buildDefaultApiAccountsSelection(uri, selection), selectionArgs);
break;
default:
throw new UnsupportedOperationException("Unknown uri: " + uri);
@@ -1034,125 +684,36 @@ public class KeychainProvider extends ContentProvider {
}
/**
- * Build default selection statement for KeyRings. If no extra selection is specified only build
- * where clause with rowId
- *
- * @param defaultSelection
- * @param keyType
- * @param selection
- * @return
- */
- private String buildDefaultKeyRingsSelection(String defaultSelection, Integer keyType,
- String selection) {
- String andType = "";
- if (keyType != null) {
- andType = " AND " + KeyRingsColumns.TYPE + "=" + keyType;
- }
-
- String andSelection = "";
- if (!TextUtils.isEmpty(selection)) {
- andSelection = " AND (" + selection + ")";
- }
-
- return defaultSelection + andType + andSelection;
- }
-
- /**
- * Build default selection statement for Keys. If no extra selection is specified only build
- * where clause with rowId
- *
- * @param uri
- * @param selection
- * @return
- */
- private String buildDefaultKeysSelection(Uri uri, Integer keyType, String selection) {
- String rowId = uri.getLastPathSegment();
-
- String foreignKeyRingRowId = uri.getPathSegments().get(2);
- String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = "
- + foreignKeyRingRowId;
-
- String andType = "";
- if (keyType != null) {
- andType = " AND " + KeysColumns.TYPE + "=" + keyType;
- }
-
- String andSelection = "";
- if (!TextUtils.isEmpty(selection)) {
- andSelection = " AND (" + selection + ")";
- }
-
- return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andType + andSelection;
- }
-
- /**
- * Build default selection statement for UserIds. If no extra selection is specified only build
+ * Build default selection statement for API apps. If no extra selection is specified only build
* where clause with rowId
*
* @param uri
* @param selection
* @return
*/
- private String buildDefaultUserIdsSelection(Uri uri, String selection) {
- String rowId = uri.getLastPathSegment();
-
- String foreignKeyRingRowId = uri.getPathSegments().get(2);
- String andForeignKeyRing = " AND " + KeysColumns.KEY_RING_ROW_ID + " = "
- + foreignKeyRingRowId;
+ private String buildDefaultApiAppsSelection(Uri uri, String selection) {
+ String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
String andSelection = "";
if (!TextUtils.isEmpty(selection)) {
andSelection = " AND (" + selection + ")";
}
- return BaseColumns._ID + "=" + rowId + andForeignKeyRing + andSelection;
+ return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection;
}
- /**
- * Build default selection statement for API apps. If no extra selection is specified only build
- * where clause with rowId
- *
- * @param uri
- * @param selection
- * @return
- */
- private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) {
- String lastPathSegment = uri.getLastPathSegment();
+ private String buildDefaultApiAccountsSelection(Uri uri, String selection) {
+ String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1));
+ String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment());
String andSelection = "";
if (!TextUtils.isEmpty(selection)) {
andSelection = " AND (" + selection + ")";
}
- if (packageSelection) {
- return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection;
- } else {
- return BaseColumns._ID + "=" + lastPathSegment + andSelection;
- }
+ return ApiAccounts.PACKAGE_NAME + "=" + packageName + " AND "
+ + ApiAccounts.ACCOUNT_NAME + "=" + accountName
+ + andSelection;
}
- // @Override
- // public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- // int match = mUriMatcher.match(uri);
- // if (match != DATA_STREAM) {
- // throw new FileNotFoundException();
- // }
- // String fileName = uri.getLastPathSegment();
- // File file = new File(getContext().getFilesDir().getAbsolutePath(), fileName);
- // return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
- // }
-
- /**
- * This broadcast is send system wide to inform other application that a keyring was inserted,
- * updated, or deleted
- */
- private void sendBroadcastDatabaseChange(int keyType, String contentItemType) {
- // TODO: Disabled, old API
- // Intent intent = new Intent();
- // intent.setAction(ACTION_BROADCAST_DATABASE_CHANGE);
- // intent.putExtra(EXTRA_BROADCAST_KEY_TYPE, keyType);
- // intent.putExtra(EXTRA_BROADCAST_CONTENT_ITEM_TYPE, contentItemType);
- //
- // getContext().sendBroadcast(intent, Constants.PERMISSION_ACCESS_API);
- }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
index da1bcb2d9..bc7de0b37 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java
@@ -25,7 +25,7 @@ import android.provider.BaseColumns;
import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns;
public class KeychainServiceBlobDatabase extends SQLiteOpenHelper {
- private static final String DATABASE_NAME = "apg_blob.db";
+ private static final String DATABASE_NAME = "openkeychain_blob.db";
private static final int DATABASE_VERSION = 2;
public static final String TABLE = "data";
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
index 6ac61e157..aa30e845d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java
@@ -38,7 +38,7 @@ import java.util.List;
import java.util.UUID;
public class KeychainServiceBlobProvider extends ContentProvider {
- private static final String STORE_PATH = Constants.Path.APP_DIR + "/ApgBlobs";
+ private static final String STORE_PATH = Constants.Path.APP_DIR + "/KeychainBlobs";
private KeychainServiceBlobDatabase mBlobDatabase = null;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index 1d249c67e..503fed3c9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -19,27 +19,36 @@ package org.sufficientlysecure.keychain.provider;
import java.security.SignatureException;
import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.bcpg.UserAttributePacket;
-import org.spongycastle.bcpg.UserAttributeSubpacket;
-import android.content.*;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.net.Uri;
import android.os.RemoteException;
-import org.spongycastle.bcpg.ArmoredOutputStream;
-import org.spongycastle.openpgp.*;
+
+import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPSignature;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
-import org.sufficientlysecure.keychain.service.remote.AppSettings;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -49,27 +58,82 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
-
+import java.util.HashSet;
+import java.util.Set;
public class ProviderHelper {
- /**
- * Private helper method to get PGPKeyRing from database
- */
+ // If we ever switch to api level 11, we can ditch this whole mess!
+ public static final int FIELD_TYPE_NULL = 1;
+ // this is called integer to stay coherent with the constants in Cursor (api level 11)
+ public static final int FIELD_TYPE_INTEGER = 2;
+ public static final int FIELD_TYPE_FLOAT = 3;
+ public static final int FIELD_TYPE_STRING = 4;
+ public static final int FIELD_TYPE_BLOB = 5;
+
+ public static Object getGenericData(Context context, Uri uri, String column, int type) {
+ return getGenericData(context, uri, new String[] { column }, new int[] { type }).get(column);
+ }
+ public static HashMap<String,Object> getGenericData(Context context, Uri uri, String[] proj, int[] types) {
+ Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null);
+
+ HashMap<String, Object> result = new HashMap<String, Object>(proj.length);
+ if (cursor != null && cursor.moveToFirst()) {
+ int pos = 0;
+ for(String p : proj) {
+ switch(types[pos]) {
+ case FIELD_TYPE_NULL: result.put(p, cursor.isNull(pos)); break;
+ case FIELD_TYPE_INTEGER: result.put(p, cursor.getLong(pos)); break;
+ case FIELD_TYPE_FLOAT: result.put(p, cursor.getFloat(pos)); break;
+ case FIELD_TYPE_STRING: result.put(p, cursor.getString(pos)); break;
+ case FIELD_TYPE_BLOB: result.put(p, cursor.getBlob(pos)); break;
+ }
+ pos += 1;
+ }
+ }
+
+ if (cursor != null) {
+ cursor.close();
+ }
+
+ return result;
+ }
+
+ public static Object getUnifiedData(Context context, long masterKeyId, String column, int type) {
+ return getUnifiedData(context, masterKeyId, new String[] { column }, new int[] { type }).get(column);
+ }
+ public static HashMap<String,Object> getUnifiedData(Context context, long masterKeyId, String[] proj, int[] types) {
+ return getGenericData(context, KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)), proj, types);
+ }
+
+ public static long getMasterKeyId(Context context, Uri queryUri) {
+ // try extracting from the uri first
+ String firstSegment = queryUri.getPathSegments().get(1);
+ if(!firstSegment.equals("find")) try {
+ return Long.parseLong(firstSegment);
+ } catch(NumberFormatException e) {
+ // didn't work? oh well.
+ Log.d(Constants.TAG, "Couldn't get masterKeyId from URI, querying...");
+ }
+ Object data = getGenericData(context, queryUri, KeyRings.MASTER_KEY_ID, FIELD_TYPE_INTEGER);
+ if(data != null)
+ return (Long) data;
+ // TODO better error handling?
+ return 0L;
+ }
+
public static Map<Long, PGPKeyRing> getPGPKeyRings(Context context, Uri queryUri) {
Cursor cursor = context.getContentResolver().query(queryUri,
- new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA}, null, null, null);
+ new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA },
+ null, null, null);
Map<Long, PGPKeyRing> result = new HashMap<Long, PGPKeyRing>(cursor.getCount());
if (cursor != null && cursor.moveToFirst()) do {
- int keyRingDataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA);
- int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
-
- byte[] data = cursor.getBlob(keyRingDataCol);
+ long masterKeyId = cursor.getLong(0);
+ byte[] data = cursor.getBlob(1);
if (data != null) {
- result.put(cursor.getLong(masterKeyIdCol), PgpConversionHelper.BytesToPGPKeyRing(data));
+ result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
}
-
} while(cursor.moveToNext());
if (cursor != null) {
@@ -79,82 +143,46 @@ public class ProviderHelper {
return result;
}
public static PGPKeyRing getPGPKeyRing(Context context, Uri queryUri) {
- return getPGPKeyRings(context, queryUri).values().iterator().next();
+ Map<Long, PGPKeyRing> result = getPGPKeyRings(context, queryUri);
+ if(result.isEmpty())
+ return null;
+ return result.values().iterator().next();
}
- /**
- * Retrieves the actual PGPPublicKeyRing object from the database blob based on the rowId
- */
- public static PGPPublicKeyRing getPGPPublicKeyRingByRowId(Context context, long rowId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
- return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri);
+ public static PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(Context context, long keyId) {
+ Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
+ long masterKeyId = getMasterKeyId(context, uri);
+ if(masterKeyId != 0)
+ return getPGPPublicKeyRing(context, masterKeyId);
+ return null;
+ }
+ public static PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(Context context, long keyId) {
+ Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
+ long masterKeyId = getMasterKeyId(context, uri);
+ if(masterKeyId != 0)
+ return getPGPSecretKeyRing(context, masterKeyId);
+ return null;
}
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
*/
- public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context,
+ public static PGPPublicKeyRing getPGPPublicKeyRing(Context context,
long masterKeyId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
- return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri);
- }
-
- /**
- * Retrieves the actual PGPPublicKeyRing object from the database blob associated with a key
- * with this keyId
- */
- public static PGPPublicKeyRing getPGPPublicKeyRingByKeyId(Context context, long keyId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsByKeyIdUri(Long.toString(keyId));
+ Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
return (PGPPublicKeyRing) getPGPKeyRing(context, queryUri);
}
/**
- * Retrieves the actual PGPPublicKey object from the database blob associated with a key with
- * this keyId
- */
- public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) {
- PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId);
-
- return (keyRing == null) ? null : keyRing.getPublicKey(keyId);
- }
-
- /**
- * Retrieves the actual PGPSecretKeyRing object from the database blob based on the rowId
- */
- public static PGPSecretKeyRing getPGPSecretKeyRingByRowId(Context context, long rowId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri(Long.toString(rowId));
- return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri);
- }
-
- /**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*/
- public static PGPSecretKeyRing getPGPSecretKeyRingByMasterKeyId(Context context,
+ public static PGPSecretKeyRing getPGPSecretKeyRing(Context context,
long masterKeyId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
+ Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri);
}
/**
- * Retrieves the actual PGPSecretKeyRing object from the database blob associated with a key
- * with this keyId
- */
- public static PGPSecretKeyRing getPGPSecretKeyRingByKeyId(Context context, long keyId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsByKeyIdUri(Long.toString(keyId));
- return (PGPSecretKeyRing) getPGPKeyRing(context, queryUri);
- }
-
- /**
- * Retrieves the actual PGPSecretKey object from the database blob associated with a key with
- * this keyId
- */
- public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) {
- PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId);
-
- return (keyRing == null) ? null : keyRing.getSecretKey(keyId);
- }
-
- /**
* Saves PGPPublicKeyRing with its keys and userIds in DB
*/
@SuppressWarnings("unchecked")
@@ -162,21 +190,12 @@ public class ProviderHelper {
PGPPublicKey masterKey = keyRing.getPublicKey();
long masterKeyId = masterKey.getKeyID();
- Uri deleteUri = KeyRings.buildPublicKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
-
- // get current _ID of key
- long currentRowId = -1;
- Cursor oldQuery = context.getContentResolver()
- .query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
- if (oldQuery != null && oldQuery.moveToFirst()) {
- currentRowId = oldQuery.getLong(0);
- } else {
- Log.e(Constants.TAG, "Key could not be found! Something wrong is happening!");
- }
+ // IF there is a secret key, preserve it!
+ PGPSecretKeyRing secretRing = ProviderHelper.getPGPSecretKeyRing(context, masterKeyId);
// delete old version of this keyRing, which also deletes all keys and userIds on cascade
try {
- context.getContentResolver().delete(deleteUri, null, null);
+ context.getContentResolver().delete(KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null);
} catch (UnsupportedOperationException e) {
Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e);
}
@@ -186,32 +205,28 @@ public class ProviderHelper {
// NOTE: If we would not use the same _ID again,
// getting back to the ViewKeyActivity would result in Nullpointer,
// because the currently loaded key would be gone from the database
- if (currentRowId != -1) {
- values.put(KeyRings._ID, currentRowId);
- }
- values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
- values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
+ values.put(KeyRingData.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded());
// insert new version of this keyRing
- Uri uri = KeyRings.buildPublicKeyRingsUri();
+ Uri uri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
Uri insertedUri = context.getContentResolver().insert(uri, values);
- long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment());
// save all keys and userIds included in keyRing object in database
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
int rank = 0;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
- operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank));
+ operations.add(buildPublicKeyOperations(context, masterKeyId, key, rank));
++rank;
}
// get a list of owned secret keys, for verification filtering
- Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(context, KeyRings.buildSecretKeyRingsUri());
+ Map<Long, PGPKeyRing> allKeyRings = getPGPKeyRings(context, KeyRingData.buildSecretKeyRingUri());
int userIdRank = 0;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
- operations.add(buildPublicUserIdOperations(context, keyRingRowId, userId, userIdRank));
+ operations.add(buildUserIdOperations(context, masterKeyId, userId, userIdRank));
// look through signatures for this specific key
for (PGPSignature cert : new IterableIterator<PGPSignature>(
@@ -244,8 +259,8 @@ public class ProviderHelper {
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID())
);
// regardless of verification, save the certification
- operations.add(buildPublicCertOperations(
- context, keyRingRowId, userIdRank, masterKey.getKeyID(), cert, verified));
+ operations.add(buildCertOperations(
+ context, masterKeyId, userIdRank, masterKey.getKeyID(), cert, verified));
}
++userIdRank;
@@ -258,99 +273,74 @@ public class ProviderHelper {
} catch (OperationApplicationException e) {
Log.e(Constants.TAG, "applyBatch failed!", e);
}
+
+ // Save the saved keyring (if any)
+ if(secretRing != null) {
+ saveKeyRing(context, secretRing);
+ }
+
}
/**
- * Saves PGPSecretKeyRing with its keys and userIds in DB
+ * Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
+ * is already in the database!
*/
@SuppressWarnings("unchecked")
public static void saveKeyRing(Context context, PGPSecretKeyRing keyRing) throws IOException {
- PGPSecretKey masterKey = keyRing.getSecretKey();
- long masterKeyId = masterKey.getKeyID();
-
- Uri deleteUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
-
- // get current _ID of key
- long currentRowId = -1;
- Cursor oldQuery = context.getContentResolver()
- .query(deleteUri, new String[]{KeyRings._ID}, null, null, null);
- if (oldQuery != null && oldQuery.moveToFirst()) {
- currentRowId = oldQuery.getLong(0);
- } else {
- Log.e(Constants.TAG, "Key could not be found! Something wrong is happening!");
- }
-
- // delete old version of this keyRing, which also deletes all keys and userIds on cascade
- try {
- context.getContentResolver().delete(deleteUri, null, null);
- } catch (UnsupportedOperationException e) {
- Log.e(Constants.TAG, "Key could not be deleted! Maybe we are creating a new one!", e);
- }
+ long masterKeyId = keyRing.getPublicKey().getKeyID();
+ // save secret keyring
ContentValues values = new ContentValues();
- // use exactly the same _ID again to replace key in-place.
- // NOTE: If we would not use the same _ID again,
- // getting back to the ViewKeyActivity would result in Nullpointer,
- // because the currently loaded key would be gone from the database
- if (currentRowId != -1) {
- values.put(KeyRings._ID, currentRowId);
- }
- values.put(KeyRings.MASTER_KEY_ID, masterKeyId);
- values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded());
-
+ values.put(KeyRingData.MASTER_KEY_ID, masterKeyId);
+ values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded());
// insert new version of this keyRing
- Uri uri = KeyRings.buildSecretKeyRingsUri();
- Uri insertedUri = context.getContentResolver().insert(uri, values);
- long keyRingRowId = Long.valueOf(insertedUri.getLastPathSegment());
+ Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
+ context.getContentResolver().insert(uri, values);
- // save all keys and userIds included in keyRing object in database
- ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
+ }
- int rank = 0;
- for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
- operations.add(buildSecretKeyOperations(context, keyRingRowId, key, rank));
- ++rank;
- }
+ /**
+ * Saves (or updates) a pair of public and secret KeyRings in the database
+ */
+ @SuppressWarnings("unchecked")
+ public static void saveKeyRing(Context context, PGPPublicKeyRing pubRing, PGPSecretKeyRing privRing) throws IOException {
+ long masterKeyId = pubRing.getPublicKey().getKeyID();
- int userIdRank = 0;
- for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
- operations.add(buildSecretUserIdOperations(context, keyRingRowId, userId, userIdRank));
- ++userIdRank;
- }
+ // delete secret keyring (so it isn't unnecessarily saved by public-saveKeyRing below)
+ context.getContentResolver().delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
- try {
- context.getContentResolver().applyBatch(KeychainContract.CONTENT_AUTHORITY, operations);
- } catch (RemoteException e) {
- Log.e(Constants.TAG, "applyBatch failed!", e);
- } catch (OperationApplicationException e) {
- Log.e(Constants.TAG, "applyBatch failed!", e);
- }
+ // save public keyring
+ saveKeyRing(context, pubRing);
+ saveKeyRing(context, privRing);
}
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
private static ContentProviderOperation buildPublicKeyOperations(Context context,
- long keyRingRowId, PGPPublicKey key, int rank) throws IOException {
+ long masterKeyId, PGPPublicKey key, int rank) throws IOException {
+
ContentValues values = new ContentValues();
+ values.put(Keys.MASTER_KEY_ID, masterKeyId);
+ values.put(Keys.RANK, rank);
+
values.put(Keys.KEY_ID, key.getKeyID());
- values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
- values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.KEY_SIZE, key.getBitStrength());
- values.put(Keys.CAN_SIGN, PgpKeyHelper.isSigningKey(key));
+ values.put(Keys.ALGORITHM, key.getAlgorithm());
+ values.put(Keys.FINGERPRINT, key.getFingerprint());
+
+ values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key)));
+ values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key)));
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
values.put(Keys.IS_REVOKED, key.isRevoked());
+
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
if (expiryDate != null) {
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
}
- values.put(Keys.KEY_RING_ROW_ID, keyRingRowId);
- values.put(Keys.KEY_DATA, key.getEncoded());
- values.put(Keys.RANK, rank);
- values.put(Keys.FINGERPRINT, key.getFingerprint());
- Uri uri = Keys.buildPublicKeysUri(Long.toString(keyRingRowId));
+ Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -358,23 +348,23 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
- private static ContentProviderOperation buildPublicCertOperations(Context context,
- long keyRingRowId,
+ private static ContentProviderOperation buildCertOperations(Context context,
+ long masterKeyId,
int rank,
long keyId,
PGPSignature cert,
boolean verified)
throws IOException {
ContentValues values = new ContentValues();
- values.put(Certs.KEY_RING_ROW_ID, keyRingRowId);
+ values.put(Certs.MASTER_KEY_ID, masterKeyId);
values.put(Certs.RANK, rank);
- values.put(Certs.KEY_ID, keyId);
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
+ values.put(Certs.EXPIRY, (String) null); // TODO
values.put(Certs.VERIFIED, verified);
values.put(Certs.KEY_DATA, cert.getEncoded());
- Uri uri = Certs.buildCertsByKeyRowIdUri(Long.toString(keyRingRowId));
+ Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId));
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
@@ -382,351 +372,28 @@ public class ProviderHelper {
/**
* Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing
*/
- private static ContentProviderOperation buildPublicUserIdOperations(Context context,
- long keyRingRowId, String userId, int rank) {
- ContentValues values = new ContentValues();
- values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId);
- values.put(UserIds.USER_ID, userId);
- values.put(UserIds.RANK, rank);
-
- Uri uri = UserIds.buildPublicUserIdsUri(Long.toString(keyRingRowId));
-
- return ContentProviderOperation.newInsert(uri).withValues(values).build();
- }
-
- /**
- * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing
- */
- private static ContentProviderOperation buildSecretKeyOperations(Context context,
- long keyRingRowId, PGPSecretKey key, int rank) throws IOException {
- ContentValues values = new ContentValues();
-
- boolean hasPrivate = true;
- if (key.isMasterKey()) {
- if (key.isPrivateKeyEmpty()) {
- hasPrivate = false;
- }
- }
-
- values.put(Keys.KEY_ID, key.getKeyID());
- values.put(Keys.IS_MASTER_KEY, key.isMasterKey());
- values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm());
- values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength());
- values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate));
- values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate));
- values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
- values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked());
- values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
- Date expiryDate = PgpKeyHelper.getExpiryDate(key);
- if (expiryDate != null) {
- values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
- }
- values.put(Keys.KEY_RING_ROW_ID, keyRingRowId);
- values.put(Keys.KEY_DATA, key.getEncoded());
- values.put(Keys.RANK, rank);
- values.put(Keys.FINGERPRINT, key.getPublicKey().getFingerprint());
-
- Uri uri = Keys.buildSecretKeysUri(Long.toString(keyRingRowId));
-
- return ContentProviderOperation.newInsert(uri).withValues(values).build();
- }
-
- /**
- * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing
- */
- private static ContentProviderOperation buildSecretUserIdOperations(Context context,
- long keyRingRowId, String userId, int rank) {
+ private static ContentProviderOperation buildUserIdOperations(Context context,
+ long masterKeyId, String userId, int rank) {
ContentValues values = new ContentValues();
- values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId);
+ values.put(UserIds.MASTER_KEY_ID, masterKeyId);
values.put(UserIds.USER_ID, userId);
values.put(UserIds.RANK, rank);
- Uri uri = UserIds.buildSecretUserIdsUri(Long.toString(keyRingRowId));
+ Uri uri = UserIds.buildUserIdsUri(Long.toString(masterKeyId));
return ContentProviderOperation.newInsert(uri).withValues(values).build();
}
- /**
- * Private helper method
- */
- private static ArrayList<Long> getKeyRingsMasterKeyIds(Context context, Uri queryUri) {
- Cursor cursor = context.getContentResolver().query(queryUri,
- new String[]{KeyRings.MASTER_KEY_ID}, null, null, null);
-
- ArrayList<Long> masterKeyIds = new ArrayList<Long>();
- if (cursor != null) {
- int masterKeyIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
- if (cursor.moveToFirst()) {
- do {
- masterKeyIds.add(cursor.getLong(masterKeyIdCol));
- } while (cursor.moveToNext());
- }
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- return masterKeyIds;
- }
-
- /**
- * Private helper method
- */
- private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) {
- Cursor cursor = context.getContentResolver().query(queryUri,
- new String[]{KeyRings._ID}, null, null, null);
-
- ArrayList<Long> rowIds = new ArrayList<Long>();
- if (cursor != null) {
- int idCol = cursor.getColumnIndex(KeyRings._ID);
- if (cursor.moveToFirst()) {
- do {
- rowIds.add(cursor.getLong(idCol));
- } while (cursor.moveToNext());
- }
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- return rowIds;
- }
-
- /**
- * Retrieves ids of all SecretKeyRings
- */
- public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri();
- return getKeyRingsMasterKeyIds(context, queryUri);
- }
-
- /**
- * Retrieves ids of all PublicKeyRings
- */
- public static ArrayList<Long> getPublicKeyRingsMasterKeyIds(Context context) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri();
- return getKeyRingsMasterKeyIds(context, queryUri);
- }
-
- /**
- * Retrieves ids of all SecretKeyRings
- */
- public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri();
- return getKeyRingsRowIds(context, queryUri);
- }
-
- /**
- * Retrieves ids of all PublicKeyRings
- */
- public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri();
- return getKeyRingsRowIds(context, queryUri);
- }
-
- public static void deletePublicKeyRing(Context context, long rowId) {
- ContentResolver cr = context.getContentResolver();
- cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null);
- }
-
- public static void deleteSecretKeyRing(Context context, long rowId) {
- ContentResolver cr = context.getContentResolver();
- cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null);
- }
-
- /**
- * Get master key id of keyring by its row id
- */
- public static long getPublicMasterKeyId(Context context, long keyRingRowId) {
- Uri queryUri = KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri);
- }
-
- /**
- * Get empty status of master key of keyring by its row id
- */
- public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyCanSign(context, queryUri);
- }
-
- /**
- * Private helper method to get master key private empty status of keyring by its row id
- */
- public static boolean getMasterKeyCanSign(Context context, Uri queryUri) {
- String[] projection = new String[]{
- KeyRings.MASTER_KEY_ID,
- "(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS sign_keys WHERE sign_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND sign_keys." + Keys.CAN_SIGN + " = '1' AND " + Keys.IS_MASTER_KEY
- + " = 1) AS sign", };
-
- ContentResolver cr = context.getContentResolver();
- Cursor cursor = cr.query(queryUri, projection, null, null, null);
-
- long masterKeyId = -1;
- if (cursor != null && cursor.moveToFirst()) {
- int masterKeyIdCol = cursor.getColumnIndex("sign");
-
- masterKeyId = cursor.getLong(masterKeyIdCol);
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- return (masterKeyId > 0);
- }
-
- public static boolean hasSecretKeyByMasterKeyId(Context context, long masterKeyId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId));
- // see if we can get our master key id back from the uri
- return getMasterKeyId(context, queryUri) == masterKeyId;
- }
-
- /**
- * Get master key id of keyring by its row id
- */
- public static long getSecretMasterKeyId(Context context, long keyRingRowId) {
- Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId));
- return getMasterKeyId(context, queryUri);
- }
-
- /**
- * Get master key id of key
- */
- public static long getMasterKeyId(Context context, Uri queryUri) {
- String[] projection = new String[]{KeyRings.MASTER_KEY_ID};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- long masterKeyId = 0;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int masterKeyIdCol = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
-
- masterKeyId = cursor.getLong(masterKeyIdCol);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return masterKeyId;
- }
-
- public static long getRowId(Context context, Uri queryUri) {
- String[] projection = new String[]{KeyRings._ID};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- long rowId = 0;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int idCol = cursor.getColumnIndexOrThrow(KeyRings._ID);
-
- rowId = cursor.getLong(idCol);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return rowId;
- }
-
- /**
- * Get fingerprint of key
- */
- public static byte[] getFingerprint(Context context, Uri queryUri) {
- String[] projection = new String[]{Keys.FINGERPRINT};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- byte[] fingerprint = null;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int col = cursor.getColumnIndexOrThrow(Keys.FINGERPRINT);
-
- fingerprint = cursor.getBlob(col);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- // FALLBACK: If fingerprint is not in database, get it from key blob!
- // this could happen if the key was saved by a previous version of Keychain!
- if (fingerprint == null) {
- Log.d(Constants.TAG, "FALLBACK: fingerprint is not in database, get it from key blob!");
-
- // get master key id
- projection = new String[]{KeyRings.MASTER_KEY_ID};
- cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
- long masterKeyId = 0;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int col = cursor.getColumnIndexOrThrow(KeyRings.MASTER_KEY_ID);
-
- masterKeyId = cursor.getLong(col);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, masterKeyId);
- // if it is no public key get it from your own keys...
- if (key == null) {
- PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, masterKeyId);
- if (secretKey == null) {
- Log.e(Constants.TAG, "Key could not be found!");
- return null;
- }
- key = secretKey.getPublicKey();
- }
-
- fingerprint = key.getFingerprint();
- }
-
- return fingerprint;
- }
-
- public static String getUserId(Context context, Uri queryUri) {
- String[] projection = new String[]{UserIds.USER_ID};
- Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null);
-
- String userId = null;
- try {
- if (cursor != null && cursor.moveToFirst()) {
- int col = cursor.getColumnIndexOrThrow(UserIds.USER_ID);
-
- userId = cursor.getString(col);
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
-
- return userId;
- }
-
- public static ArrayList<String> getKeyRingsAsArmoredString(Context context, Uri uri,
- long[] masterKeyIds) {
+ public static ArrayList<String> getKeyRingsAsArmoredString(Context context, long[] masterKeyIds) {
ArrayList<String> output = new ArrayList<String>();
if (masterKeyIds != null && masterKeyIds.length > 0) {
- Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds);
+ Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, masterKeyIds);
if (cursor != null) {
- int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
- int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA);
+ int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
+ int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
if (cursor.moveToFirst()) {
do {
Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
@@ -776,48 +443,11 @@ public class ProviderHelper {
return null;
}
}
-
- public static byte[] getKeyRingsAsByteArray(Context context, Uri uri, long[] masterKeyIds) {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
- if (masterKeyIds != null && masterKeyIds.length > 0) {
-
- Cursor cursor = getCursorWithSelectedKeyringMasterKeyIds(context, uri, masterKeyIds);
-
- if (cursor != null) {
- int masterIdCol = cursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
- int dataCol = cursor.getColumnIndex(KeyRings.KEY_RING_DATA);
- if (cursor.moveToFirst()) {
- do {
- Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
-
- // get actual keyring data blob and write it to ByteArrayOutputStream
- try {
- bos.write(cursor.getBlob(dataCol));
- } catch (IOException e) {
- Log.e(Constants.TAG, "IOException", e);
- }
- } while (cursor.moveToNext());
- }
- }
-
- if (cursor != null) {
- cursor.close();
- }
-
- } else {
- Log.e(Constants.TAG, "No master keys given!");
- }
-
- return bos.toByteArray();
- }
-
- private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, Uri baseUri,
- long[] masterKeyIds) {
+ private static Cursor getCursorWithSelectedKeyringMasterKeyIds(Context context, long[] masterKeyIds) {
Cursor cursor = null;
if (masterKeyIds != null && masterKeyIds.length > 0) {
- String inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN (";
+ String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN (";
for (int i = 0; i < masterKeyIds.length; ++i) {
if (i != 0) {
inMasterKeyList += ", ";
@@ -826,9 +456,9 @@ public class ProviderHelper {
}
inMasterKeyList += ")";
- cursor = context.getContentResolver().query(baseUri,
- new String[]{KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.KEY_RING_DATA},
- inMasterKeyList, null, null);
+ cursor = context.getContentResolver().query(KeyRingData.buildPublicKeyRingUri(), new String[] {
+ KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA
+ }, inMasterKeyList, null, null);
}
return cursor;
@@ -859,19 +489,28 @@ public class ProviderHelper {
ContentValues values = new ContentValues();
values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName());
values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature());
- values.put(ApiApps.KEY_ID, appSettings.getKeyId());
- values.put(ApiApps.COMPRESSION, appSettings.getCompression());
- values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm());
- values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm());
+ return values;
+ }
+ private static ContentValues contentValueForApiAccounts(AccountSettings accSettings) {
+ ContentValues values = new ContentValues();
+ values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName());
+ values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId());
+ values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression());
+ values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm());
+ values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm());
return values;
}
public static void insertApiApp(Context context, AppSettings appSettings) {
- context.getContentResolver().insert(ApiApps.CONTENT_URI,
+ context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI,
contentValueForApiApps(appSettings));
}
+ public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) {
+ context.getContentResolver().insert(uri, contentValueForApiAccounts(accSettings));
+ }
+
public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) {
if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null,
null) <= 0) {
@@ -879,30 +518,73 @@ public class ProviderHelper {
}
}
+ public static void updateApiAccount(Context context, AccountSettings accSettings, Uri uri) {
+ if (context.getContentResolver().update(uri, contentValueForApiAccounts(accSettings), null,
+ null) <= 0) {
+ throw new RuntimeException();
+ }
+ }
+
+ /**
+ * Must be an uri pointing to an account
+ *
+ * @param context
+ * @param uri
+ * @return
+ */
public static AppSettings getApiAppSettings(Context context, Uri uri) {
AppSettings settings = null;
Cursor cur = context.getContentResolver().query(uri, null, null, null, null);
if (cur != null && cur.moveToFirst()) {
settings = new AppSettings();
- settings.setPackageName(cur.getString(cur
- .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
- settings.setPackageSignature(cur.getBlob(cur
- .getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
- settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID)));
- settings.setCompression(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.COMPRESSION)));
- settings.setHashAlgorithm(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.HASH_ALORITHM)));
- settings.setEncryptionAlgorithm(cur.getInt(cur
- .getColumnIndexOrThrow(KeychainContract.ApiApps.ENCRYPTION_ALGORITHM)));
+ settings.setPackageName(cur.getString(
+ cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME)));
+ settings.setPackageSignature(cur.getBlob(
+ cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE)));
}
return settings;
}
+ public static AccountSettings getApiAccountSettings(Context context, Uri accountUri) {
+ AccountSettings settings = null;
+
+ Cursor cur = context.getContentResolver().query(accountUri, null, null, null, null);
+ if (cur != null && cur.moveToFirst()) {
+ settings = new AccountSettings();
+
+ settings.setAccountName(cur.getString(
+ cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME)));
+ settings.setKeyId(cur.getLong(
+ cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID)));
+ settings.setCompression(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION)));
+ settings.setHashAlgorithm(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM)));
+ settings.setEncryptionAlgorithm(cur.getInt(
+ cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM)));
+ }
+
+ return settings;
+ }
+
+ public static Set<Long> getAllKeyIdsForApp(Context context, Uri uri) {
+ Set<Long> keyIds = new HashSet<Long>();
+
+ Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
+ if (cursor != null) {
+ int keyIdColumn = cursor.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID);
+ while (cursor.moveToNext()) {
+ keyIds.add(cursor.getLong(keyIdColumn));
+ }
+ }
+
+ return keyIds;
+ }
+
public static byte[] getApiAppSignature(Context context, String packageName) {
- Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName);
+ Uri queryUri = ApiApps.buildByPackageNameUri(packageName);
String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE};
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
index 6f2d67efb..832cbc752 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java
@@ -15,48 +15,39 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Id;
-public class AppSettings {
- private String mPackageName;
- private byte[] mPackageSignature;
+public class AccountSettings {
+ private String mAccountName;
private long mKeyId = Id.key.none;
private int mEncryptionAlgorithm;
private int mHashAlgorithm;
private int mCompression;
- public AppSettings() {
+ public AccountSettings() {
}
- public AppSettings(String packageName, byte[] packageSignature) {
+ public AccountSettings(String accountName) {
super();
- this.mPackageName = packageName;
- this.mPackageSignature = packageSignature;
+ this.mAccountName = accountName;
+
// defaults:
this.mEncryptionAlgorithm = PGPEncryptedData.AES_256;
this.mHashAlgorithm = HashAlgorithmTags.SHA512;
this.mCompression = Id.choice.compression.zlib;
}
- public String getPackageName() {
- return mPackageName;
- }
-
- public void setPackageName(String packageName) {
- this.mPackageName = packageName;
- }
-
- public byte[] getPackageSignature() {
- return mPackageSignature;
+ public String getAccountName() {
+ return mAccountName;
}
- public void setPackageSignature(byte[] packageSignature) {
- this.mPackageSignature = packageSignature;
+ public void setAccountName(String mAccountName) {
+ this.mAccountName = mAccountName;
}
public long getKeyId() {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
new file mode 100644
index 000000000..a3f9f84c9
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote;
+
+public class AppSettings {
+ private String mPackageName;
+ private byte[] mPackageSignature;
+
+ public AppSettings() {
+
+ }
+
+ public AppSettings(String packageName, byte[] packageSignature) {
+ super();
+ this.mPackageName = packageName;
+ this.mPackageSignature = packageSignature;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public void setPackageName(String packageName) {
+ this.mPackageName = packageName;
+ }
+
+ public byte[] getPackageSignature() {
+ return mPackageSignature;
+ }
+
+ public void setPackageSignature(byte[] packageSignature) {
+ this.mPackageSignature = packageSignature;
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 95dc897f0..b38fea5a9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import android.app.PendingIntent;
import android.content.Intent;
@@ -23,6 +23,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
@@ -34,22 +35,22 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
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.ApiAccounts;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Set;
public class OpenPgpService extends RemoteService {
- private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
- private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
- private static final int PRIVATE_REQUEST_CODE_GET_KEYS = 553;
-
/**
* Search database for key ids based on emails.
*
@@ -61,15 +62,15 @@ public class OpenPgpService extends RemoteService {
ArrayList<Long> keyIds = new ArrayList<Long>();
boolean missingUserIdsCheck = false;
- boolean dublicateUserIdsCheck = false;
+ boolean duplicateUserIdsCheck = false;
ArrayList<String> missingUserIds = new ArrayList<String>();
- ArrayList<String> dublicateUserIds = new ArrayList<String>();
+ ArrayList<String> duplicateUserIds = new ArrayList<String>();
for (String email : encryptionUserIds) {
- Uri uri = KeychainContract.KeyRings.buildPublicKeyRingsByEmailsUri(email);
+ Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
Cursor cur = getContentResolver().query(uri, null, null, null, null);
if (cur.moveToFirst()) {
- long id = cur.getLong(cur.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID));
+ long id = cur.getLong(cur.getColumnIndex(KeyRings.MASTER_KEY_ID));
keyIds.add(id);
} else {
missingUserIdsCheck = true;
@@ -77,8 +78,8 @@ public class OpenPgpService extends RemoteService {
Log.d(Constants.TAG, "user id missing");
}
if (cur.moveToNext()) {
- dublicateUserIdsCheck = true;
- dublicateUserIds.add(email);
+ duplicateUserIdsCheck = true;
+ duplicateUserIds.add(email);
Log.d(Constants.TAG, "more than one user id with the same email");
}
}
@@ -90,17 +91,18 @@ public class OpenPgpService extends RemoteService {
}
// allow the user to verify pub key selection
- if (missingUserIdsCheck || dublicateUserIdsCheck) {
+ if (missingUserIdsCheck || duplicateUserIdsCheck) {
// build PendingIntent
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
- intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
+ intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, duplicateUserIds);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity
- (getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -126,8 +128,9 @@ public class OpenPgpService extends RemoteService {
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity
- (getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -137,7 +140,7 @@ public class OpenPgpService extends RemoteService {
}
private Intent signImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings) {
+ ParcelFileDescriptor output, AccountSettings accSettings) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
@@ -146,11 +149,11 @@ public class OpenPgpService extends RemoteService {
if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
@@ -164,9 +167,9 @@ public class OpenPgpService extends RemoteService {
// sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
- .signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ .signatureHashAlgorithm(accSettings.getHashAlgorithm())
.signatureForceV3(false)
- .signatureKeyId(appSettings.getKeyId())
+ .signatureKeyId(accSettings.getKeyId())
.signaturePassphrase(passphrase);
builder.build().execute();
} finally {
@@ -187,7 +190,8 @@ public class OpenPgpService extends RemoteService {
}
private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings, boolean sign) {
+ ParcelFileDescriptor output, AccountSettings accSettings,
+ boolean sign) {
try {
boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
@@ -210,14 +214,14 @@ public class OpenPgpService extends RemoteService {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_ERROR,
new OpenPgpError(OpenPgpError.GENERIC_ERROR,
- "Missing parameter user_ids or key_ids!"));
+ "Missing parameter user_ids or key_ids!"));
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
}
// add own key for encryption
keyIds = Arrays.copyOf(keyIds, keyIds.length + 1);
- keyIds[keyIds.length - 1] = appSettings.getKeyId();
+ keyIds[keyIds.length - 1] = accSettings.getKeyId();
// build InputData and write into OutputStream
// Get Input- and OutputStream from ParcelFileDescriptor
@@ -229,8 +233,8 @@ public class OpenPgpService extends RemoteService {
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
- .compressionId(appSettings.getCompression())
- .symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm())
+ .compressionId(accSettings.getCompression())
+ .symmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
.encryptionKeyIds(keyIds);
if (sign) {
@@ -239,18 +243,18 @@ public class OpenPgpService extends RemoteService {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
- appSettings.getKeyId());
+ accSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
}
// sign and encrypt
- builder.signatureHashAlgorithm(appSettings.getHashAlgorithm())
+ builder.signatureHashAlgorithm(accSettings.getHashAlgorithm())
.signatureForceV3(false)
- .signatureKeyId(appSettings.getKeyId())
+ .signatureKeyId(accSettings.getKeyId())
.signaturePassphrase(passphrase);
} else {
// encrypt only
@@ -276,7 +280,7 @@ public class OpenPgpService extends RemoteService {
}
private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input,
- ParcelFileDescriptor output, AppSettings appSettings) {
+ ParcelFileDescriptor output, Set<Long> allowedKeyIds) {
try {
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
@@ -290,19 +294,21 @@ public class OpenPgpService extends RemoteService {
InputData inputData = new InputData(is, inputLength);
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
- builder.assumeSymmetric(false) // no support for symmetric encryption
- // allow only the private key for this app for decryption
- .enforcedKeyId(appSettings.getKeyId())
+ builder.allowSymmetricDecryption(false) // no support for symmetric encryption
+ .allowedKeyIds(allowedKeyIds) // allow only private keys associated with
+ // accounts of this app
.passphrase(passphrase);
// TODO: currently does not support binary signed-only content
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
- if (decryptVerifyResult.isKeyPassphraseNeeded()) {
+ if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
// get PendingIntent for passphrase input, add it to given params and return to client
- Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId());
+ Intent passphraseBundle =
+ getPassphraseBundleIntent(data, decryptVerifyResult.getKeyIdPassphraseNeeded());
return passphraseBundle;
- } else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) {
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
+ decryptVerifyResult.getStatus()) {
throw new PgpGeneralException("Decryption of symmetric content not supported by API!");
}
@@ -311,14 +317,14 @@ public class OpenPgpService extends RemoteService {
if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY) {
// If signature is unknown we return an _additional_ PendingIntent
// to retrieve the missing key
- // TODO!!!
- Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
- intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
- intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
- intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
+ Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId());
+ intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
}
@@ -346,19 +352,19 @@ public class OpenPgpService extends RemoteService {
try {
long keyId = data.getLongExtra(OpenPgpApi.EXTRA_KEY_ID, 0);
- if (ProviderHelper.getPGPPublicKeyByKeyId(this, keyId) == null) {
+ if (ProviderHelper.getPGPPublicKeyRing(this, keyId) == null) {
Intent result = new Intent();
// If keys are not in db we return an additional PendingIntent
// to retrieve the missing key
- // TODO!!!
- Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
- intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
- intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo");
- intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
+ Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, keyId);
+ intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
@@ -366,6 +372,9 @@ public class OpenPgpService extends RemoteService {
} else {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+
+ // TODO: also return PendingIntent that opens the key view activity
+
return result;
}
} catch (Exception e) {
@@ -407,7 +416,7 @@ public class OpenPgpService extends RemoteService {
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) {
Intent result = new Intent();
OpenPgpError error = new OpenPgpError
- (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
+ (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
result.putExtra(OpenPgpApi.RESULT_ERROR, error);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
return result;
@@ -432,17 +441,30 @@ public class OpenPgpService extends RemoteService {
return errorResult;
}
- final AppSettings appSettings = getAppSettings();
+ String accName;
+ if (data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME) != null) {
+ accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME);
+ } else {
+ accName = "default";
+ }
+ final AccountSettings accSettings = getAccSettings(accName);
+ if (accSettings == null) {
+ return getCreateAccountIntent(data, accName);
+ }
String action = data.getAction();
if (OpenPgpApi.ACTION_SIGN.equals(action)) {
- return signImpl(data, input, output, appSettings);
+ return signImpl(data, input, output, accSettings);
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, appSettings, false);
+ return encryptAndSignImpl(data, input, output, accSettings, false);
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, appSettings, true);
+ return encryptAndSignImpl(data, input, output, accSettings, true);
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
- return decryptAndVerifyImpl(data, input, output, appSettings);
+ String currentPkg = getCurrentCallingPackage();
+ Set<Long> allowedKeyIds =
+ ProviderHelper.getAllKeyIdsForApp(mContext,
+ ApiAccounts.buildBaseUri(currentPkg));
+ return decryptAndVerifyImpl(data, input, output, allowedKeyIds);
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
return getKeyImpl(data);
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
index 6a883316a..16a800022 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
import android.app.PendingIntent;
import android.app.Service;
@@ -27,12 +27,14 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
+
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -44,10 +46,6 @@ import java.util.Arrays;
public abstract class RemoteService extends Service {
Context mContext;
- private static final int PRIVATE_REQUEST_CODE_REGISTER = 651;
- private static final int PRIVATE_REQUEST_CODE_ERROR = 652;
-
-
public Context getContext() {
return mContext;
}
@@ -55,13 +53,10 @@ public abstract class RemoteService extends Service {
protected Intent isAllowed(Intent data) {
try {
if (isCallerAllowed(false)) {
-
return null;
} else {
- String[] callingPackages = getPackageManager().getPackagesForUid(
- Binder.getCallingUid());
- // TODO: currently simply uses first entry
- String packageName = callingPackages[0];
+ String packageName = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "isAllowed packageName: " + packageName);
byte[] packageSignature;
try {
@@ -83,8 +78,9 @@ public abstract class RemoteService extends Service {
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -99,11 +95,12 @@ public abstract class RemoteService extends Service {
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
- getString(R.string.api_error_wrong_signature));
+ getString(R.string.api_error_wrong_signature));
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(),
- PRIVATE_REQUEST_CODE_ERROR, intent, 0);
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
// return PendingIntent to be executed by client
Intent result = new Intent();
@@ -125,27 +122,57 @@ public abstract class RemoteService extends Service {
}
/**
- * Retrieves AppSettings from database for the application calling this remote service
+ * Returns package name associated with the UID, which is assigned to the process that sent you the
+ * current transaction that is being processed :)
*
- * @return
+ * @return package name
*/
- protected AppSettings getAppSettings() {
+ protected String getCurrentCallingPackage() {
+ // TODO:
+ // callingPackages contains more than one entry when sharedUserId has been used...
String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
+ String currentPkg = callingPackages[0];
+ Log.d(Constants.TAG, "currentPkg: " + currentPkg);
- // get app settings for this package
- for (int i = 0; i < callingPackages.length; i++) {
- String currentPkg = callingPackages[i];
+ return currentPkg;
+ }
+
+ /**
+ * Retrieves AccountSettings from database for the application calling this remote service
+ *
+ * @return
+ */
+ protected AccountSettings getAccSettings(String accountName) {
+ String currentPkg = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "accountName: " + accountName);
- Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg);
+ Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName);
- AppSettings settings = ProviderHelper.getApiAppSettings(this, uri);
+ AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri);
- if (settings != null) {
- return settings;
- }
- }
+ return settings; // can be null!
+ }
+
+ protected Intent getCreateAccountIntent(Intent data, String accountName) {
+ String packageName = getCurrentCallingPackage();
+ Log.d(Constants.TAG, "accountName: " + accountName);
+
+ Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
+ intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT);
+ intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
+ intent.putExtra(RemoteServiceActivity.EXTRA_ACC_NAME, accountName);
+ intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
+
+ PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+
+ // return PendingIntent to be executed by client
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
- return null;
+ return result;
}
/**
@@ -180,7 +207,7 @@ public abstract class RemoteService extends Service {
}
}
- Log.d(Constants.TAG, "Caller is NOT allowed!");
+ Log.d(Constants.TAG, "Uid is NOT allowed!");
return false;
}
@@ -192,7 +219,7 @@ public abstract class RemoteService extends Service {
* @throws WrongPackageSignatureException
*/
private boolean isPackageAllowed(String packageName) throws WrongPackageSignatureException {
- Log.d(Constants.TAG, "packageName: " + packageName);
+ Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName);
ArrayList<String> allowedPkgs = ProviderHelper.getRegisteredApiApps(this);
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
@@ -216,10 +243,12 @@ public abstract class RemoteService extends Service {
return true;
} else {
throw new WrongPackageSignatureException(
- "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)");
+ "PACKAGE NOT ALLOWED! Signature wrong! (Signature not " +
+ "equals signature from database)");
}
}
+ Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
return false;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java
index 0b642086a..6f44a65e9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote;
public class WrongPackageSignatureException extends Exception {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index 2ef170dec..671a3e0aa 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
import android.net.Uri;
@@ -24,16 +24,18 @@ import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.util.Log;
-public class AppSettingsActivity extends ActionBarActivity {
- private Uri mAppUri;
+public class AccountSettingsActivity extends ActionBarActivity {
+ private Uri mAccountUri;
- private AppSettingsFragment mSettingsFragment;
+ private AccountSettingsFragment mAccountSettingsFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -50,57 +52,58 @@ public class AppSettingsActivity extends ActionBarActivity {
}
});
- setContentView(R.layout.api_app_settings_activity);
+ setContentView(R.layout.api_account_settings_activity);
- mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ mAccountSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
Intent intent = getIntent();
- mAppUri = intent.getData();
- if (mAppUri == null) {
+ mAccountUri = intent.getData();
+ if (mAccountUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
finish();
return;
} else {
- Log.d(Constants.TAG, "uri: " + mAppUri);
- loadData(mAppUri);
+ Log.d(Constants.TAG, "uri: " + mAccountUri);
+ loadData(savedInstanceState, mAccountUri);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.api_app_settings, menu);
+ getMenuInflater().inflate(R.menu.api_account_settings, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.menu_api_settings_revoke:
- revokeAccess();
+ case R.id.menu_account_settings_delete:
+ deleteAccount();
return true;
- case R.id.menu_api_settings_cancel:
+ case R.id.menu_account_settings_cancel:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
- private void loadData(Uri appUri) {
- AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
- mSettingsFragment.setAppSettings(settings);
+ private void loadData(Bundle savedInstanceState, Uri accountUri) {
+ // TODO: load this also like other fragment with newInstance arguments?
+ AccountSettings settings = ProviderHelper.getApiAccountSettings(this, accountUri);
+ mAccountSettingsFragment.setAccSettings(settings);
}
- private void revokeAccess() {
- if (getContentResolver().delete(mAppUri, null, null) <= 0) {
+ private void deleteAccount() {
+ if (getContentResolver().delete(mAccountUri, null, null) <= 0) {
throw new RuntimeException();
}
finish();
}
private void save() {
- ProviderHelper.updateApiApp(this, mSettingsFragment.getAppSettings(), mAppUri);
+ ProviderHelper.updateApiAccount(this, mAccountSettingsFragment.getAccSettings(), mAccountUri);
finish();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
new file mode 100644
index 000000000..992aa7c95
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2013-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.remote.ui;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.ui.EditKeyActivity;
+import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
+import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
+import org.sufficientlysecure.keychain.util.AlgorithmNames;
+
+public class AccountSettingsFragment extends Fragment implements
+ SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
+
+ private static final int REQUEST_CODE_CREATE_KEY = 0x00008884;
+
+ // model
+ private AccountSettings mAccSettings;
+
+ // view
+ private TextView mAccNameView;
+ private Spinner mEncryptionAlgorithm;
+ private Spinner mHashAlgorithm;
+ private Spinner mCompression;
+
+ private SelectSecretKeyLayoutFragment mSelectKeyFragment;
+ private BootstrapButton mCreateKeyButton;
+
+ KeyValueSpinnerAdapter mEncryptionAdapter;
+ KeyValueSpinnerAdapter mHashAdapter;
+ KeyValueSpinnerAdapter mCompressionAdapter;
+
+ public AccountSettings getAccSettings() {
+ return mAccSettings;
+ }
+
+ public void setAccSettings(AccountSettings accountSettings) {
+ this.mAccSettings = accountSettings;
+
+ mAccNameView.setText(accountSettings.getAccountName());
+ mSelectKeyFragment.selectKey(accountSettings.getKeyId());
+ mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(accountSettings
+ .getEncryptionAlgorithm()));
+ mHashAlgorithm.setSelection(mHashAdapter.getPosition(accountSettings.getHashAlgorithm()));
+ mCompression.setSelection(mCompressionAdapter.getPosition(accountSettings.getCompression()));
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.api_account_settings_fragment, container, false);
+ initView(view);
+ return view;
+ }
+
+ /**
+ * Set error String on key selection
+ *
+ * @param error
+ */
+ public void setErrorOnSelectKeyFragment(String error) {
+ mSelectKeyFragment.setError(error);
+ }
+
+ private void initView(View view) {
+ mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
+ R.id.api_account_settings_select_key_fragment);
+ mSelectKeyFragment.setCallback(this);
+
+ mAccNameView = (TextView) view.findViewById(R.id.api_account_settings_acc_name);
+ mEncryptionAlgorithm = (Spinner) view
+ .findViewById(R.id.api_account_settings_encryption_algorithm);
+ mHashAlgorithm = (Spinner) view.findViewById(R.id.api_account_settings_hash_algorithm);
+ mCompression = (Spinner) view.findViewById(R.id.api_account_settings_compression);
+ mCreateKeyButton = (BootstrapButton) view.findViewById(R.id.api_account_settings_create_key);
+
+ mCreateKeyButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ createKey();
+ }
+ });
+
+ AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
+
+ mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
+ algorithmNames.getEncryptionNames());
+ mEncryptionAlgorithm.setAdapter(mEncryptionAdapter);
+ mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setEncryptionAlgorithm((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
+ mHashAlgorithm.setAdapter(mHashAdapter);
+ mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setHashAlgorithm((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+
+ mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
+ algorithmNames.getCompressionNames());
+ mCompression.setAdapter(mCompressionAdapter);
+ mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
+
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ mAccSettings.setCompression((int) id);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ });
+ }
+
+ private void createKey() {
+ Intent intent = new Intent(getActivity(), EditKeyActivity.class);
+ intent.setAction(EditKeyActivity.ACTION_CREATE_KEY);
+ intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true);
+ // set default user id to account name TODO: not working currently in EditKey
+ intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, mAccSettings.getAccountName());
+ startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CODE_CREATE_KEY: {
+ if (resultCode == Activity.RESULT_OK) {
+ // select newly created key
+ long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), data.getData());
+ mSelectKeyFragment.selectKey(masterKeyId);
+ }
+ break;
+ }
+
+ default:
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+
+ /**
+ * callback from select secret key fragment
+ */
+ @Override
+ public void onKeySelected(long secretKeyId) {
+ mAccSettings.setKeyId(secretKeyId);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java
new file mode 100644
index 000000000..cfc9c92ad
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java
@@ -0,0 +1,198 @@
+/*
+ * 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.remote.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.ui.widget.FixedListView;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class AccountsListFragment extends ListFragment implements
+ LoaderManager.LoaderCallbacks<Cursor> {
+
+ private static final String ARG_DATA_URI = "uri";
+
+ // This is the Adapter being used to display the list's data.
+ AccountsAdapter mAdapter;
+
+ private Uri mDataUri;
+
+ /**
+ * Creates new instance of this fragment
+ */
+ public static AccountsListFragment newInstance(Uri dataUri) {
+ AccountsListFragment frag = new AccountsListFragment();
+
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_DATA_URI, dataUri);
+
+ frag.setArguments(args);
+
+ return frag;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View layout = super.onCreateView(inflater, container,
+ savedInstanceState);
+ ListView lv = (ListView) layout.findViewById(android.R.id.list);
+ ViewGroup parent = (ViewGroup) lv.getParent();
+
+ /*
+ * http://stackoverflow.com/a/15880684
+ * Remove ListView and add FixedListView in its place.
+ * This is done here programatically to be still able to use the progressBar of ListFragment.
+ *
+ * We want FixedListView to be able to put this ListFragment inside a ScrollView
+ */
+ int lvIndex = parent.indexOfChild(lv);
+ parent.removeViewAt(lvIndex);
+ FixedListView newLv = new FixedListView(getActivity());
+ newLv.setId(android.R.id.list);
+ parent.addView(newLv, lvIndex, lv.getLayoutParams());
+ return layout;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mDataUri = getArguments().getParcelable(ARG_DATA_URI);
+
+ getListView().setOnItemClickListener(new OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ String selectedAccountName = mAdapter.getItemAccountName(position);
+ Uri accountUri = mDataUri.buildUpon().appendEncodedPath(selectedAccountName).build();
+ Log.d(Constants.TAG, "accountUri: " + accountUri);
+
+ // edit account settings
+ Intent intent = new Intent(getActivity(), AccountSettingsActivity.class);
+ intent.setData(accountUri);
+ startActivity(intent);
+ }
+ });
+
+ // Give some text to display if there is no data. In a real
+ // application this would come from a resource.
+ setEmptyText(getString(R.string.api_settings_accounts_empty));
+
+ // We have a menu item to show in action bar.
+ setHasOptionsMenu(true);
+
+ // Create an empty adapter we will use to display the loaded data.
+ mAdapter = new AccountsAdapter(getActivity(), null, 0);
+ setListAdapter(mAdapter);
+
+ // Prepare the loader. Either re-connect with an existing one,
+ // or start a new one.
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ // These are the Contacts rows that we will retrieve.
+ static final String[] PROJECTION = new String[]{
+ KeychainContract.ApiAccounts._ID, // 0
+ KeychainContract.ApiAccounts.ACCOUNT_NAME // 1
+ };
+
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null,
+ KeychainContract.ApiAccounts.ACCOUNT_NAME + " COLLATE LOCALIZED ASC");
+ }
+
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ // Swap the new cursor in. (The framework will take care of closing the
+ // old cursor once we return.)
+ mAdapter.swapCursor(data);
+ }
+
+ public void onLoaderReset(Loader<Cursor> loader) {
+ // This is called when the last Cursor provided to onLoadFinished()
+ // above is about to be closed. We need to make sure we are no
+ // longer using it.
+ mAdapter.swapCursor(null);
+ }
+
+ private class AccountsAdapter extends CursorAdapter {
+ private LayoutInflater mInflater;
+
+ public AccountsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ }
+
+ /**
+ * Similar to CursorAdapter.getItemId().
+ * Required to build Uris for api app view, which is not based on row ids
+ *
+ * @param position
+ * @return
+ */
+ public String getItemAccountName(int position) {
+ if (mDataValid && mCursor != null) {
+ if (mCursor.moveToPosition(position)) {
+ return mCursor.getString(1);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView text = (TextView) view.findViewById(R.id.api_accounts_adapter_item_name);
+
+ String accountName = cursor.getString(1);
+ text.setText(accountName);
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.api_accounts_adapter_list_item, null);
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
new file mode 100644
index 000000000..f6f9631cb
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote.ui;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBarActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class AppSettingsActivity extends ActionBarActivity {
+ private Uri mAppUri;
+
+ private AppSettingsFragment mSettingsFragment;
+ private AccountsListFragment mAccountsListFragment;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // let the actionbar look like Android's contact app
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setIcon(android.R.color.transparent);
+ actionBar.setHomeButtonEnabled(true);
+
+ setContentView(R.layout.api_app_settings_activity);
+
+ mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ Intent intent = getIntent();
+ mAppUri = intent.getData();
+ if (mAppUri == null) {
+ Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
+ finish();
+ return;
+ } else {
+ Log.d(Constants.TAG, "uri: " + mAppUri);
+ loadData(savedInstanceState, mAppUri);
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ getMenuInflater().inflate(R.menu.api_app_settings, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_api_settings_revoke:
+ revokeAccess();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void loadData(Bundle savedInstanceState, Uri appUri) {
+ // TODO: load this also like other fragment with newInstance arguments?
+ AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri);
+ mSettingsFragment.setAppSettings(settings);
+
+ String appName;
+ PackageManager pm = getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(settings.getPackageName(), 0);
+ appName = (String) pm.getApplicationLabel(ai);
+ } catch (PackageManager.NameNotFoundException e) {
+ // fallback
+ appName = settings.getPackageName();
+ }
+ setTitle(appName);
+
+ Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build();
+ Log.d(Constants.TAG, "accountsUri: " + accountsUri);
+ startListFragment(savedInstanceState, accountsUri);
+ }
+
+ private void startListFragment(Bundle savedInstanceState, Uri dataUri) {
+ // 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;
+ }
+
+ // Create an instance of the fragment
+ mAccountsListFragment = AccountsListFragment.newInstance(dataUri);
+
+ // Add the fragment to the 'fragment_container' FrameLayout
+ // NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.api_accounts_list_fragment, mAccountsListFragment)
+ .commitAllowingStateLoss();
+ // do it immediately!
+ getSupportFragmentManager().executePendingTransactions();
+ }
+
+ private void revokeAccess() {
+ if (getContentResolver().delete(mAppUri, null, null) <= 0) {
+ throw new RuntimeException();
+ }
+ finish();
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java
new file mode 100644
index 000000000..a6db02708
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2013-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.remote.ui;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.spongycastle.util.encoders.Hex;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.remote.AppSettings;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class AppSettingsFragment extends Fragment {
+
+ // model
+ private AppSettings mAppSettings;
+
+ // view
+ private TextView mAppNameView;
+ private ImageView mAppIconView;
+ private TextView mPackageName;
+ private TextView mPackageSignature;
+
+ public AppSettings getAppSettings() {
+ return mAppSettings;
+ }
+
+ public void setAppSettings(AppSettings appSettings) {
+ this.mAppSettings = appSettings;
+ updateView(appSettings);
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
+ mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
+ mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
+ mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
+ mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
+ return view;
+ }
+
+ private void updateView(AppSettings appSettings) {
+ // get application name and icon from package manager
+ String appName;
+ Drawable appIcon = null;
+ PackageManager pm = getActivity().getApplicationContext().getPackageManager();
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(appSettings.getPackageName(), 0);
+
+ appName = (String) pm.getApplicationLabel(ai);
+ appIcon = pm.getApplicationIcon(ai);
+ } catch (NameNotFoundException e) {
+ // fallback
+ appName = appSettings.getPackageName();
+ }
+ mAppNameView.setText(appName);
+ mAppIconView.setImageDrawable(appIcon);
+
+ // advanced info: package name
+ mPackageName.setText(appSettings.getPackageName());
+
+ // advanced info: package signature SHA-256
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(appSettings.getPackageSignature());
+ byte[] digest = md.digest();
+ String signature = new String(Hex.encode(digest));
+
+ mPackageSignature.setText(signature);
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Constants.TAG, "Should not happen!", e);
+ }
+ }
+
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
index f6f216efd..f86d279f0 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java
@@ -15,13 +15,13 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
import android.os.Bundle;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.DrawerActivity;
-public class RegisteredAppsListActivity extends DrawerActivity {
+public class AppsListActivity extends DrawerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
index 25d0c7593..22082e913 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java
@@ -15,10 +15,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
-import android.content.ContentUris;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -26,14 +28,20 @@ import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
+import android.support.v4.widget.CursorAdapter;
+import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
-public class RegisteredAppsListFragment extends ListFragment implements
+public class AppsListFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
// This is the Adapter being used to display the list's data.
@@ -46,9 +54,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
getListView().setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
+ String selectedPackageName = mAdapter.getItemPackageName(position);
// edit app settings
Intent intent = new Intent(getActivity(), AppSettingsActivity.class);
- intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id));
+ intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName));
startActivity(intent);
}
});
@@ -70,7 +79,10 @@ public class RegisteredAppsListFragment extends ListFragment implements
}
// These are the Contacts rows that we will retrieve.
- static final String[] PROJECTION = new String[]{ApiApps._ID, ApiApps.PACKAGE_NAME};
+ static final String[] PROJECTION = new String[]{
+ ApiApps._ID, // 0
+ ApiApps.PACKAGE_NAME // 1
+ };
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
@@ -98,4 +110,65 @@ public class RegisteredAppsListFragment extends ListFragment implements
mAdapter.swapCursor(null);
}
+ private class RegisteredAppsAdapter extends CursorAdapter {
+
+ private LayoutInflater mInflater;
+ private PackageManager mPM;
+
+ public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
+ super(context, c, flags);
+
+ mInflater = LayoutInflater.from(context);
+ mPM = context.getApplicationContext().getPackageManager();
+ }
+
+ /**
+ * Similar to CursorAdapter.getItemId().
+ * Required to build Uris for api app view, which is not based on row ids
+ *
+ * @param position
+ * @return
+ */
+ public String getItemPackageName(int position) {
+ if (mDataValid && mCursor != null) {
+ if (mCursor.moveToPosition(position)) {
+ return mCursor.getString(1);
+ } else {
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
+ ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
+
+ String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
+ if (packageName != null) {
+ // get application name
+ try {
+ ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0);
+
+ text.setText(mPM.getApplicationLabel(ai));
+ icon.setImageDrawable(mPM.getApplicationIcon(ai));
+ } catch (final PackageManager.NameNotFoundException e) {
+ // fallback
+ text.setText(packageName);
+ }
+ } else {
+ // fallback
+ text.setText(packageName);
+ }
+
+ }
+
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
+ }
+ }
+
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index e20114853..307c9c61a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -15,7 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package org.sufficientlysecure.keychain.service.remote;
+package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
import android.os.Bundle;
@@ -24,6 +24,7 @@ import android.os.Message;
import android.os.Messenger;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
+
import org.openintents.openpgp.util.OpenPgpApi;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import org.sufficientlysecure.keychain.Constants;
@@ -31,7 +32,10 @@ import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -41,6 +45,8 @@ import java.util.ArrayList;
public class RemoteServiceActivity extends ActionBarActivity {
public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER";
+ public static final String ACTION_CREATE_ACCOUNT = Constants.INTENT_PREFIX
+ + "API_ACTIVITY_CREATE_ACCOUNT";
public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX
+ "API_ACTIVITY_CACHE_PASSPHRASE";
public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX
@@ -57,6 +63,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
// register action
public static final String EXTRA_PACKAGE_NAME = "package_name";
public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature";
+ // create acc action
+ public static final String EXTRA_ACC_NAME = "acc_name";
// select pub keys action
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
@@ -65,7 +73,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
public static final String EXTRA_ERROR_MESSAGE = "error_message";
// register view
- private AppSettingsFragment mSettingsFragment;
+ private AppSettingsFragment mAppSettingsFragment;
+ // create acc view
+ private AccountSettingsFragment mAccSettingsFragment;
// select pub keys view
private SelectPublicKeyFragment mSelectFragment;
@@ -85,6 +95,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
if (ACTION_REGISTER.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
+ Log.d(Constants.TAG, "ACTION_REGISTER packageName: " + packageName);
// Inflate a "Done"/"Cancel" custom action bar view
ActionBarHelper.setTwoButtonView(getSupportActionBar(),
@@ -94,13 +105,52 @@ public class RemoteServiceActivity extends ActionBarActivity {
public void onClick(View v) {
// Allow
+ ProviderHelper.insertApiApp(RemoteServiceActivity.this,
+ mAppSettingsFragment.getAppSettings());
+
+ // give data through for new service call
+ Intent resultData = extras.getParcelable(EXTRA_DATA);
+ RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ RemoteServiceActivity.this.finish();
+ }
+ }, R.string.api_register_disallow, R.drawable.ic_action_cancel,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Disallow
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
+ }
+ );
+
+ setContentView(R.layout.api_remote_register_app);
+
+ mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_app_settings_fragment);
+
+ AppSettings settings = new AppSettings(packageName, packageSignature);
+ mAppSettingsFragment.setAppSettings(settings);
+ } else if (ACTION_CREATE_ACCOUNT.equals(action)) {
+ final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+ final String accName = extras.getString(EXTRA_ACC_NAME);
+
+ // Inflate a "Done"/"Cancel" custom action bar view
+ ActionBarHelper.setTwoButtonView(getSupportActionBar(),
+ R.string.api_settings_save, R.drawable.ic_action_done,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Save
+
// user needs to select a key!
- if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
- mSettingsFragment.setErrorOnSelectKeyFragment(
+ if (mAccSettingsFragment.getAccSettings().getKeyId() == Id.key.none) {
+ mAccSettingsFragment.setErrorOnSelectKeyFragment(
getString(R.string.api_register_error_select_key));
} else {
- ProviderHelper.insertApiApp(RemoteServiceActivity.this,
- mSettingsFragment.getAppSettings());
+ ProviderHelper.insertApiAccount(RemoteServiceActivity.this,
+ KeychainContract.ApiAccounts.buildBaseUri(packageName),
+ mAccSettingsFragment.getAccSettings());
// give data through for new service call
Intent resultData = extras.getParcelable(EXTRA_DATA);
@@ -108,29 +158,43 @@ public class RemoteServiceActivity extends ActionBarActivity {
RemoteServiceActivity.this.finish();
}
}
- }, R.string.api_register_disallow, R.drawable.ic_action_cancel,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- // Disallow
- RemoteServiceActivity.this.setResult(RESULT_CANCELED);
- RemoteServiceActivity.this.finish();
- }
+ }, R.string.api_settings_cancel, R.drawable.ic_action_cancel,
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // Cancel
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ RemoteServiceActivity.this.finish();
+ }
}
);
- setContentView(R.layout.api_app_register_activity);
+ setContentView(R.layout.api_remote_create_account);
- mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById(
- R.id.api_app_settings_fragment);
+ mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById(
+ R.id.api_account_settings_fragment);
- AppSettings settings = new AppSettings(packageName, packageSignature);
- mSettingsFragment.setAppSettings(settings);
+ AccountSettings settings = new AccountSettings(accName);
+ mAccSettingsFragment.setAccSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
- Intent resultData = extras.getParcelable(EXTRA_DATA);
+ final Intent resultData = extras.getParcelable(EXTRA_DATA);
+
+ PassphraseDialogFragment.show(this, secretKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ // return given params again, for calling the service method again
+ RemoteServiceActivity.this.setResult(RESULT_OK, resultData);
+ } else {
+ RemoteServiceActivity.this.setResult(RESULT_CANCELED);
+ }
+
+ RemoteServiceActivity.this.finish();
+ }
+ });
- showPassphraseDialog(resultData, secretKeyId);
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
ArrayList<String> missingUserIds = intent
@@ -185,7 +249,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
);
- setContentView(R.layout.api_app_select_pub_keys_activity);
+ setContentView(R.layout.api_remote_select_pub_keys);
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
@@ -227,7 +291,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
}
});
- setContentView(R.layout.api_app_error_message);
+ setContentView(R.layout.api_remote_error_message);
// set text on view
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index daaff5d54..1c6aa7971 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -18,30 +18,61 @@
package org.sufficientlysecure.keychain.service;
import android.app.IntentService;
-import android.content.Context;
import android.content.Intent;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import org.spongycastle.openpgp.*;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
+import org.spongycastle.openpgp.PGPKeyRing;
+import org.spongycastle.openpgp.PGPObjectFactory;
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.spongycastle.openpgp.PGPUtil;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
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.pgp.*;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.pgp.PgpImportExport;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
+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.ui.adapter.ImportKeysListEntry;
-import org.sufficientlysecure.keychain.util.*;
-
-import java.io.*;
+import org.sufficientlysecure.keychain.util.HkpKeyServer;
+import org.sufficientlysecure.keychain.util.InputData;
+import org.sufficientlysecure.keychain.util.KeychainServiceListener;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.GregorianCalendar;
import java.util.List;
/**
@@ -84,35 +115,26 @@ public class KeychainIntentService extends IntentService
// possible targets:
public static final int TARGET_BYTES = 1;
public static final int TARGET_URI = 2;
- public static final int TARGET_STREAM = 3;
// encrypt
- public static final String ENCRYPT_SECRET_KEY_ID = "secret_key_id";
+ public static final String ENCRYPT_SIGNATURE_KEY_ID = "secret_key_id";
public static final String ENCRYPT_USE_ASCII_ARMOR = "use_ascii_armor";
public static final String ENCRYPT_ENCRYPTION_KEYS_IDS = "encryption_keys_ids";
public static final String ENCRYPT_COMPRESSION_ID = "compression_id";
- public static final String ENCRYPT_GENERATE_SIGNATURE = "generate_signature";
- public static final String ENCRYPT_SIGN_ONLY = "sign_only";
public static final String ENCRYPT_MESSAGE_BYTES = "message_bytes";
public static final String ENCRYPT_INPUT_FILE = "input_file";
public static final String ENCRYPT_OUTPUT_FILE = "output_file";
- public static final String ENCRYPT_PROVIDER_URI = "provider_uri";
+ public static final String ENCRYPT_SYMMETRIC_PASSPHRASE = "passphrase";
// decrypt/verify
- public static final String DECRYPT_RETURN_BYTES = "return_binary";
public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
- public static final String DECRYPT_ASSUME_SYMMETRIC = "assume_symmetric";
+ public static final String DECRYPT_PASSPHRASE = "passphrase";
// save keyring
- public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase";
- public static final String SAVE_KEYRING_CURRENT_PASSPHRASE = "current_passphrase";
- public static final String SAVE_KEYRING_USER_IDS = "user_ids";
- public static final String SAVE_KEYRING_KEYS = "keys";
- public static final String SAVE_KEYRING_KEYS_USAGES = "keys_usages";
- public static final String SAVE_KEYRING_KEYS_EXPIRY_DATES = "keys_expiry_dates";
- public static final String SAVE_KEYRING_MASTER_KEY_ID = "master_key_id";
+ public static final String SAVE_KEYRING_PARCEL = "save_parcel";
public static final String SAVE_KEYRING_CAN_SIGN = "can_sign";
+
// generate key
public static final String GENERATE_KEY_ALGORITHM = "algorithm";
public static final String GENERATE_KEY_KEY_SIZE = "key_size";
@@ -128,10 +150,9 @@ public class KeychainIntentService extends IntentService
// export key
public static final String EXPORT_OUTPUT_STREAM = "export_output_stream";
public static final String EXPORT_FILENAME = "export_filename";
- public static final String EXPORT_KEY_TYPE = "export_key_type";
+ public static final String EXPORT_SECRET = "export_secret";
public static final String EXPORT_ALL = "export_all";
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
- public static final String EXPORT_KEY_RING_ROW_ID = "export_key_rind_row_id";
// upload key
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
@@ -150,17 +171,12 @@ public class KeychainIntentService extends IntentService
*/
// keys
public static final String RESULT_NEW_KEY = "new_key";
- public static final String RESULT_NEW_KEY2 = "new_key2";
+ public static final String RESULT_KEY_USAGES = "new_key_usages";
// encrypt
- public static final String RESULT_SIGNATURE_BYTES = "signature_data";
- public static final String RESULT_SIGNATURE_STRING = "signature_text";
- public static final String RESULT_ENCRYPTED_STRING = "encrypted_message";
- public static final String RESULT_ENCRYPTED_BYTES = "encrypted_data";
- public static final String RESULT_URI = "result_uri";
+ public static final String RESULT_BYTES = "encrypted_data";
// decrypt/verify
- public static final String RESULT_DECRYPTED_STRING = "decrypted_message";
public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature";
@@ -172,10 +188,6 @@ public class KeychainIntentService extends IntentService
// export
public static final String RESULT_EXPORT = "exported";
- // query
- public static final String RESULT_QUERY_KEY_DATA = "query_key_data";
- public static final String RESULT_QUERY_KEY_SEARCH_RESULT = "query_key_search_result";
-
Messenger mMessenger;
private boolean mIsCanceled;
@@ -225,20 +237,17 @@ public class KeychainIntentService extends IntentService
/* Input */
int target = data.getInt(TARGET);
- long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
- String encryptionPassphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
+ long signatureKeyId = data.getLong(ENCRYPT_SIGNATURE_KEY_ID);
+ String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);
boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR);
long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS);
int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID);
- boolean generateSignature = data.getBoolean(ENCRYPT_GENERATE_SIGNATURE);
- boolean signOnly = data.getBoolean(ENCRYPT_SIGN_ONLY);
-
- InputStream inStream = null;
- long inLength = -1;
- InputData inputData = null;
- OutputStream outStream = null;
- String streamFilename = null;
+ InputStream inStream;
+ long inLength;
+ InputData inputData;
+ OutputStream outStream;
+// String streamFilename = null;
switch (target) {
case TARGET_BYTES: /* encrypting bytes directly */
byte[] bytes = data.getByteArray(ENCRYPT_MESSAGE_BYTES);
@@ -270,29 +279,30 @@ public class KeychainIntentService extends IntentService
break;
- case TARGET_STREAM: /* Encrypting stream from content uri */
- Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
-
- // InputStream
- InputStream in = getContentResolver().openInputStream(providerUri);
- inLength = PgpHelper.getLengthOfStream(in);
- inputData = new InputData(in, inLength);
-
- // OutputStream
- try {
- while (true) {
- streamFilename = PgpHelper.generateRandomFilename(32);
- if (streamFilename == null) {
- throw new PgpGeneralException("couldn't generate random file name");
- }
- openFileInput(streamFilename).close();
- }
- } catch (FileNotFoundException e) {
- // found a name that isn't used yet
- }
- outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
-
- break;
+ // TODO: not used currently
+// case TARGET_STREAM: /* Encrypting stream from content uri */
+// Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
+//
+// // InputStream
+// InputStream in = getContentResolver().openInputStream(providerUri);
+// inLength = PgpHelper.getLengthOfStream(in);
+// inputData = new InputData(in, inLength);
+//
+// // OutputStream
+// try {
+// while (true) {
+// streamFilename = PgpHelper.generateRandomFilename(32);
+// if (streamFilename == null) {
+// throw new PgpGeneralException("couldn't generate random file name");
+// }
+// openFileInput(streamFilename).close();
+// }
+// } catch (FileNotFoundException e) {
+// // found a name that isn't used yet
+// }
+// outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
+//
+// break;
default:
throw new PgpGeneralException("No target choosen!");
@@ -304,45 +314,20 @@ public class KeychainIntentService extends IntentService
new PgpSignEncrypt.Builder(this, inputData, outStream);
builder.progress(this);
- if (generateSignature) {
- Log.d(Constants.TAG, "generating signature...");
- builder.enableAsciiArmorOutput(useAsciiArmor)
- .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
- .signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
-
- builder.build().generateSignature();
- } else if (signOnly) {
- Log.d(Constants.TAG, "sign only...");
- builder.enableAsciiArmorOutput(useAsciiArmor)
- .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
- .signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
-
- builder.build().execute();
- } else {
- Log.d(Constants.TAG, "encrypt...");
- builder.enableAsciiArmorOutput(useAsciiArmor)
- .compressionId(compressionId)
- .symmetricEncryptionAlgorithm(
- Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
- .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
- .encryptionKeyIds(encryptionKeyIds)
- .encryptionPassphrase(encryptionPassphrase)
- .signatureKeyId(secretKeyId)
- .signatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .signaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
-
- builder.build().execute();
- }
+ builder.enableAsciiArmorOutput(useAsciiArmor)
+ .compressionId(compressionId)
+ .symmetricEncryptionAlgorithm(
+ Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
+ .signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
+ .encryptionKeyIds(encryptionKeyIds)
+ .symmetricPassphrase(symmetricPassphrase)
+ .signatureKeyId(signatureKeyId)
+ .signatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .signaturePassphrase(
+ PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));
+
+ builder.build().execute();
outStream.close();
@@ -352,33 +337,20 @@ public class KeychainIntentService extends IntentService
switch (target) {
case TARGET_BYTES:
- if (useAsciiArmor) {
- String output = new String(
- ((ByteArrayOutputStream) outStream).toByteArray());
- if (generateSignature) {
- resultData.putString(RESULT_SIGNATURE_STRING, output);
- } else {
- resultData.putString(RESULT_ENCRYPTED_STRING, output);
- }
- } else {
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
- if (generateSignature) {
- resultData.putByteArray(RESULT_SIGNATURE_BYTES, output);
- } else {
- resultData.putByteArray(RESULT_ENCRYPTED_BYTES, output);
- }
- }
+ byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
+
+ resultData.putByteArray(RESULT_BYTES, output);
break;
case TARGET_URI:
// nothing, file was written, just send okay
break;
- case TARGET_STREAM:
- String uri = DataStream.buildDataStreamUri(streamFilename).toString();
- resultData.putString(RESULT_URI, uri);
-
- break;
+// case TARGET_STREAM:
+// String uri = DataStream.buildDataStreamUri(streamFilename).toString();
+// resultData.putString(RESULT_URI, uri);
+//
+// break;
}
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -392,15 +364,13 @@ public class KeychainIntentService extends IntentService
/* Input */
int target = data.getInt(TARGET);
- long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
- boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES);
- boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC);
+ String passphrase = data.getString(DECRYPT_PASSPHRASE);
- InputStream inStream = null;
- long inLength = -1;
- InputData inputData = null;
- OutputStream outStream = null;
+ InputStream inStream;
+ long inLength;
+ InputData inputData;
+ OutputStream outStream;
String streamFilename = null;
switch (target) {
case TARGET_BYTES: /* decrypting bytes directly */
@@ -435,29 +405,30 @@ public class KeychainIntentService extends IntentService
break;
- case TARGET_STREAM: /* decrypting stream from content uri */
- Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
-
- // InputStream
- InputStream in = getContentResolver().openInputStream(providerUri);
- inLength = PgpHelper.getLengthOfStream(in);
- inputData = new InputData(in, inLength);
-
- // OutputStream
- try {
- while (true) {
- streamFilename = PgpHelper.generateRandomFilename(32);
- if (streamFilename == null) {
- throw new PgpGeneralException("couldn't generate random file name");
- }
- openFileInput(streamFilename).close();
- }
- } catch (FileNotFoundException e) {
- // found a name that isn't used yet
- }
- outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
-
- break;
+ // TODO: not used, maybe contains code useful for new decrypt method for files?
+// case TARGET_STREAM: /* decrypting stream from content uri */
+// Uri providerUri = (Uri) data.getParcelable(ENCRYPT_PROVIDER_URI);
+//
+// // InputStream
+// InputStream in = getContentResolver().openInputStream(providerUri);
+// inLength = PgpHelper.getLengthOfStream(in);
+// inputData = new InputData(in, inLength);
+//
+// // OutputStream
+// try {
+// while (true) {
+// streamFilename = PgpHelper.generateRandomFilename(32);
+// if (streamFilename == null) {
+// throw new PgpGeneralException("couldn't generate random file name");
+// }
+// openFileInput(streamFilename).close();
+// }
+// } catch (FileNotFoundException e) {
+// // found a name that isn't used yet
+// }
+// outStream = openFileOutput(streamFilename, Context.MODE_PRIVATE);
+//
+// break;
default:
throw new PgpGeneralException("No target choosen!");
@@ -473,8 +444,8 @@ public class KeychainIntentService extends IntentService
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
builder.progressDialogUpdater(this);
- builder.assumeSymmetric(assumeSymmetricEncryption)
- .passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
+ builder.allowSymmetricDecryption(true)
+ .passphrase(passphrase);
PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();
@@ -486,25 +457,18 @@ public class KeychainIntentService extends IntentService
switch (target) {
case TARGET_BYTES:
- if (returnBytes) {
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
- resultData.putByteArray(RESULT_DECRYPTED_BYTES, output);
- } else {
- String output = new String(
- ((ByteArrayOutputStream) outStream).toByteArray());
- resultData.putString(RESULT_DECRYPTED_STRING, output);
- }
-
+ byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
+ resultData.putByteArray(RESULT_DECRYPTED_BYTES, output);
break;
case TARGET_URI:
// nothing, file was written, just send okay and verification bundle
break;
- case TARGET_STREAM:
- String uri = DataStream.buildDataStreamUri(streamFilename).toString();
- resultData.putString(RESULT_URI, uri);
-
- break;
+// case TARGET_STREAM:
+// String uri = DataStream.buildDataStreamUri(streamFilename).toString();
+// resultData.putString(RESULT_URI, uri);
+//
+// break;
}
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -516,38 +480,42 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_SAVE_KEYRING.equals(action)) {
try {
/* Input */
- String oldPassPhrase = data.getString(SAVE_KEYRING_CURRENT_PASSPHRASE);
- String newPassPhrase = data.getString(SAVE_KEYRING_NEW_PASSPHRASE);
+ SaveKeyringParcel saveParams = data.getParcelable(SAVE_KEYRING_PARCEL);
+ String oldPassphrase = saveParams.oldPassphrase;
+ String newPassphrase = saveParams.newPassphrase;
boolean canSign = true;
if (data.containsKey(SAVE_KEYRING_CAN_SIGN)) {
canSign = data.getBoolean(SAVE_KEYRING_CAN_SIGN);
}
- if (newPassPhrase == null) {
- newPassPhrase = oldPassPhrase;
+ if (newPassphrase == null) {
+ newPassphrase = oldPassphrase;
}
- ArrayList<String> userIds = data.getStringArrayList(SAVE_KEYRING_USER_IDS);
- ArrayList<PGPSecretKey> keys = PgpConversionHelper.BytesToPGPSecretKeyList(data
- .getByteArray(SAVE_KEYRING_KEYS));
- ArrayList<Integer> keysUsages = data.getIntegerArrayList(SAVE_KEYRING_KEYS_USAGES);
- ArrayList<GregorianCalendar> keysExpiryDates =
- (ArrayList<GregorianCalendar>) data.getSerializable(SAVE_KEYRING_KEYS_EXPIRY_DATES);
- long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID);
+ long masterKeyId = saveParams.keys.get(0).getKeyID();
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
/* Operation */
if (!canSign) {
- keyOperations.changeSecretKeyPassphrase(
- ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId),
- oldPassPhrase, newPassPhrase);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRing(this, masterKeyId);
+ keyRing = keyOperations.changeSecretKeyPassphrase(keyRing,
+ oldPassphrase, newPassphrase);
+ setProgress(R.string.progress_saving_key_ring, 50, 100);
+ ProviderHelper.saveKeyRing(this, keyRing);
+ setProgress(R.string.progress_done, 100, 100);
} else {
- PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId);
- keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates,
- pubkey, oldPassPhrase, newPassPhrase);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
+ PGPSecretKeyRing privkey = ProviderHelper.getPGPSecretKeyRing(this, masterKeyId);
+ PGPPublicKeyRing pubkey = ProviderHelper.getPGPPublicKeyRing(this, masterKeyId);
+ PgpKeyOperation.Pair<PGPSecretKeyRing,PGPPublicKeyRing> pair =
+ keyOperations.buildSecretKey(privkey, pubkey, saveParams);
+ setProgress(R.string.progress_saving_key_ring, 90, 100);
+ // save the pair
+ ProviderHelper.saveKeyRing(this, pair.second, pair.first);
+ setProgress(R.string.progress_done, 100, 100);
}
- PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase);
+ PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);
/* Output */
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
@@ -563,7 +531,7 @@ public class KeychainIntentService extends IntentService
boolean masterKey = data.getBoolean(GENERATE_KEY_MASTER_KEY);
/* Operation */
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey newKey = keyOperations.createKey(algorithm, keysize,
passphrase, masterKey);
@@ -583,24 +551,37 @@ public class KeychainIntentService extends IntentService
try {
/* Input */
String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
+ ArrayList<PGPSecretKey> newKeys = new ArrayList<PGPSecretKey>();
+ ArrayList<Integer> keyUsageList = new ArrayList<Integer>();
/* Operation */
- int keysTotal = 2;
+ int keysTotal = 3;
int keysCreated = 0;
setProgress(
getApplicationContext().getResources().
getQuantityString(R.plurals.progress_generating, keysTotal),
keysCreated,
keysTotal);
- PgpKeyOperation keyOperations = new PgpKeyOperation(this, this);
+ PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
PGPSecretKey masterKey = keyOperations.createKey(Id.choice.algorithm.rsa,
4096, passphrase, true);
+ newKeys.add(masterKey);
+ keyUsageList.add(KeyFlags.CERTIFY_OTHER);
keysCreated++;
setProgress(keysCreated, keysTotal);
PGPSecretKey subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
4096, passphrase, false);
+ newKeys.add(subKey);
+ keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
+ keysCreated++;
+ setProgress(keysCreated, keysTotal);
+
+ subKey = keyOperations.createKey(Id.choice.algorithm.rsa,
+ 4096, passphrase, false);
+ newKeys.add(subKey);
+ keyUsageList.add(KeyFlags.SIGN_DATA);
keysCreated++;
setProgress(keysCreated, keysTotal);
@@ -608,11 +589,11 @@ public class KeychainIntentService extends IntentService
// for sign
/* Output */
+
Bundle resultData = new Bundle();
resultData.putByteArray(RESULT_NEW_KEY,
- PgpConversionHelper.PGPSecretKeyToBytes(masterKey));
- resultData.putByteArray(RESULT_NEW_KEY2,
- PgpConversionHelper.PGPSecretKeyToBytes(subKey));
+ PgpConversionHelper.PGPSecretKeyArrayListToBytes(newKeys));
+ resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);
OtherHelper.logDebugBundle(resultData, "resultData");
@@ -657,53 +638,50 @@ public class KeychainIntentService extends IntentService
} else if (ACTION_EXPORT_KEYRING.equals(action)) {
try {
- /* Input */
- int keyType = Id.type.public_key;
- if (data.containsKey(EXPORT_KEY_TYPE)) {
- keyType = data.getInt(EXPORT_KEY_TYPE);
- }
-
+ boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
+ long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
String outputFile = data.getString(EXPORT_FILENAME);
- long[] rowIds = new long[0];
-
- // If not exporting all keys get the rowIds of the keys to export from the intent
+ // If not exporting all keys get the masterKeyIds of the keys to export from the intent
boolean exportAll = data.getBoolean(EXPORT_ALL);
- if (!exportAll) {
- rowIds = data.getLongArray(EXPORT_KEY_RING_ROW_ID);
- }
-
- /* Operation */
// check if storage is ready
if (!FileHelper.isStorageMounted(outputFile)) {
throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
}
- // OutputStream
- FileOutputStream outStream = new FileOutputStream(outputFile);
-
- ArrayList<Long> keyRingRowIds = new ArrayList<Long>();
- if (exportAll) {
+ ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>();
+ ArrayList<Long> secretMasterKeyIds = new ArrayList<Long>();
- // get all key ring row ids based on export type
- if (keyType == Id.type.public_key) {
- keyRingRowIds = ProviderHelper.getPublicKeyRingsRowIds(this);
- } else {
- keyRingRowIds = ProviderHelper.getSecretKeyRingsRowIds(this);
- }
- } else {
- for (long rowId : rowIds) {
- keyRingRowIds.add(rowId);
+ String selection = null;
+ if(!exportAll) {
+ selection = KeychainDatabase.Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN( ";
+ for(long l : masterKeyIds) {
+ selection += Long.toString(l) + ",";
}
+ selection = selection.substring(0, selection.length()-1) + " )";
}
- Bundle resultData;
+ Cursor cursor = getContentResolver().query(KeyRings.buildUnifiedKeyRingsUri(),
+ new String[]{ KeyRings.MASTER_KEY_ID, KeyRings.HAS_SECRET },
+ selection, null, null);
+ try {
+ cursor.moveToFirst();
+ do {
+ // export public either way
+ publicMasterKeyIds.add(cursor.getLong(0));
+ // add secret if available (and requested)
+ if(exportSecret && cursor.getInt(1) != 0)
+ secretMasterKeyIds.add(cursor.getLong(0));
+ } while(cursor.moveToNext());
+ } finally {
+ cursor.close();
+ }
PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);
-
- resultData = pgpImportExport
- .exportKeyRings(keyRingRowIds, keyType, outStream);
+ Bundle resultData = pgpImportExport
+ .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds,
+ new FileOutputStream(outputFile));
if (mIsCanceled) {
boolean isDeleted = new File(outputFile).delete();
@@ -747,45 +725,54 @@ public class KeychainIntentService extends IntentService
HkpKeyServer server = new HkpKeyServer(keyServer);
for (ImportKeysListEntry entry : entries) {
- byte[] downloadedKey = server.get(entry.getKeyId()).getBytes();
-
- /**
- * TODO: copied from ImportKeysListLoader
- *
- *
- * this parses the downloaded key
- */
- // need to have access to the bufferedInput, so we can reuse it for the possible
- // PGPObject chunks after the first one, e.g. files with several consecutive ASCII
- // armor blocks
+ // if available use complete fingerprint for get request
+ byte[] downloadedKeyBytes;
+ if (entry.getFingerPrintHex() != null) {
+ downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes();
+ } else {
+ downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
+ }
+
+ // create PGPKeyRing object based on downloaded armored key
+ PGPKeyRing downloadedKey = null;
BufferedInputStream bufferedInput =
- new BufferedInputStream(new ByteArrayInputStream(downloadedKey));
- try {
-
- // read all available blocks... (asc files can contain many blocks with BEGIN END)
- while (bufferedInput.available() > 0) {
- InputStream in = PGPUtil.getDecoderStream(bufferedInput);
- PGPObjectFactory objectFactory = new PGPObjectFactory(in);
-
- // go through all objects in this block
- Object obj;
- while ((obj = objectFactory.nextObject()) != null) {
- Log.d(Constants.TAG, "Found class: " + obj.getClass());
-
- if (obj instanceof PGPKeyRing) {
- PGPKeyRing newKeyring = (PGPKeyRing) obj;
-
- entry.setBytes(newKeyring.getEncoded());
- } else {
- Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!");
- }
+ new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
+ 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) {
+ Log.d(Constants.TAG, "Found class: " + obj.getClass());
+
+ if (obj instanceof PGPKeyRing) {
+ downloadedKey = (PGPKeyRing) obj;
+ } else {
+ throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
}
}
- } catch (Exception e) {
- Log.e(Constants.TAG, "Exception on parsing key file!", e);
}
+
+ // verify downloaded key by comparing fingerprints
+ if (entry.getFingerPrintHex() != null) {
+ String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(
+ downloadedKey.getPublicKey().getFingerprint());
+ if (downloadedKeyFp.equals(entry.getFingerPrintHex())) {
+ Log.d(Constants.TAG, "fingerprint of downloaded key is the same as " +
+ "the requested fingerprint!");
+ } else {
+ throw new PgpGeneralException("fingerprint of downloaded key is " +
+ "NOT the same as the requested fingerprint!");
+ }
+ }
+
+ // save key bytes in entry object for doing the
+ // actual import afterwards
+ entry.setBytes(downloadedKey.getEncoded());
}
+
Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle();
@@ -809,16 +796,24 @@ public class KeychainIntentService extends IntentService
ArrayList<String> userIds = data.getStringArrayList(CERTIFY_KEY_UIDS);
/* Operation */
- String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
+ String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this,
masterKeyId);
+ if (signaturePassphrase == null) {
+ throw new PgpGeneralException("Unable to obtain passphrase");
+ }
- PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
- PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
- userIds, signaturePassPhrase);
+ PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
+ PGPPublicKeyRing publicRing = ProviderHelper.getPGPPublicKeyRing(this, pubKeyId);
+ PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
+ PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(this,
+ masterKeyId);
+ publicKey = keyOperation.certifyKey(certificationKey, publicKey,
+ userIds, signaturePassphrase);
+ publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
// store the signed key in our local cache
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
- int retval = pgpImportExport.storeKeyRingInCache(signedPubKeyRing);
+ int retval = pgpImportExport.storeKeyRingInCache(publicRing);
if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
throw new PgpGeneralException("Failed to store signed key in local cache");
}
@@ -835,6 +830,10 @@ public class KeychainIntentService extends IntentService
if (this.mIsCanceled) {
return;
}
+ // contextualize the exception, if necessary
+ if (e instanceof PgpGeneralMsgIdException) {
+ e = ((PgpGeneralMsgIdException) e).getContextualized(this);
+ }
Log.e(Constants.TAG, "ApgService Exception: ", e);
e.printStackTrace();
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 176d09c1a..962b304c7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -24,19 +24,29 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.*;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.support.v4.util.LongSparseArray;
import android.util.Log;
-import android.util.LongSparseArray;
+
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import java.util.Date;
@@ -86,7 +96,7 @@ public class PassphraseCacheService extends Service {
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_ADD);
- intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassPhraseCacheTtl());
+ intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassphraseCacheTtl());
intent.putExtra(EXTRA_PASSPHRASE, passphrase);
intent.putExtra(EXTRA_KEY_ID, keyId);
@@ -161,15 +171,11 @@ public class PassphraseCacheService extends Service {
// try to get master key id which is used as an identifier for cached passphrases
long masterKeyId = keyId;
if (masterKeyId != Id.key.symmetric) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(this, keyId);
- if (keyRing == null) {
+ masterKeyId = ProviderHelper.getMasterKeyId(this,
+ KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)));
+ // Failure
+ if(masterKeyId == 0)
return null;
- }
- PGPSecretKey masterKey = PgpKeyHelper.getMasterKey(keyRing);
- if (masterKey == null) {
- return null;
- }
- masterKeyId = masterKey.getKeyID();
}
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
@@ -202,8 +208,7 @@ public class PassphraseCacheService extends Service {
public static boolean hasPassphrase(Context context, long secretKeyId) {
// check if the key has no passphrase
try {
- PGPSecretKeyRing secRing = ProviderHelper
- .getPGPSecretKeyRingByKeyId(context, secretKeyId);
+ PGPSecretKeyRing secRing = ProviderHelper.getPGPSecretKeyRing(context, secretKeyId);
PGPSecretKey secretKey = null;
boolean foundValidKey = false;
for (Iterator keys = secRing.getSecretKeys(); keys.hasNext(); ) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
new file mode 100644
index 000000000..7c2dcf2c1
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 Ash Hughes <ashes-iontach@hotmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+
+public class SaveKeyringParcel implements Parcelable {
+
+ public ArrayList<String> userIDs;
+ public ArrayList<String> originalIDs;
+ public ArrayList<String> deletedIDs;
+ public boolean[] newIDs;
+ public boolean primaryIDChanged;
+ public boolean[] moddedKeys;
+ public ArrayList<PGPSecretKey> deletedKeys;
+ public ArrayList<GregorianCalendar> keysExpiryDates;
+ public ArrayList<Integer> keysUsages;
+ public String newPassphrase;
+ public String oldPassphrase;
+ public boolean[] newKeys;
+ public ArrayList<PGPSecretKey> keys;
+ public String originalPrimaryID;
+
+ public SaveKeyringParcel() {}
+
+ private SaveKeyringParcel(Parcel source) {
+ userIDs = (ArrayList<String>) source.readSerializable();
+ originalIDs = (ArrayList<String>) source.readSerializable();
+ deletedIDs = (ArrayList<String>) source.readSerializable();
+ newIDs = source.createBooleanArray();
+ primaryIDChanged = source.readByte() != 0;
+ moddedKeys = source.createBooleanArray();
+ byte[] tmp = source.createByteArray();
+ if (tmp == null) {
+ deletedKeys = null;
+ } else {
+ deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp);
+ }
+ keysExpiryDates = (ArrayList<GregorianCalendar>) source.readSerializable();
+ keysUsages = source.readArrayList(Integer.class.getClassLoader());
+ newPassphrase = source.readString();
+ oldPassphrase = source.readString();
+ newKeys = source.createBooleanArray();
+ keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray());
+ originalPrimaryID = source.readString();
+ }
+
+ @Override
+ public void writeToParcel(Parcel destination, int flags) {
+ destination.writeSerializable(userIDs); //might not be the best method to store.
+ destination.writeSerializable(originalIDs);
+ destination.writeSerializable(deletedIDs);
+ destination.writeBooleanArray(newIDs);
+ destination.writeByte((byte) (primaryIDChanged ? 1 : 0));
+ destination.writeBooleanArray(moddedKeys);
+ byte[] tmp = null;
+ if (deletedKeys.size() != 0) {
+ tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys);
+ }
+ destination.writeByteArray(tmp);
+ destination.writeSerializable(keysExpiryDates);
+ destination.writeList(keysUsages);
+ destination.writeString(newPassphrase);
+ destination.writeString(oldPassphrase);
+ destination.writeBooleanArray(newKeys);
+ destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
+ destination.writeString(originalPrimaryID);
+ }
+
+ public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
+ public SaveKeyringParcel createFromParcel(final Parcel source) {
+ return new SaveKeyringParcel(source);
+ }
+
+ public SaveKeyringParcel[] newArray(final int size) {
+ return new SaveKeyringParcel[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
deleted file mode 100644
index 837295018..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Copyright (C) 2013-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.service.remote;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.widget.*;
-import android.widget.AdapterView.OnItemSelectedListener;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.util.encoders.Hex;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
-import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
-import org.sufficientlysecure.keychain.util.AlgorithmNames;
-import org.sufficientlysecure.keychain.util.Log;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-public class AppSettingsFragment extends Fragment implements
- SelectSecretKeyLayoutFragment.SelectSecretKeyCallback {
-
- // model
- private AppSettings mAppSettings;
-
- // view
- private LinearLayout mAdvancedSettingsContainer;
- private BootstrapButton mAdvancedSettingsButton;
- private TextView mAppNameView;
- private ImageView mAppIconView;
- private Spinner mEncryptionAlgorithm;
- private Spinner mHashAlgorithm;
- private Spinner mCompression;
- private TextView mPackageName;
- private TextView mPackageSignature;
-
- private SelectSecretKeyLayoutFragment mSelectKeyFragment;
-
- KeyValueSpinnerAdapter mEncryptionAdapter;
- KeyValueSpinnerAdapter mHashAdapter;
- KeyValueSpinnerAdapter mCompressionAdapter;
-
- public AppSettings getAppSettings() {
- return mAppSettings;
- }
-
- public void setAppSettings(AppSettings appSettings) {
- this.mAppSettings = appSettings;
- setPackage(appSettings.getPackageName());
- mPackageName.setText(appSettings.getPackageName());
-
- try {
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- md.update(appSettings.getPackageSignature());
- byte[] digest = md.digest();
- String signature = new String(Hex.encode(digest));
-
- mPackageSignature.setText(signature);
- } catch (NoSuchAlgorithmException e) {
- Log.e(Constants.TAG, "Should not happen!", e);
- }
-
- mSelectKeyFragment.selectKey(appSettings.getKeyId());
- mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(appSettings
- .getEncryptionAlgorithm()));
- mHashAlgorithm.setSelection(mHashAdapter.getPosition(appSettings.getHashAlgorithm()));
- mCompression.setSelection(mCompressionAdapter.getPosition(appSettings.getCompression()));
- }
-
- /**
- * Inflate the layout for this fragment
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false);
- initView(view);
- return view;
- }
-
- /**
- * Set error String on key selection
- *
- * @param error
- */
- public void setErrorOnSelectKeyFragment(String error) {
- mSelectKeyFragment.setError(error);
- }
-
- private void initView(View view) {
- mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
- R.id.api_app_settings_select_key_fragment);
- mSelectKeyFragment.setCallback(this);
-
- mAdvancedSettingsButton = (BootstrapButton) view
- .findViewById(R.id.api_app_settings_advanced_button);
- mAdvancedSettingsContainer = (LinearLayout) view
- .findViewById(R.id.api_app_settings_advanced);
-
- mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name);
- mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon);
- mEncryptionAlgorithm = (Spinner) view
- .findViewById(R.id.api_app_settings_encryption_algorithm);
- mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm);
- mCompression = (Spinner) view.findViewById(R.id.api_app_settings_compression);
- mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name);
- mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature);
-
- AlgorithmNames algorithmNames = new AlgorithmNames(getActivity());
-
- mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(),
- algorithmNames.getEncryptionNames());
- mEncryptionAlgorithm.setAdapter(mEncryptionAdapter);
- mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mAppSettings.setEncryptionAlgorithm((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames());
- mHashAlgorithm.setAdapter(mHashAdapter);
- mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mAppSettings.setHashAlgorithm((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(),
- algorithmNames.getCompressionNames());
- mCompression.setAdapter(mCompressionAdapter);
- mCompression.setOnItemSelectedListener(new OnItemSelectedListener() {
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mAppSettings.setCompression((int) id);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- }
- });
-
- final Animation visibleAnimation = new AlphaAnimation(0.0f, 1.0f);
- visibleAnimation.setDuration(250);
- final Animation invisibleAnimation = new AlphaAnimation(1.0f, 0.0f);
- invisibleAnimation.setDuration(250);
-
- // TODO: Better: collapse/expand animation
- // final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
- // Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
- // Animation.RELATIVE_TO_SELF, 0.0f);u
- // animation2.setDuration(150);
-
- mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) {
- mAdvancedSettingsContainer.startAnimation(invisibleAnimation);
- mAdvancedSettingsContainer.setVisibility(View.GONE);
- mAdvancedSettingsButton.setText(getString(R.string.api_settings_show_advanced));
- mAdvancedSettingsButton.setLeftIcon("fa-caret-up");
- } else {
- mAdvancedSettingsContainer.startAnimation(visibleAnimation);
- mAdvancedSettingsContainer.setVisibility(View.VISIBLE);
- mAdvancedSettingsButton.setText(getString(R.string.api_settings_hide_advanced));
- mAdvancedSettingsButton.setLeftIcon("fa-caret-down");
- }
- }
- });
- }
-
- private void setPackage(String packageName) {
- PackageManager pm = getActivity().getApplicationContext().getPackageManager();
-
- // get application name and icon from package manager
- String appName = null;
- Drawable appIcon = null;
- try {
- ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);
-
- appName = (String) pm.getApplicationLabel(ai);
- appIcon = pm.getApplicationIcon(ai);
- } catch (final NameNotFoundException e) {
- // fallback
- appName = packageName;
- }
- mAppNameView.setText(appName);
- mAppIconView.setImageDrawable(appIcon);
- }
-
- /**
- * callback from select secret key fragment
- */
- @Override
- public void onKeySelected(long secretKeyId) {
- mAppSettings.setKeyId(secretKeyId);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java
deleted file mode 100644
index e0dc4162f..000000000
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package org.sufficientlysecure.keychain.service.remote;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.Cursor;
-import android.support.v4.widget.CursorAdapter;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
-
-public class RegisteredAppsAdapter extends CursorAdapter {
-
- private LayoutInflater mInflater;
- private PackageManager mPM;
-
- public RegisteredAppsAdapter(Context context, Cursor c, int flags) {
- super(context, c, flags);
-
- mInflater = LayoutInflater.from(context);
- mPM = context.getApplicationContext().getPackageManager();
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name);
- ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon);
-
- String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME));
- if (packageName != null) {
- // get application name
- try {
- ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0);
-
- text.setText(mPM.getApplicationLabel(ai));
- icon.setImageDrawable(mPM.getApplicationIcon(ai));
- } catch (final NameNotFoundException e) {
- // fallback
- text.setText(packageName);
- }
- } else {
- // fallback
- text.setText(packageName);
- }
-
- }
-
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.api_apps_adapter_list_item, null);
- }
-
-}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index f2e6c4bd9..2efa8a69c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2011 Senecaso
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -32,16 +32,20 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.*;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ListView;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.spongycastle.openpgp.PGPPublicKeyRing;
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.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -140,8 +144,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
Log.e(Constants.TAG, "uri: " + mDataUri);
- PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, mDataUri);
-
mUserIds = (ListView) findViewById(R.id.user_ids);
mUserIdsAdapter = new ViewKeyUserIdsAdapter(this, null, 0, true);
@@ -150,20 +152,12 @@ public class CertifyKeyActivity extends ActionBarActivity implements
getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
- if (signKey != null) {
- mPubKeyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
- }
- if (mPubKeyId == 0) {
- Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
- finish();
- return;
- }
}
static final String[] KEYRING_PROJECTION =
new String[] {
KeychainContract.KeyRings._ID,
- KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.Keys.MASTER_KEY_ID,
KeychainContract.Keys.FINGERPRINT,
KeychainContract.UserIds.USER_ID,
};
@@ -184,11 +178,13 @@ public class CertifyKeyActivity extends ActionBarActivity implements
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch(id) {
- case LOADER_ID_KEYRING:
- return new CursorLoader(this, mDataUri, KEYRING_PROJECTION, null, null, null);
+ case LOADER_ID_KEYRING: {
+ Uri uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(this, uri, KEYRING_PROJECTION, null, null, null);
+ }
case LOADER_ID_USER_IDS: {
- Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
- return new CursorLoader(this, baseUri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER);
+ Uri uri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
+ return new CursorLoader(this, uri, USER_IDS_PROJECTION, null, null, USER_IDS_SORT_ORDER);
}
}
return null;
@@ -200,20 +196,18 @@ public class CertifyKeyActivity extends ActionBarActivity implements
case LOADER_ID_KEYRING:
// the first key here is our master key
if (data.moveToFirst()) {
- long keyId = data.getLong(INDEX_MASTER_KEY_ID);
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
+ // TODO: put findViewById in onCreate!
+ mPubKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(mPubKeyId);
((TextView) findViewById(R.id.key_id)).setText(keyIdStr);
String mainUserId = data.getString(INDEX_USER_ID);
((TextView) findViewById(R.id.main_user_id)).setText(mainUserId);
byte[] fingerprintBlob = data.getBlob(INDEX_FINGERPRINT);
- if (fingerprintBlob == null) {
- // FALLBACK for old database entries
- fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri);
- }
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
- ((TextView) findViewById(R.id.fingerprint)).setText(OtherHelper.colorizeFingerprint(fingerprint));
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
+ ((TextView) findViewById(R.id.fingerprint))
+ .setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
}
break;
case LOADER_ID_USER_IDS:
@@ -231,37 +225,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
}
}
- private void showPassphraseDialog(final long secretKeyId) {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- startSigning();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
- messenger, secretKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException e) {
- Log.d(Constants.TAG, "No passphrase for this secret key!");
- // send message to handler to start certification directly
- returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
- }
- }
-
/**
* handles the UI bits of the signing process on the UI thread
*/
private void initiateSigning() {
- PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId);
+ PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRing(this, mPubKeyId);
if (pubring != null) {
// if we have already signed this key, dont bother doing it again
boolean alreadySigned = false;
@@ -284,7 +252,15 @@ public class CertifyKeyActivity extends ActionBarActivity implements
*/
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
if (passphrase == null) {
- showPassphraseDialog(mMasterKeyId);
+ PassphraseDialogFragment.show(this, mMasterKeyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ startSigning();
+ }
+ }
+ });
// bail out; need to wait until the user has entered the passphrase before trying again
return;
} else {
@@ -307,7 +283,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
// Bail out if there is not at least one user id selected
ArrayList<String> userIds = mUserIdsAdapter.getSelectedUserIds();
- if(userIds.isEmpty()) {
+ if (userIds.isEmpty()) {
Toast.makeText(CertifyKeyActivity.this, "No User IDs to sign selected!",
Toast.LENGTH_SHORT).show();
return;
@@ -327,11 +303,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after signing is done in ApgService
+ // Message is received after signing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -380,11 +356,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after uploading is done in ApgService
+ // Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
index 3e389c034..8533e9072 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,180 +17,48 @@
package org.sufficientlysecure.keychain.ui;
-import android.annotation.SuppressLint;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.animation.AnimationUtils;
-import android.widget.*;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.devspark.appmsg.AppMsg;
-import org.openintents.openpgp.OpenPgpSignatureResult;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.widget.Toast;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.*;
import java.util.regex.Matcher;
-@SuppressLint("NewApi")
public class DecryptActivity extends DrawerActivity {
/* Intents */
- // without permission
public static final String ACTION_DECRYPT = Constants.INTENT_PREFIX + "DECRYPT";
/* EXTRA keys for input */
public static final String EXTRA_TEXT = "text";
- private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
- private static final int RESULT_CODE_FILE = 0x00007003;
-
- private long mSignatureKeyId = 0;
-
- private boolean mReturnResult = false;
-
- // TODO: replace signed only checks with something more intelligent
- // PgpDecryptVerify should handle all automatically!!!
- private boolean mSignedOnly = false;
- private boolean mAssumeSymmetricEncryption = false;
-
- private EditText mMessage = null;
- private RelativeLayout mSignatureLayout = null;
- private ImageView mSignatureStatusImage = null;
- private TextView mUserId = null;
- private TextView mUserIdRest = null;
-
- private ViewFlipper mSource = null;
- private TextView mSourceLabel = null;
- private ImageView mSourcePrevious = null;
- private ImageView mSourceNext = null;
-
- private int mDecryptTarget;
-
- private EditText mFilename = null;
- private CheckBox mDeleteAfter = null;
- private BootstrapButton mBrowse = null;
- private BootstrapButton mLookupKey = null;
-
- private String mInputFilename = null;
- private String mOutputFilename = null;
-
- private Uri mContentUri = null;
- private boolean mReturnBinary = false;
-
- private long mSecretKeyId = Id.key.none;
+ ViewPager mViewPager;
+ PagerTabStrip mPagerTabStrip;
+ PagerTabStripAdapter mTabsAdapter;
- private FileDialogFragment mFileDialog;
+ Bundle mMessageFragmentBundle = new Bundle();
+ Bundle mFileFragmentBundle = new Bundle();
+ int mSwitchToTab = PAGER_TAB_MESSAGE;
- private boolean mDecryptImmediately = false;
-
- private BootstrapButton mDecryptButton;
+ private static final int PAGER_TAB_MESSAGE = 0;
+ private static final int PAGER_TAB_FILE = 1;
private void initView() {
- mSource = (ViewFlipper) findViewById(R.id.source);
- mSourceLabel = (TextView) findViewById(R.id.sourceLabel);
- mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious);
- mSourceNext = (ImageView) findViewById(R.id.sourceNext);
-
- mSourcePrevious.setClickable(true);
- mSourcePrevious.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_right_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_right_out));
- mSource.showPrevious();
- updateSource();
- }
- });
-
- mSourceNext.setClickable(true);
- OnClickListener nextSourceClickListener = new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_left_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(DecryptActivity.this,
- R.anim.push_left_out));
- mSource.showNext();
- updateSource();
- }
- };
- mSourceNext.setOnClickListener(nextSourceClickListener);
-
- mSourceLabel.setClickable(true);
- mSourceLabel.setOnClickListener(nextSourceClickListener);
+ mViewPager = (ViewPager) findViewById(R.id.decrypt_pager);
+ mPagerTabStrip = (PagerTabStrip) findViewById(R.id.decrypt_pager_tab_strip);
- mMessage = (EditText) findViewById(R.id.message);
- mSignatureLayout = (RelativeLayout) findViewById(R.id.signature);
- mSignatureStatusImage = (ImageView) findViewById(R.id.ic_signature_status);
- mUserId = (TextView) findViewById(R.id.mainUserId);
- mUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
-
- // measure the height of the source_file view and set the message view's min height to that,
- // so it fills mSource fully... bit of a hack.
- View tmp = findViewById(R.id.sourceFile);
- tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- int height = tmp.getMeasuredHeight();
- mMessage.setMinimumHeight(height);
-
- mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
- mBrowse.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- FileHelper.openFile(DecryptActivity.this, mFilename.getText().toString(), "*/*",
- RESULT_CODE_FILE);
- }
- });
-
- mLookupKey = (BootstrapButton) findViewById(R.id.lookup_key);
- mLookupKey.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- lookupUnknownKey(mSignatureKeyId);
- }
- });
-
- mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterDecryption);
-
- // default: message source
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
- mSource.showNext();
- }
-
- mDecryptButton = (BootstrapButton) findViewById(R.id.action_decrypt);
- mDecryptButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- decryptClicked();
- }
- });
+ mTabsAdapter = new PagerTabStripAdapter(this);
+ mViewPager.setAdapter(mTabsAdapter);
}
@Override
@@ -206,68 +74,17 @@ public class DecryptActivity extends DrawerActivity {
setupDrawerNavigation(savedInstanceState);
- // Handle intent actions
+ // Handle intent actions, maybe changes the bundles
handleActions(getIntent());
- if (mSource.getCurrentView().getId() == R.id.sourceMessage
- && mMessage.getText().length() == 0) {
-
- CharSequence clipboardText = ClipboardReflection.getClipboardText(this);
-
- String data = "";
- if (clipboardText != null) {
- Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText);
- if (!matcher.matches()) {
- matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(clipboardText);
- }
- if (matcher.matches()) {
- data = matcher.group(1);
- mMessage.setText(data);
- AppMsg.makeText(this, R.string.using_clipboard_content, AppMsg.STYLE_INFO)
- .show();
- }
- }
- }
-
- mSignatureLayout.setVisibility(View.GONE);
- mSignatureLayout.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- if (mSignatureKeyId == 0) {
- return;
- }
- PGPPublicKeyRing key = ProviderHelper.getPGPPublicKeyRingByKeyId(
- DecryptActivity.this, mSignatureKeyId);
- if (key != null) {
- Intent intent = new Intent(DecryptActivity.this, ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
- intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, mSignatureKeyId);
- startActivity(intent);
- }
- }
- });
-
- if (mReturnResult) {
- mSourcePrevious.setClickable(false);
- mSourcePrevious.setEnabled(false);
- mSourcePrevious.setVisibility(View.INVISIBLE);
-
- mSourceNext.setClickable(false);
- mSourceNext.setEnabled(false);
- mSourceNext.setVisibility(View.INVISIBLE);
-
- mSourceLabel.setClickable(false);
- mSourceLabel.setEnabled(false);
- }
-
- updateSource();
-
- if (mDecryptImmediately
- || (mSource.getCurrentView().getId() == R.id.sourceMessage && (mMessage.getText()
- .length() > 0 || mContentUri != null))) {
- decryptClicked();
- }
+ mTabsAdapter.addTab(DecryptMessageFragment.class,
+ mMessageFragmentBundle, getString(R.string.label_message));
+ mTabsAdapter.addTab(DecryptFileFragment.class,
+ mFileFragmentBundle, getString(R.string.label_file));
+ mViewPager.setCurrentItem(mSwitchToTab);
}
+
/**
* Handles all actions with this intent
*
@@ -316,22 +133,26 @@ public class DecryptActivity extends DrawerActivity {
* Main Actions
*/
if (ACTION_DECRYPT.equals(action) && textData != null) {
- Log.d(Constants.TAG, "textData null, matching text ...");
+ Log.d(Constants.TAG, "textData not null, matching text ...");
Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(textData);
if (matcher.matches()) {
Log.d(Constants.TAG, "PGP_MESSAGE matched");
textData = matcher.group(1);
// replace non breakable spaces
textData = textData.replaceAll("\\xa0", " ");
- mMessage.setText(textData);
+
+ mMessageFragmentBundle.putString(DecryptMessageFragment.ARG_CIPHERTEXT, textData);
+ mSwitchToTab = PAGER_TAB_MESSAGE;
} else {
- matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(textData);
+ matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(textData);
if (matcher.matches()) {
- Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
+ Log.d(Constants.TAG, "PGP_CLEARTEXT_SIGNATURE matched");
textData = matcher.group(1);
// replace non breakable spaces
textData = textData.replaceAll("\\xa0", " ");
- mMessage.setText(textData);
+
+ mMessageFragmentBundle.putString(DecryptMessageFragment.ARG_CIPHERTEXT, textData);
+ mSwitchToTab = PAGER_TAB_MESSAGE;
} else {
Log.d(Constants.TAG, "Nothing matched!");
}
@@ -341,17 +162,12 @@ public class DecryptActivity extends DrawerActivity {
String path = FileHelper.getPath(this, uri);
if (path != null) {
- mInputFilename = path;
- mFilename.setText(mInputFilename);
- guessOutputFilename();
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceFile) {
- mSource.showNext();
- }
+ mFileFragmentBundle.putString(DecryptFileFragment.ARG_FILENAME, path);
+ mSwitchToTab = PAGER_TAB_FILE;
} else {
Log.e(Constants.TAG,
- "Direct binary data without actual file in filesystem is not supported. Please use the Remote Service API!");
+ "Direct binary data without actual file in filesystem is not supported. " +
+ "Please use the Remote Service API!");
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
.show();
// end activity
@@ -363,428 +179,4 @@ public class DecryptActivity extends DrawerActivity {
}
}
- private void guessOutputFilename() {
- mInputFilename = mFilename.getText().toString();
- File file = new File(mInputFilename);
- String filename = file.getName();
- if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
- filename = filename.substring(0, filename.length() - 4);
- }
- mOutputFilename = Constants.Path.APP_DIR + "/" + filename;
- }
-
- private void updateSource() {
- switch (mSource.getCurrentView().getId()) {
- case R.id.sourceFile: {
- mSourceLabel.setText(R.string.label_file);
- mDecryptButton.setText(getString(R.string.btn_decrypt));
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
- mDecryptButton.setText(getString(R.string.btn_decrypt));
- break;
- }
-
- default: {
- break;
- }
- }
- }
-
- private void decryptClicked() {
- if (mSource.getCurrentView().getId() == R.id.sourceFile) {
- mDecryptTarget = Id.target.file;
- } else {
- mDecryptTarget = Id.target.message;
- }
- initiateDecryption();
- }
-
- private void initiateDecryption() {
- if (mDecryptTarget == Id.target.file) {
- String currentFilename = mFilename.getText().toString();
- if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
- guessOutputFilename();
- }
-
- if (mInputFilename.equals("")) {
- AppMsg.makeText(this, R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (mInputFilename.startsWith("file")) {
- File file = new File(mInputFilename);
- if (!file.exists() || !file.isFile()) {
- AppMsg.makeText(
- this,
- getString(R.string.error_message,
- getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
- .show();
- return;
- }
- }
- }
-
- if (mDecryptTarget == Id.target.message) {
- String messageData = mMessage.getText().toString();
- Matcher matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(messageData);
- if (matcher.matches()) {
- mSignedOnly = true;
- decryptStart();
- return;
- }
- }
-
- // else treat it as an decrypted message/file
- mSignedOnly = false;
-
- getDecryptionKeyFromInputStream();
-
- // if we need a symmetric passphrase or a passphrase to use a secret key ask for it
- if (mSecretKeyId == Id.key.symmetric
- || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
- showPassphraseDialog();
- } else {
- if (mDecryptTarget == Id.target.file) {
- askForOutputFilename();
- } else { // mDecryptTarget == Id.target.message
- decryptStart();
- }
- }
- }
-
- /**
- * Shows passphrase dialog to cache a new passphrase the user enters for using it later for
- * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
- * for a symmetric passphrase
- */
- private void showPassphraseDialog() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- if (mDecryptTarget == Id.target.file) {
- askForOutputFilename();
- } else {
- decryptStart();
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(this,
- messenger, mSecretKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException e) {
- Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
- // send message to handler to start encryption directly
- returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
- }
- }
-
- /**
- * TODO: Rework function, remove global variables
- */
- private void getDecryptionKeyFromInputStream() {
- InputStream inStream = null;
- if (mContentUri != null) {
- try {
- inStream = getContentResolver().openInputStream(mContentUri);
- } catch (FileNotFoundException e) {
- Log.e(Constants.TAG, "File not found!", e);
- AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- } else if (mDecryptTarget == Id.target.file) {
- // check if storage is ready
- if (!FileHelper.isStorageMounted(mInputFilename)) {
- AppMsg.makeText(this, getString(R.string.error_external_storage_not_ready),
- AppMsg.STYLE_ALERT).show();
- return;
- }
-
- try {
- inStream = new BufferedInputStream(new FileInputStream(mInputFilename));
- } catch (FileNotFoundException e) {
- Log.e(Constants.TAG, "File not found!", e);
- AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- } finally {
- try {
- if (inStream != null) {
- inStream.close();
- }
- } catch (Exception e) {
- }
- }
- } else {
- inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes());
- }
-
- // get decryption key for this inStream
- try {
- try {
- if (inStream.markSupported()) {
- inStream.mark(200); // should probably set this to the max size of two pgpF
- // objects, if it even needs to be anything other than 0.
- }
- mSecretKeyId = PgpHelper.getDecryptionKeyId(this, inStream);
- if (mSecretKeyId == Id.key.none) {
- throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
- }
- mAssumeSymmetricEncryption = false;
- } catch (NoAsymmetricEncryptionException e) {
- if (inStream.markSupported()) {
- inStream.reset();
- }
- mSecretKeyId = Id.key.symmetric;
- if (!PgpDecryptVerify.hasSymmetricEncryption(this, inStream)) {
- throw new PgpGeneralException(
- getString(R.string.error_no_known_encryption_found));
- }
- mAssumeSymmetricEncryption = true;
- }
- } catch (Exception e) {
- AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()),
- AppMsg.STYLE_ALERT).show();
- }
- }
-
- private void replyClicked() {
- Intent intent = new Intent(this, EncryptActivity.class);
- intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- String data = mMessage.getText().toString();
- data = data.replaceAll("(?m)^", "> ");
- data = "\n\n" + data;
- intent.putExtra(EncryptActivity.EXTRA_TEXT, data);
- intent.putExtra(EncryptActivity.EXTRA_SIGNATURE_KEY_ID, mSecretKeyId);
- intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[]{mSignatureKeyId});
- startActivity(intent);
- }
-
- private void askForOutputFilename() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- decryptStart();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- mFileDialog = FileDialogFragment.newInstance(messenger,
- getString(R.string.title_decrypt_to_file),
- getString(R.string.specify_file_to_decrypt_to), mOutputFilename, null);
-
- mFileDialog.show(getSupportFragmentManager(), "fileDialog");
- }
-
- private void lookupUnknownKey(long unknownKeyId) {
- Intent intent = new Intent(this, ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
- intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId);
- startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY);
- }
-
- private void decryptStart() {
- Log.d(Constants.TAG, "decryptStart");
-
- // Send all information needed to service to decrypt in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
-
- // choose action based on input: decrypt stream, file or bytes
- if (mContentUri != null) {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_STREAM);
-
- data.putParcelable(KeychainIntentService.ENCRYPT_PROVIDER_URI, mContentUri);
- } else if (mDecryptTarget == Id.target.file) {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
-
- Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename);
-
- data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
- } else {
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
-
- String message = mMessage.getText().toString();
- data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, message.getBytes());
- }
-
- data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyId);
-
- data.putBoolean(KeychainIntentService.DECRYPT_RETURN_BYTES, mReturnBinary);
- data.putBoolean(KeychainIntentService.DECRYPT_ASSUME_SYMMETRIC, mAssumeSymmetricEncryption);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in ApgService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle returnData = message.getData();
-
- mSignatureKeyId = 0;
- mSignatureLayout.setVisibility(View.GONE);
-
- AppMsg.makeText(DecryptActivity.this, R.string.decryption_successful,
- AppMsg.STYLE_INFO).show();
- if (mReturnResult) {
- Intent intent = new Intent();
- intent.putExtras(returnData);
- setResult(RESULT_OK, intent);
- finish();
- return;
- }
-
- switch (mDecryptTarget) {
- case Id.target.message:
- String decryptedMessage = returnData
- .getString(KeychainIntentService.RESULT_DECRYPTED_STRING);
- mMessage.setText(decryptedMessage);
- mMessage.setHorizontallyScrolling(false);
-
- break;
-
- case Id.target.file:
- if (mDeleteAfter.isChecked()) {
- // Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
- deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
- }
- break;
-
- default:
- // shouldn't happen
- break;
-
- }
-
- PgpDecryptVerifyResult decryptVerifyResult =
- returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
-
- OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
-
- if (signatureResult != null) {
-
- String userId = signatureResult.getUserId();
- mSignatureKeyId = signatureResult.getKeyId();
- mUserIdRest.setText("id: "
- + PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
- if (userId == null) {
- userId = getResources().getString(R.string.user_id_no_name);
- }
- String chunks[] = userId.split(" <", 2);
- userId = chunks[0];
- if (chunks.length > 1) {
- mUserIdRest.setText("<" + chunks[1]);
- }
- mUserId.setText(userId);
-
- switch (signatureResult.getStatus()) {
- case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
- mLookupKey.setVisibility(View.GONE);
- break;
- }
-
- // TODO!
-// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
-// break;
-// }
-
- case OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY: {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- mLookupKey.setVisibility(View.VISIBLE);
- AppMsg.makeText(DecryptActivity.this,
- R.string.unknown_signature,
- AppMsg.STYLE_ALERT).show();
- break;
- }
-
- default: {
- mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
- mLookupKey.setVisibility(View.GONE);
- break;
- }
- }
- mSignatureLayout.setVisibility(View.VISIBLE);
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case RESULT_CODE_FILE: {
- if (resultCode == RESULT_OK && data != null) {
- try {
- String path = FileHelper.getPath(this, data.getData());
- Log.d(Constants.TAG, "path=" + path);
-
- mFilename.setText(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!");
- }
- }
- return;
- }
-
- // this request is returned after LookupUnknownKeyDialogFragment started
- // ImportKeysActivity and user looked uo key
- case RESULT_CODE_LOOKUP_KEY: {
- Log.d(Constants.TAG, "Returning from Lookup Key...");
- if (resultCode == RESULT_OK) {
- // decrypt again
- decryptStart();
- }
- return;
- }
-
- default: {
- super.onActivityResult(requestCode, resultCode, data);
-
- break;
- }
- }
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
new file mode 100644
index 000000000..492c0cf29
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java
@@ -0,0 +1,261 @@
+/*
+ * 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.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.FileHelper;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.File;
+
+public class DecryptFileFragment extends DecryptFragment {
+ public static final String ARG_FILENAME = "filename";
+
+ private static final int RESULT_CODE_FILE = 0x00007003;
+
+ // view
+ private EditText mFilename;
+ private CheckBox mDeleteAfter;
+ private BootstrapButton mBrowse;
+ private BootstrapButton mDecryptButton;
+
+ private String mInputFilename = null;
+ private String mOutputFilename = null;
+
+ private FileDialogFragment mFileDialog;
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_file_fragment, container, false);
+
+ mFilename = (EditText) view.findViewById(R.id.decrypt_file_filename);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.decrypt_file_browse);
+ mDeleteAfter = (CheckBox) view.findViewById(R.id.decrypt_file_delete_after_decryption);
+ mDecryptButton = (BootstrapButton) view.findViewById(R.id.decrypt_file_action_decrypt);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ FileHelper.openFile(DecryptFileFragment.this, mFilename.getText().toString(), "*/*",
+ RESULT_CODE_FILE);
+ }
+ });
+ mDecryptButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptAction();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String filename = getArguments().getString(ARG_FILENAME);
+ if (filename != null) {
+ mFilename.setText(filename);
+ }
+ }
+
+ private void guessOutputFilename() {
+ mInputFilename = mFilename.getText().toString();
+ File file = new File(mInputFilename);
+ String filename = file.getName();
+ if (filename.endsWith(".asc") || filename.endsWith(".gpg") || filename.endsWith(".pgp")) {
+ filename = filename.substring(0, filename.length() - 4);
+ }
+ mOutputFilename = Constants.Path.APP_DIR + "/" + filename;
+ }
+
+ private void decryptAction() {
+ String currentFilename = mFilename.getText().toString();
+ if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
+ guessOutputFilename();
+ }
+
+ if (mInputFilename.equals("")) {
+ AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (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();
+ return;
+ }
+ }
+
+ askForOutputFilename();
+ }
+
+ private void askForOutputFilename() {
+ // Message is received after passphrase is cached
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == FileDialogFragment.MESSAGE_OKAY) {
+ Bundle data = message.getData();
+ mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ decryptStart(null);
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ mFileDialog = FileDialogFragment.newInstance(messenger,
+ getString(R.string.title_decrypt_to_file),
+ getString(R.string.specify_file_to_decrypt_to), mOutputFilename, null);
+
+ mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog");
+ }
+
+ @Override
+ protected void decryptStart(String passphrase) {
+ Log.d(Constants.TAG, "decryptStart");
+
+ // Send all information needed to service to decrypt in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
+
+ // data
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
+
+ Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
+ + mOutputFilename);
+
+ data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
+ data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
+
+ data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_decrypting), 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();
+
+ PgpDecryptVerifyResult decryptVerifyResult =
+ returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
+
+ if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded());
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
+ decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(Id.key.symmetric);
+ } else {
+ AppMsg.makeText(getActivity(), R.string.decryption_successful,
+ AppMsg.STYLE_INFO).show();
+
+ if (mDeleteAfter.isChecked()) {
+ // Create and show dialog to delete original file
+ DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
+ .newInstance(mInputFilename);
+ deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
+ }
+
+ OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
+
+ // display signature result in activity
+ onSignatureResult(signatureResult);
+ }
+ }
+ }
+ };
+
+ // 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);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_FILE: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ try {
+ String path = FileHelper.getPath(getActivity(), data.getData());
+ Log.d(Constants.TAG, "path=" + path);
+
+ mFilename.setText(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!");
+ }
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
new file mode 100644
index 000000000..1c465f55c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java
@@ -0,0 +1,180 @@
+/*
+ * 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.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v4.app.Fragment;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.openintents.openpgp.OpenPgpSignatureResult;
+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.ProviderHelper;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+
+public class DecryptFragment extends Fragment {
+ private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
+
+ protected long mSignatureKeyId = 0;
+
+ protected RelativeLayout mSignatureLayout = null;
+ protected ImageView mSignatureStatusImage = null;
+ protected TextView mUserId = null;
+ protected TextView mUserIdRest = null;
+
+ protected BootstrapButton mLookupKey = null;
+
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ mSignatureLayout = (RelativeLayout) getView().findViewById(R.id.signature);
+ mSignatureStatusImage = (ImageView) getView().findViewById(R.id.ic_signature_status);
+ mUserId = (TextView) getView().findViewById(R.id.mainUserId);
+ mUserIdRest = (TextView) getView().findViewById(R.id.mainUserIdRest);
+ mLookupKey = (BootstrapButton) getView().findViewById(R.id.lookup_key);
+ mLookupKey.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ lookupUnknownKey(mSignatureKeyId);
+ }
+ });
+ mSignatureLayout.setVisibility(View.GONE);
+ mSignatureLayout.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ lookupUnknownKey(mSignatureKeyId);
+ }
+ });
+ }
+
+ private void lookupUnknownKey(long unknownKeyId) {
+ Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
+ intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId);
+ startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+
+ case RESULT_CODE_LOOKUP_KEY: {
+ if (resultCode == Activity.RESULT_OK) {
+ // TODO: generate new OpenPgpSignatureResult and display it
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+
+ protected void onSignatureResult(OpenPgpSignatureResult signatureResult) {
+ mSignatureKeyId = 0;
+ mSignatureLayout.setVisibility(View.GONE);
+ if (signatureResult != null) {
+
+ mSignatureKeyId = signatureResult.getKeyId();
+
+ String userId = signatureResult.getUserId();
+ String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
+ if (userIdSplit[0] != null) {
+ mUserId.setText(userId);
+ } else {
+ mUserId.setText(R.string.user_id_no_name);
+ }
+ if (userIdSplit[1] != null) {
+ mUserIdRest.setText(userIdSplit[1]);
+ } else {
+ mUserIdRest.setText(getString(R.string.label_key_id) + ": "
+ + PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId));
+ }
+
+ switch (signatureResult.getStatus()) {
+ case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_ok);
+ mLookupKey.setVisibility(View.GONE);
+ break;
+ }
+
+ // TODO!
+// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: {
+// break;
+// }
+
+ case OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY: {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ mLookupKey.setVisibility(View.VISIBLE);
+ AppMsg.makeText(getActivity(),
+ R.string.unknown_signature,
+ AppMsg.STYLE_ALERT).show();
+ break;
+ }
+
+ default: {
+ mSignatureStatusImage.setImageResource(R.drawable.overlay_error);
+ mLookupKey.setVisibility(View.GONE);
+ break;
+ }
+ }
+ mSignatureLayout.setVisibility(View.VISIBLE);
+ }
+ }
+
+ protected void showPassphraseDialog(long keyId) {
+ PassphraseDialogFragment.show(getActivity(), keyId,
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ String passphrase =
+ message.getData().getString(PassphraseDialogFragment.MESSAGE_DATA_PASSPHRASE);
+ decryptStart(passphrase);
+ }
+ }
+ });
+ }
+
+ /**
+ * Should be overridden by MessageFragment and FileFragment to start actual decryption
+ *
+ * @param passphrase
+ */
+ protected void decryptStart(String passphrase) {
+
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
new file mode 100644
index 000000000..2169bbd77
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptMessageFragment.java
@@ -0,0 +1,189 @@
+/*
+ * 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.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.openintents.openpgp.OpenPgpSignatureResult;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
+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 java.util.regex.Matcher;
+
+public class DecryptMessageFragment extends DecryptFragment {
+ public static final String ARG_CIPHERTEXT = "ciphertext";
+
+ // view
+ private EditText mMessage;
+ private BootstrapButton mDecryptButton;
+ private BootstrapButton mDecryptFromCLipboardButton;
+
+ // model
+ private String mCiphertext;
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.decrypt_message_fragment, container, false);
+
+ mMessage = (EditText) view.findViewById(R.id.message);
+ mDecryptButton = (BootstrapButton) view.findViewById(R.id.action_decrypt);
+ mDecryptFromCLipboardButton = (BootstrapButton) view.findViewById(R.id.action_decrypt_from_clipboard);
+ mDecryptButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptClicked();
+ }
+ });
+ mDecryptFromCLipboardButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ decryptFromClipboardClicked();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String ciphertext = getArguments().getString(ARG_CIPHERTEXT);
+ if (ciphertext != null) {
+ mMessage.setText(ciphertext);
+ decryptStart(null);
+ }
+ }
+
+ private void decryptClicked() {
+ mCiphertext = mMessage.getText().toString();
+ decryptStart(null);
+ }
+
+ private void decryptFromClipboardClicked() {
+ CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
+
+ // only decrypt if clipboard content is available and a pgp message or cleartext signature
+ if (clipboardText != null) {
+ Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(clipboardText);
+ if (!matcher.matches()) {
+ matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(clipboardText);
+ }
+ if (matcher.matches()) {
+ mCiphertext = matcher.group(1);
+ decryptStart(null);
+ } else {
+ AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
+ .show();
+ }
+ } else {
+ AppMsg.makeText(getActivity(), R.string.error_invalid_data, AppMsg.STYLE_INFO)
+ .show();
+ }
+ }
+
+ @Override
+ protected void decryptStart(String passphrase) {
+ Log.d(Constants.TAG, "decryptStart");
+
+ // Send all information needed to service to decrypt in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY);
+
+ // data
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
+ data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes());
+ data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_decrypting), 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();
+
+ PgpDecryptVerifyResult decryptVerifyResult =
+ returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT);
+
+ if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded());
+ } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED ==
+ decryptVerifyResult.getStatus()) {
+ showPassphraseDialog(Id.key.symmetric);
+ } else {
+ AppMsg.makeText(getActivity(), R.string.decryption_successful,
+ AppMsg.STYLE_INFO).show();
+
+ byte[] decryptedMessage = returnData
+ .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES);
+ mMessage.setText(new String(decryptedMessage));
+ mMessage.setHorizontallyScrolling(false);
+
+ OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
+
+ // display signature result in activity
+ onSignatureResult(signatureResult);
+ }
+ }
+ }
+ };
+
+ // 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/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
index c0fd53007..f81224380 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java
@@ -21,19 +21,26 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
-import android.view.*;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.FontAwesomeText;
+
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity;
public class DrawerActivity extends ActionBarActivity {
private DrawerLayout mDrawerLayout;
@@ -42,10 +49,8 @@ public class DrawerActivity extends ActionBarActivity {
private CharSequence mDrawerTitle;
private CharSequence mTitle;
+ private boolean mIsDrawerLocked = false;
- private static Class[] mItemsClass = new Class[]{KeyListActivity.class,
- EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class,
- RegisteredAppsListActivity.class};
private Class mSelectedItem;
private static final int MENU_ID_PREFERENCE = 222;
@@ -55,10 +60,22 @@ public class DrawerActivity extends ActionBarActivity {
mDrawerTitle = getString(R.string.app_name);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
-
- // set a custom shadow that overlays the main content when the drawer
- // opens
- mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+ ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame);
+ int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin;
+ int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size);
+ int errorInMarginAllowed = 5;
+
+ // if the left margin of the loaded layout is close to the
+ // one used in tablets then set drawer as open and locked
+ if (Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList);
+ mDrawerLayout.setScrimColor(Color.TRANSPARENT);
+ mIsDrawerLocked = true;
+ } else {
+ // set a custom shadow that overlays the main content when the drawer opens
+ mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
+ mIsDrawerLocked = false;
+ }
NavItem mItemIconTexts[] = new NavItem[]{
new NavItem("fa-user", getString(R.string.nav_contacts)),
@@ -73,8 +90,11 @@ public class DrawerActivity extends ActionBarActivity {
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// enable ActionBar app icon to behave as action to toggle nav drawer
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setHomeButtonEnabled(true);
+ // if the drawer is not locked
+ if (!mIsDrawerLocked) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+ }
// ActionBarDrawerToggle ties together the the proper interactions
// between the sliding drawer and the action bar app icon
@@ -86,19 +106,8 @@ public class DrawerActivity extends ActionBarActivity {
) {
public void onDrawerClosed(View view) {
getSupportActionBar().setTitle(mTitle);
- // creates call to onPrepareOptionsMenu()
- supportInvalidateOptionsMenu();
- // call intent activity if selected
- if (mSelectedItem != null) {
- finish();
- overridePendingTransition(0, 0);
-
- Intent intent = new Intent(DrawerActivity.this, mSelectedItem);
- startActivity(intent);
- // disable animation of activity start
- overridePendingTransition(0, 0);
- }
+ callIntentForDrawerItem(mSelectedItem);
}
public void onDrawerOpened(View drawerView) {
@@ -108,33 +117,56 @@ public class DrawerActivity extends ActionBarActivity {
supportInvalidateOptionsMenu();
}
};
- mDrawerLayout.setDrawerListener(mDrawerToggle);
- // if (savedInstanceState == null) {
- // selectItem(0);
- // }
+ if (!mIsDrawerLocked) {
+ mDrawerLayout.setDrawerListener(mDrawerToggle);
+ } else {
+ // If the drawer is locked open make it un-focusable
+ // so that it doesn't consume all the Back button presses
+ mDrawerLayout.setFocusableInTouchMode(false);
+ }
+ }
+
+ /**
+ * Uses startActivity to call the Intent of the given class
+ *
+ * @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.*
+ */
+ public void callIntentForDrawerItem(Class drawerItem) {
+ // creates call to onPrepareOptionsMenu()
+ supportInvalidateOptionsMenu();
+
+ // call intent activity if selected
+ if (drawerItem != null) {
+ finish();
+ overridePendingTransition(0, 0);
+
+ Intent intent = new Intent(this, drawerItem);
+ startActivity(intent);
+
+ // disable animation of activity start
+ overridePendingTransition(0, 0);
+ }
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
+ if (mDrawerToggle == null) {
+ return super.onCreateOptionsMenu(menu);
+ }
+
menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences);
menu.add(42, MENU_ID_HELP, 101, R.string.menu_help);
return super.onCreateOptionsMenu(menu);
}
- /* Called whenever we call invalidateOptionsMenu() */
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- // If the nav drawer is open, hide action items related to the content
- // view
- boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
- // menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
- return super.onPrepareOptionsMenu(menu);
- }
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ if (mDrawerToggle == null) {
+ return super.onOptionsItemSelected(item);
+ }
+
// The action bar home/up action should open or close the drawer.
// ActionBarDrawerToggle will take care of this.
if (mDrawerToggle.onOptionsItemSelected(item)) {
@@ -155,26 +187,11 @@ public class DrawerActivity extends ActionBarActivity {
default:
return super.onOptionsItemSelected(item);
}
-
- // Handle action buttons
- // switch (item.getItemId()) {
- // case R.id.action_websearch:
- // // create intent to perform web search for this planet
- // Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
- // intent.putExtra(SearchManager.QUERY, getSupportActionBar().getTitle());
- // // catch event that there's no activity to handle intent
- // if (intent.resolveActivity(getPackageManager()) != null) {
- // startActivity(intent);
- // } else {
- // Toast.makeText(this, R.string.app_not_available, Toast.LENGTH_LONG).show();
- // }
- // return true;
- // default:
- // return super.onOptionsItemSelected(item);
- // }
}
- /* The click listener for ListView in the navigation drawer */
+ /**
+ * The click listener for ListView in the navigation drawer
+ */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -185,10 +202,18 @@ public class DrawerActivity extends ActionBarActivity {
private void selectItem(int position) {
// update selected item and title, then close the drawer
mDrawerList.setItemChecked(position, true);
- // setTitle(mDrawerTitles[position]);
- mDrawerLayout.closeDrawer(mDrawerList);
// set selected class
- mSelectedItem = mItemsClass[position];
+ mSelectedItem = Constants.DrawerItems.ARRAY[position];
+
+ // setTitle(mDrawerTitles[position]);
+ // If drawer isn't locked just close the drawer and
+ // it will move to the selected item by itself (via drawer toggle listener)
+ if (!mIsDrawerLocked) {
+ mDrawerLayout.closeDrawer(mDrawerList);
+ // else move to the selected item yourself
+ } else {
+ callIntentForDrawerItem(mSelectedItem);
+ }
}
/**
@@ -199,14 +224,18 @@ public class DrawerActivity extends ActionBarActivity {
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
- mDrawerToggle.syncState();
+ if (mDrawerToggle != null) {
+ mDrawerToggle.syncState();
+ }
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// Pass any configuration change to the drawer toggles
- mDrawerToggle.onConfigurationChanged(newConfig);
+ if (mDrawerToggle != null) {
+ mDrawerToggle.onConfigurationChanged(newConfig);
+ }
}
private class NavItem {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
index edf980773..60bababd1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -27,32 +28,45 @@ 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.*;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.LinearLayout;
import android.widget.Toast;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
+import org.sufficientlysecure.keychain.ui.widget.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;
@@ -61,9 +75,10 @@ import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
import java.util.GregorianCalendar;
+import java.util.List;
import java.util.Vector;
-public class EditKeyActivity extends ActionBarActivity {
+public class EditKeyActivity extends ActionBarActivity implements EditorListener {
// Actions for internal use only:
public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY";
@@ -74,10 +89,6 @@ public class EditKeyActivity extends ActionBarActivity {
public static final String EXTRA_NO_PASSPHRASE = "no_passphrase";
public static final String EXTRA_GENERATE_DEFAULT_KEYS = "generate_default_keys";
- // results when saving key
- public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id";
- public static final String RESULT_EXTRA_USER_ID = "user_id";
-
// EDIT
private Uri mDataUri;
@@ -87,9 +98,11 @@ public class EditKeyActivity extends ActionBarActivity {
private SectionView mKeysView;
private String mCurrentPassphrase = null;
- private String mNewPassPhrase = null;
- private String mSavedNewPassPhrase = null;
- private boolean mIsPassPhraseSet;
+ private String mNewPassphrase = null;
+ private String mSavedNewPassphrase = null;
+ private boolean mIsPassphraseSet;
+ private boolean mNeedsSaving;
+ private boolean mIsBrandNewKeyring = false;
private BootstrapButton mChangePassphrase;
@@ -102,12 +115,37 @@ public class EditKeyActivity extends ActionBarActivity {
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);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setIcon(android.R.color.transparent);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
mUserIds = new Vector<String>();
mKeys = new Vector<PGPSecretKey>();
mKeysUsages = new Vector<Integer>();
@@ -128,24 +166,10 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionCreateKey(Intent intent) {
- // Inflate a "Save"/"Cancel" custom action bar
- ActionBarHelper.setTwoButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- saveClicked();
- }
- }, R.string.btn_do_not_save, R.drawable.ic_action_cancel, new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- cancelClicked();
- }
- }
- );
-
Bundle extras = intent.getExtras();
mCurrentPassphrase = "";
+ mIsBrandNewKeyring = true;
if (extras != null) {
// if userId is given, prefill the fields
@@ -180,7 +204,7 @@ public class EditKeyActivity extends ActionBarActivity {
serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after generating is done in ApgService
+ // 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,
@@ -197,28 +221,28 @@ public class EditKeyActivity extends ActionBarActivity {
@Override
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // 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.getData();
- PGPSecretKey masterKey = (PGPSecretKey) PgpConversionHelper
- .BytesToPGPSecretKey(data
+
+ ArrayList<PGPSecretKey> newKeys =
+ PgpConversionHelper.BytesToPGPSecretKeyList(data
.getByteArray(KeychainIntentService.RESULT_NEW_KEY));
- PGPSecretKey subKey = (PGPSecretKey) PgpConversionHelper
- .BytesToPGPSecretKey(data
- .getByteArray(KeychainIntentService.RESULT_NEW_KEY2));
- // add master key
- mKeys.add(masterKey);
- mKeysUsages.add(Id.choice.usage.sign_only); //TODO: get from key flags
+ ArrayList<Integer> keyUsageFlags = data.getIntegerArrayList(
+ KeychainIntentService.RESULT_KEY_USAGES);
- // add sub key
- mKeys.add(subKey);
- mKeysUsages.add(Id.choice.usage.encrypt_only); //TODO: get from key flags
+ if (newKeys.size() == keyUsageFlags.size()) {
+ for (int i = 0; i < newKeys.size(); ++i) {
+ mKeys.add(newKeys.get(i));
+ mKeysUsages.add(keyUsageFlags.get(i));
+ }
+ }
- buildLayout();
+ buildLayout(true);
}
}
};
@@ -234,7 +258,7 @@ public class EditKeyActivity extends ActionBarActivity {
}
}
} else {
- buildLayout();
+ buildLayout(false);
}
}
@@ -244,67 +268,16 @@ public class EditKeyActivity extends ActionBarActivity {
* @param intent
*/
private void handleActionEditKey(Intent intent) {
- // Inflate a "Save"/"Cancel" custom action bar
- ActionBarHelper.setOneButtonView(getSupportActionBar(), R.string.btn_save, R.drawable.ic_action_save,
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- saveClicked();
- }
- });
-
mDataUri = intent.getData();
if (mDataUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
finish();
- return;
} else {
Log.d(Constants.TAG, "uri: " + mDataUri);
// get master key id using row id
long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
-
- mMasterCanSign = ProviderHelper.getMasterKeyCanSign(this, mDataUri);
- finallyEdit(masterKeyId, mMasterCanSign);
- }
- }
-
- private void showPassphraseDialog(final long masterKeyId, final boolean masterCanSign) {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- String passphrase = PassphraseCacheService.getCachedPassphrase(
- EditKeyActivity.this, masterKeyId);
- mCurrentPassphrase = passphrase;
- finallySaveClicked();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
- EditKeyActivity.this, messenger, masterKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException e) {
- Log.d(Constants.TAG, "No passphrase for this secret key!");
- // send message to handler to start encryption directly
- returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
- }
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- // show menu only on edit
- if (mDataUri != null) {
- return super.onPrepareOptionsMenu(menu);
- } else {
- return false;
+ finallyEdit(masterKeyId);
}
}
@@ -312,45 +285,66 @@ public class EditKeyActivity extends ActionBarActivity {
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.key_edit, menu);
+ //totally get rid of some actions for new keys
+ if (mDataUri == null) {
+ MenuItem mButton = menu.findItem(R.id.menu_key_edit_export_file);
+ mButton.setVisible(false);
+ mButton = menu.findItem(R.id.menu_key_edit_delete);
+ mButton.setVisible(false);
+ }
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
+ case android.R.id.home:
+ cancelClicked();
+ // TODO: why isn't this triggered on my tablet - one of many ui problems
+ // I've had with this device. A code compatibility issue or a Samsung fail?
+ return true;
case R.id.menu_key_edit_cancel:
cancelClicked();
return true;
case R.id.menu_key_edit_export_file:
- long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
- mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC);
+ if (needsSaving()) {
+ Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show();
+ } else {
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
+ mExportHelper.showExportKeysDialog(
+ new long[] { masterKeyId }, Constants.Path.APP_DIR_FILE_SEC, true);
+ return true;
+ }
+ return true;
+ case R.id.menu_key_edit_delete:
+ Uri convertUri = KeyRingData.buildSecretKeyRingUri(mDataUri);
+ // Message is received after key is deleted
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }};
+ mExportHelper.deleteKey(convertUri, returnHandler);
return true;
- case R.id.menu_key_edit_delete: {
- // Message is received after key is deleted
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
- };
- mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler);
+ case R.id.menu_key_edit_save:
+ saveClicked();
return true;
- }
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("unchecked")
- private void finallyEdit(final long masterKeyId, final boolean masterCanSign) {
+ private void finallyEdit(final long masterKeyId) {
if (masterKeyId != 0) {
PGPSecretKey masterKey = null;
- mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId);
+ mKeyRing = ProviderHelper.getPGPSecretKeyRing(this, masterKeyId);
if (mKeyRing != null) {
- masterKey = PgpKeyHelper.getMasterKey(mKeyRing);
+ masterKey = mKeyRing.getSecretKey();
+ mMasterCanSign = PgpKeyHelper.isCertificationKey(mKeyRing.getSecretKey());
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mKeyRing.getSecretKeys())) {
mKeys.add(key);
mKeysUsages.add(-1); // get usage when view is created
@@ -358,20 +352,29 @@ public class EditKeyActivity extends ActionBarActivity {
} else {
Log.e(Constants.TAG, "Keyring not found with masterKeyId: " + masterKeyId);
Toast.makeText(this, R.string.error_no_secret_key_found, Toast.LENGTH_LONG).show();
+ // TODO
}
if (masterKey != null) {
+ boolean isSet = false;
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
Log.d(Constants.TAG, "Added userId " + userId);
+ if (!isSet) {
+ isSet = true;
+ String[] parts = PgpKeyHelper.splitUserId(userId);
+ if (parts[0] != null) {
+ setTitle(parts[0]);
+ }
+ }
mUserIds.add(userId);
}
}
}
mCurrentPassphrase = "";
+ buildLayout(false);
- buildLayout();
- mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
- if (!mIsPassPhraseSet) {
+ mIsPassphraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId);
+ if (!mIsPassphraseSet) {
// check "no passphrase" checkbox and remove button
mNoPassphrase.setChecked(true);
mChangePassphrase.setVisibility(View.GONE);
@@ -390,10 +393,11 @@ public class EditKeyActivity extends ActionBarActivity {
Bundle data = message.getData();
// set new returned passphrase!
- mNewPassPhrase = data
+ mNewPassphrase = data
.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE);
- updatePassPhraseButtonText();
+ updatePassphraseButtonText();
+ somethingChanged();
}
}
};
@@ -402,7 +406,7 @@ public class EditKeyActivity extends ActionBarActivity {
Messenger messenger = new Messenger(returnHandler);
// set title based on isPassphraseSet()
- int title = -1;
+ int title;
if (isPassphraseSet()) {
title = R.string.title_change_passphrase;
} else {
@@ -418,30 +422,37 @@ public class EditKeyActivity extends ActionBarActivity {
/**
* Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user
* id and key.
+ *
+ * @param newKeys
*/
- private void buildLayout() {
+ private void buildLayout(boolean newKeys) {
setContentView(R.layout.edit_key_activity);
// find views
mChangePassphrase = (BootstrapButton) 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(Id.type.user_id);
- mUserIdsView.setCanEdit(mMasterCanSign);
+ 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(Id.type.key);
- mKeysView.setCanEdit(mMasterCanSign);
- mKeysView.setKeys(mKeys, mKeysUsages);
+ mKeysView.setCanBeEdited(mMasterCanSign);
+ mKeysView.setKeys(mKeys, mKeysUsages, newKeys);
+ mKeysView.setEditorListener(this);
container.addView(mKeysView);
- updatePassPhraseButtonText();
+ updatePassphraseButtonText();
mChangePassphrase.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
@@ -449,20 +460,21 @@ public class EditKeyActivity extends ActionBarActivity {
}
});
- // disable passphrase when no passphrase checkobox is checked!
+ // 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 = "";
+ mSavedNewPassphrase = mNewPassphrase;
+ mNewPassphrase = "";
mChangePassphrase.setVisibility(View.GONE);
} else {
- mNewPassPhrase = mSavedNewPassPhrase;
+ mNewPassphrase = mSavedNewPassphrase;
mChangePassphrase.setVisibility(View.VISIBLE);
}
+ somethingChanged();
}
});
}
@@ -477,37 +489,116 @@ public class EditKeyActivity extends ActionBarActivity {
public boolean isPassphraseSet() {
if (mNoPassphrase.isChecked()) {
return true;
- } else if ((mIsPassPhraseSet)
- || (mNewPassPhrase != null && !mNewPassPhrase.equals(""))) {
+ } else if ((mIsPassphraseSet)
+ || (mNewPassphrase != null && !mNewPassphrase.equals(""))) {
return true;
} else {
return false;
}
}
- private void saveClicked() {
- long masterKeyId = getMasterKeyId();
- try {
- if (!isPassphraseSet()) {
- throw new PgpGeneralException(this.getString(R.string.set_a_passphrase));
+ public boolean hasPassphraseChanged() {
+ if (mNoPassphrase != null) {
+ if (mNoPassphrase.isChecked()) {
+ return mIsPassphraseSet;
+ } else {
+ return (mNewPassphrase != null && !mNewPassphrase.equals(""));
}
+ } else {
+ return false;
+ }
+ }
- String passphrase = null;
- if (mIsPassPhraseSet) {
- passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
- } else {
- passphrase = "";
+ 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(
+ EditKeyActivity.this, masterKeyId);
+ checkEmptyIDsWanted();
+ }
+ }
+ });
+ } else {
+ mCurrentPassphrase = passphrase;
+ checkEmptyIDsWanted();
+ }
+ } catch (PgpGeneralException e) {
+ Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
+ Toast.LENGTH_SHORT).show();
}
- if (passphrase == null) {
- showPassphraseDialog(masterKeyId, mMasterCanSign);
- } else {
- mCurrentPassphrase = passphrase;
- finallySaveClicked();
+ } 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))) {
+ AlertDialog.Builder alert = new AlertDialog.Builder(
+ EditKeyActivity.this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.warning);
+ alert.setMessage(EditKeyActivity.this.getString(R.string.ask_empty_id_ok));
+
+ alert.setPositiveButton(EditKeyActivity.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.create().show();
+ return;
+ }
+ curID++;
}
} catch (PgpGeneralException e) {
- //Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
- // Toast.LENGTH_SHORT).show();
+ Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
+ Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
+ Toast.LENGTH_SHORT).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() {
@@ -517,42 +608,45 @@ public class EditKeyActivity extends ActionBarActivity {
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
+ SaveKeyringParcel saveParams = new SaveKeyringParcel();
+ 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.newPassphrase = 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.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE,
- mCurrentPassphrase);
- data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase);
- data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS,
- getUserIds(mUserIdsView));
- ArrayList<PGPSecretKey> keys = getKeys(mKeysView);
- data.putByteArray(KeychainIntentService.SAVE_KEYRING_KEYS,
- PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys));
- data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES,
- getKeysUsages(mKeysView));
- data.putSerializable(KeychainIntentService.SAVE_KEYRING_KEYS_EXPIRY_DATES,
- getKeysExpiryDates(mKeysView));
- data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId());
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 ApgService
+ // 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 ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
Intent data = new Intent();
- data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, getMasterKeyId());
- ArrayList<String> userIds = null;
- try {
- userIds = getUserIds(mUserIdsView);
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, "exception while getting user ids", e);
- }
- data.putExtra(RESULT_EXTRA_USER_ID, userIds.get(0));
+
+ // return uri pointing to new created key
+ Uri uri = KeychainContract.KeyRings.buildGenericKeyRingUri(
+ String.valueOf(getMasterKeyId()));
+ data.setData(uri);
+
setResult(RESULT_OK, data);
finish();
}
@@ -568,14 +662,42 @@ public class EditKeyActivity extends ActionBarActivity {
// start service with intent
startService(intent);
} catch (PgpGeneralException e) {
- //Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
- // Toast.LENGTH_SHORT).show();
+ Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage()));
+ Toast.makeText(this, getString(R.string.error_message, e.getMessage()),
+ Toast.LENGTH_SHORT).show();
}
}
private void cancelClicked() {
- setResult(RESULT_CANCELED);
- finish();
+ if (needsSaving()) { //ask if we want to save
+ AlertDialog.Builder alert = new AlertDialog.Builder(
+ EditKeyActivity.this);
+
+ alert.setIcon(android.R.drawable.ic_dialog_alert);
+ alert.setTitle(R.string.warning);
+ alert.setMessage(EditKeyActivity.this.getString(R.string.ask_save_changed_key));
+
+ alert.setPositiveButton(EditKeyActivity.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.create().show();
+ } else {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
}
/**
@@ -592,19 +714,8 @@ public class EditKeyActivity extends ActionBarActivity {
boolean gotMainUserId = false;
for (int i = 0; i < userIdEditors.getChildCount(); ++i) {
UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i);
- String userId = null;
- try {
- userId = editor.getValue();
- } catch (UserIdEditor.NoNameException e) {
- throw new PgpGeneralException(this.getString(R.string.error_user_id_needs_a_name));
- } catch (UserIdEditor.NoEmailException e) {
- throw new PgpGeneralException(
- this.getString(R.string.error_user_id_needs_an_email_address));
- }
-
- if (userId.equals("")) {
- continue;
- }
+ String userId;
+ userId = editor.getValue();
if (editor.isMainUserId()) {
userIds.add(0, userId);
@@ -688,7 +799,7 @@ public class EditKeyActivity extends ActionBarActivity {
return keysExpiryDates;
}
- private void updatePassPhraseButtonText() {
+ private void updatePassphraseButtonText() {
mChangePassphrase.setText(isPassphraseSet() ? getString(R.string.btn_change_passphrase)
: getString(R.string.btn_set_passphrase));
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 1231b6209..a03c7d797 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -17,49 +17,25 @@
package org.sufficientlysecure.keychain.ui;
-import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.*;
-import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.beardedhen.androidbootstrap.FontAwesomeText;
-import com.devspark.appmsg.AppMsg;
-import org.spongycastle.openpgp.PGPPublicKey;
-import org.spongycastle.openpgp.PGPPublicKeyRing;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
+import android.support.v4.view.PagerTabStrip;
+import android.support.v4.view.ViewPager;
+import android.widget.Toast;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.helper.FileHelper;
-import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.KeychainIntentService;
-import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.PassphraseCacheService;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
-import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
-import org.sufficientlysecure.keychain.util.Choice;
+import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.File;
-import java.util.Vector;
-
-public class EncryptActivity extends DrawerActivity {
+public class EncryptActivity extends DrawerActivity implements
+ EncryptSymmetricFragment.OnSymmetricKeySelection,
+ EncryptAsymmetricFragment.OnAsymmetricKeySelection,
+ EncryptActivityInterface {
/* Intents */
public static final String ACTION_ENCRYPT = Constants.INTENT_PREFIX + "ENCRYPT";
@@ -74,57 +50,94 @@ public class EncryptActivity extends DrawerActivity {
public static final String EXTRA_SIGNATURE_KEY_ID = "signature_key_id";
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids";
+ // view
+ ViewPager mViewPagerMode;
+ PagerTabStrip mPagerTabStripMode;
+ PagerTabStripAdapter mTabsAdapterMode;
+ ViewPager mViewPagerContent;
+ PagerTabStrip mPagerTabStripContent;
+ PagerTabStripAdapter mTabsAdapterContent;
+
+ // tabs
+ Bundle mAsymmetricFragmentBundle = new Bundle();
+ Bundle mSymmetricFragmentBundle = new Bundle();
+ Bundle mMessageFragmentBundle = new Bundle();
+ Bundle mFileFragmentBundle = new Bundle();
+ int mSwitchToMode = PAGER_MODE_ASYMMETRIC;
+ int mSwitchToContent = PAGER_CONTENT_MESSAGE;
+
+ private static final int PAGER_MODE_ASYMMETRIC = 0;
+ private static final int PAGER_MODE_SYMMETRIC = 1;
+ private static final int PAGER_CONTENT_MESSAGE = 0;
+ private static final int PAGER_CONTENT_FILE = 1;
+
+ // model useb by message and file fragment
private long mEncryptionKeyIds[] = null;
+ private long mSigningKeyId = Id.key.none;
+ private String mPassphrase;
+ private String mPassphraseAgain;
- private EditText mMessage = null;
- private BootstrapButton mSelectKeysButton = null;
-
- private CheckBox mSign = null;
- private TextView mMainUserId = null;
- private TextView mMainUserIdRest = null;
-
- private ViewFlipper mSource = null;
- private TextView mSourceLabel = null;
- private ImageView mSourcePrevious = null;
- private ImageView mSourceNext = null;
+ @Override
+ public void onSigningKeySelected(long signingKeyId) {
+ mSigningKeyId = signingKeyId;
+ }
- private ViewFlipper mMode = null;
- private TextView mModeLabel = null;
- private ImageView mModePrevious = null;
- private ImageView mModeNext = null;
+ @Override
+ public void onEncryptionKeysSelected(long[] encryptionKeyIds) {
+ mEncryptionKeyIds = encryptionKeyIds;
+ }
- private int mEncryptTarget;
+ @Override
+ public void onPassphraseUpdate(String passphrase) {
+ mPassphrase = passphrase;
+ }
- private EditText mPassphrase = null;
- private EditText mPassphraseAgain = null;
- private CheckBox mAsciiArmor = null;
- private Spinner mFileCompression = null;
+ @Override
+ public void onPassphraseAgainUpdate(String passphrase) {
+ mPassphraseAgain = passphrase;
+ }
- private EditText mFilename = null;
- private CheckBox mDeleteAfter = null;
- private CheckBox mShareAfter = null;
- private BootstrapButton mBrowse = null;
+ @Override
+ public boolean isModeSymmetric() {
+ if (PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
- private String mInputFilename = null;
- private String mOutputFilename = null;
+ @Override
+ public long getSignatureKey() {
+ return mSigningKeyId;
+ }
- private Integer mShortAnimationDuration = null;
- private boolean mFileAdvancedSettingsVisible = false;
- private TextView mFileAdvancedSettings = null;
- private LinearLayout mFileAdvancedSettingsContainer = null;
- private FontAwesomeText mAdvancedSettingsIcon;
- private boolean mAsciiArmorDemand = false;
- private boolean mOverrideAsciiArmor = false;
+ @Override
+ public long[] getEncryptionKeys() {
+ return mEncryptionKeyIds;
+ }
- private boolean mGenerateSignature = false;
+ @Override
+ public String getPassphrase() {
+ return mPassphrase;
+ }
- private long mSecretKeyId = Id.key.none;
+ @Override
+ public String getPassphraseAgain() {
+ return mPassphraseAgain;
+ }
- private FileDialogFragment mFileDialog;
- private BootstrapButton mEncryptShare;
- private BootstrapButton mEncryptClipboard;
- private BootstrapButton mEncryptFile;
+ private void initView() {
+ mViewPagerMode = (ViewPager) findViewById(R.id.encrypt_pager_mode);
+ mPagerTabStripMode = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_mode);
+ mViewPagerContent = (ViewPager) findViewById(R.id.encrypt_pager_content);
+ mPagerTabStripContent = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_content);
+
+ mTabsAdapterMode = new PagerTabStripAdapter(this);
+ mViewPagerMode.setAdapter(mTabsAdapterMode);
+ mTabsAdapterContent = new PagerTabStripAdapter(this);
+ mViewPagerContent.setAdapter(mTabsAdapterContent);
+ }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -142,14 +155,17 @@ public class EncryptActivity extends DrawerActivity {
// Handle intent actions
handleActions(getIntent());
- updateView();
- updateSource();
- updateMode();
-
- updateActionBarButtons();
-
- // retrieve and cache the system's short animation time
- mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
+ mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class,
+ mAsymmetricFragmentBundle, getString(R.string.label_asymmetric));
+ mTabsAdapterMode.addTab(EncryptSymmetricFragment.class,
+ mSymmetricFragmentBundle, getString(R.string.label_symmetric));
+ mViewPagerMode.setCurrentItem(mSwitchToMode);
+
+ mTabsAdapterContent.addTab(EncryptMessageFragment.class,
+ mMessageFragmentBundle, getString(R.string.label_message));
+ mTabsAdapterContent.addTab(EncryptFileFragment.class,
+ mFileFragmentBundle, getString(R.string.label_file));
+ mViewPagerContent.setCurrentItem(mSwitchToContent);
}
/**
@@ -190,9 +206,8 @@ public class EncryptActivity extends DrawerActivity {
}
if (extras.containsKey(EXTRA_ASCII_ARMOR)) {
- mAsciiArmorDemand = extras.getBoolean(EXTRA_ASCII_ARMOR, true);
- mOverrideAsciiArmor = true;
- mAsciiArmor.setChecked(mAsciiArmorDemand);
+ boolean requestAsciiArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, true);
+ mFileFragmentBundle.putBoolean(EncryptFileFragment.ARG_ASCII_ARMOR, requestAsciiArmor);
}
String textData = extras.getString(EXTRA_TEXT);
@@ -201,20 +216,19 @@ public class EncryptActivity extends DrawerActivity {
long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
// preselect keys given by intent
- preselectKeys(signatureKeyId, encryptionKeyIds);
+ mAsymmetricFragmentBundle.putLongArray(EncryptAsymmetricFragment.ARG_ENCRYPTION_KEY_IDS,
+ encryptionKeyIds);
+ mAsymmetricFragmentBundle.putLong(EncryptAsymmetricFragment.ARG_SIGNATURE_KEY_ID,
+ signatureKeyId);
+ mSwitchToMode = PAGER_MODE_ASYMMETRIC;
/**
* Main Actions
*/
if (ACTION_ENCRYPT.equals(action) && textData != null) {
// encrypt text based on given extra
-
- mMessage.setText(textData);
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceMessage) {
- mSource.showNext();
- }
+ mMessageFragmentBundle.putString(EncryptMessageFragment.ARG_TEXT, textData);
+ mSwitchToContent = PAGER_CONTENT_MESSAGE;
} else if (ACTION_ENCRYPT.equals(action) && uri != null) {
// encrypt file based on Uri
@@ -222,17 +236,12 @@ public class EncryptActivity extends DrawerActivity {
String path = FileHelper.getPath(this, uri);
if (path != null) {
- mInputFilename = path;
- mFilename.setText(mInputFilename);
-
- mSource.setInAnimation(null);
- mSource.setOutAnimation(null);
- while (mSource.getCurrentView().getId() != R.id.sourceFile) {
- mSource.showNext();
- }
+ mFileFragmentBundle.putString(EncryptFileFragment.ARG_FILENAME, path);
+ mSwitchToContent = PAGER_CONTENT_FILE;
} else {
Log.e(Constants.TAG,
- "Direct binary data without actual file in filesystem is not supported by Intents. Please use the Remote Service API!");
+ "Direct binary data without actual file in filesystem is not supported " +
+ "by Intents. Please use the Remote Service API!");
Toast.makeText(this, R.string.error_only_files_are_supported, Toast.LENGTH_LONG)
.show();
// end activity
@@ -244,779 +253,4 @@ public class EncryptActivity extends DrawerActivity {
}
}
- /**
- * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those!
- *
- * @param preselectedSignatureKeyId
- * @param preselectedEncryptionKeyIds
- */
- private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) {
- if (preselectedSignatureKeyId != 0) {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this,
- preselectedSignatureKeyId);
- PGPSecretKey masterKey = null;
- if (keyRing != null) {
- masterKey = PgpKeyHelper.getMasterKey(keyRing);
- if (masterKey != null) {
- Vector<PGPSecretKey> signKeys = PgpKeyHelper.getUsableSigningKeys(keyRing);
- if (signKeys.size() > 0) {
- mSecretKeyId = masterKey.getKeyID();
- }
- }
- }
- }
-
- if (preselectedEncryptionKeyIds != null) {
- Vector<Long> goodIds = new Vector<Long>();
- for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
- PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this,
- preselectedEncryptionKeyIds[i]);
- PGPPublicKey masterKey = null;
- if (keyRing == null) {
- continue;
- }
- masterKey = PgpKeyHelper.getMasterKey(keyRing);
- if (masterKey == null) {
- continue;
- }
- Vector<PGPPublicKey> encryptKeys = PgpKeyHelper.getUsableEncryptKeys(keyRing);
- if (encryptKeys.size() == 0) {
- continue;
- }
- goodIds.add(masterKey.getKeyID());
- }
- if (goodIds.size() > 0) {
- mEncryptionKeyIds = new long[goodIds.size()];
- for (int i = 0; i < goodIds.size(); ++i) {
- mEncryptionKeyIds[i] = goodIds.get(i);
- }
- }
- }
- }
-
- /**
- * Guess output filename based on input path
- *
- * @param path
- * @return Suggestion for output filename
- */
- private String guessOutputFilename(String path) {
- // output in the same directory but with additional ending
- File file = new File(path);
- String ending = (mAsciiArmor.isChecked() ? ".asc" : ".gpg");
- String outputFilename = file.getParent() + File.separator + file.getName() + ending;
-
- return outputFilename;
- }
-
- private void updateSource() {
- switch (mSource.getCurrentView().getId()) {
- case R.id.sourceFile: {
- mSourceLabel.setText(R.string.label_file);
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
- break;
- }
-
- default: {
- break;
- }
- }
- updateActionBarButtons();
- }
-
- /**
- * Update ActionBar buttons based on current selection in view
- */
- private void updateActionBarButtons() {
- switch (mSource.getCurrentView().getId()) {
- case R.id.sourceFile: {
- mEncryptShare.setVisibility(View.GONE);
- mEncryptClipboard.setVisibility(View.GONE);
- mEncryptFile.setVisibility(View.VISIBLE);
- break;
- }
-
- case R.id.sourceMessage: {
- mSourceLabel.setText(R.string.label_message);
-
- mEncryptShare.setVisibility(View.VISIBLE);
- mEncryptClipboard.setVisibility(View.VISIBLE);
- mEncryptFile.setVisibility(View.GONE);
-
- if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
- mEncryptShare.setEnabled(true);
- mEncryptClipboard.setEnabled(true);
- } else {
- if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- if (mSecretKeyId == 0) {
- mEncryptShare.setEnabled(false);
- mEncryptClipboard.setEnabled(false);
- } else {
- mEncryptShare.setEnabled(true);
- mEncryptClipboard.setEnabled(true);
- }
- } else {
- mEncryptShare.setEnabled(true);
- mEncryptClipboard.setEnabled(true);
- }
- }
- break;
- }
-
- default: {
- break;
- }
- }
-
- }
-
- private void updateMode() {
- switch (mMode.getCurrentView().getId()) {
- case R.id.modeAsymmetric: {
- mModeLabel.setText(R.string.label_asymmetric);
- break;
- }
-
- case R.id.modeSymmetric: {
- mModeLabel.setText(R.string.label_symmetric);
- break;
- }
-
- default: {
- break;
- }
- }
- updateActionBarButtons();
- }
-
- private void encryptToClipboardClicked() {
- mEncryptTarget = Id.target.clipboard;
- initiateEncryption();
- }
-
- private void encryptClicked() {
- Log.d(Constants.TAG, "encryptClicked invoked!");
-
- if (mSource.getCurrentView().getId() == R.id.sourceFile) {
- mEncryptTarget = Id.target.file;
- } else {
- mEncryptTarget = Id.target.email;
- }
- initiateEncryption();
- }
-
- private void initiateEncryption() {
- if (mEncryptTarget == Id.target.file) {
- String currentFilename = mFilename.getText().toString();
- if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
- mInputFilename = mFilename.getText().toString();
- }
-
- mOutputFilename = guessOutputFilename(mInputFilename);
-
- if (mInputFilename.equals("")) {
- AppMsg.makeText(this, R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (!mInputFilename.startsWith("content")) {
- File file = new File(mInputFilename);
- if (!file.exists() || !file.isFile()) {
- AppMsg.makeText(
- this,
- getString(R.string.error_message,
- getString(R.string.error_file_not_found)), AppMsg.STYLE_ALERT)
- .show();
- return;
- }
- }
- }
-
- // symmetric encryption
- if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
- boolean gotPassPhrase = false;
- String passphrase = mPassphrase.getText().toString();
- String passphraseAgain = mPassphraseAgain.getText().toString();
- if (!passphrase.equals(passphraseAgain)) {
- AppMsg.makeText(this, R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- gotPassPhrase = (passphrase.length() != 0);
- if (!gotPassPhrase) {
- AppMsg.makeText(this, R.string.passphrase_must_not_be_empty, AppMsg.STYLE_ALERT)
- .show();
- return;
- }
- } else {
- boolean encryptIt = (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0);
- // for now require at least one form of encryption for files
- if (!encryptIt && mEncryptTarget == Id.target.file) {
- AppMsg.makeText(this, R.string.select_encryption_key, AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (!encryptIt && mSecretKeyId == 0) {
- AppMsg.makeText(this, R.string.select_encryption_or_signature_key,
- AppMsg.STYLE_ALERT).show();
- return;
- }
-
- if (mSecretKeyId != 0
- && PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) {
- showPassphraseDialog();
-
- return;
- }
- }
-
- if (mEncryptTarget == Id.target.file) {
- showOutputFileDialog();
- } else {
- encryptStart();
- }
- }
-
- /**
- * Shows passphrase dialog to cache a new passphrase the user enters for using it later for
- * encryption
- */
- private void showPassphraseDialog() {
- // Message is received after passphrase is cached
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
- if (mEncryptTarget == Id.target.file) {
- showOutputFileDialog();
- } else {
- encryptStart();
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- try {
- PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(
- EncryptActivity.this, messenger, mSecretKeyId);
-
- passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
- } catch (PgpGeneralException e) {
- Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
- // send message to handler to start encryption directly
- returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
- }
- }
-
- private void showOutputFileDialog() {
- // Message is received after file is selected
- Handler returnHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- if (message.what == FileDialogFragment.MESSAGE_OKAY) {
- Bundle data = message.getData();
- mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
- encryptStart();
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(returnHandler);
-
- mFileDialog = FileDialogFragment.newInstance(messenger,
- getString(R.string.title_encrypt_to_file),
- getString(R.string.specify_file_to_encrypt_to), mOutputFilename, null);
-
- mFileDialog.show(getSupportFragmentManager(), "fileDialog");
- }
-
- private void encryptStart() {
- // Send all information needed to service to edit key in other thread
- Intent intent = new Intent(this, KeychainIntentService.class);
-
- // fill values for this action
- Bundle data = new Bundle();
-
- boolean useAsciiArmor = true;
- long encryptionKeyIds[] = null;
- int compressionId = 0;
- boolean signOnly = false;
- long mSecretKeyIdToPass = 0;
-
- if (mMode.getCurrentView().getId() == R.id.modeSymmetric) {
- Log.d(Constants.TAG, "Symmetric encryption enabled!");
- String passphrase = mPassphrase.getText().toString();
- if (passphrase.length() == 0) {
- passphrase = null;
- }
- data.putString(KeychainIntentService.GENERATE_KEY_SYMMETRIC_PASSPHRASE, passphrase);
- } else {
- mSecretKeyIdToPass = mSecretKeyId;
- encryptionKeyIds = mEncryptionKeyIds;
- signOnly = (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0);
- }
-
- intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
-
- // choose default settings, target and data bundle by target
- if (mEncryptTarget == Id.target.file) {
- useAsciiArmor = mAsciiArmor.isChecked();
- compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
-
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
-
- Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
- + mOutputFilename);
-
- data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
- data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
-
- } else {
- useAsciiArmor = true;
- compressionId = Preferences.getPreferences(this).getDefaultMessageCompression();
-
- data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
-
- String message = mMessage.getText().toString();
- if (signOnly) {
- fixBadCharactersForGmail(message);
- }
- data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes());
- }
-
- if (mOverrideAsciiArmor) {
- useAsciiArmor = mAsciiArmorDemand;
- }
-
- data.putLong(KeychainIntentService.ENCRYPT_SECRET_KEY_ID, mSecretKeyIdToPass);
- data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, useAsciiArmor);
- data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS, encryptionKeyIds);
- data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
- data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
- data.putBoolean(KeychainIntentService.ENCRYPT_SIGN_ONLY, signOnly);
-
- intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
-
- // Message is received after encrypting is done in ApgService
- KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
- getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) {
- public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
- super.handleMessage(message);
-
- if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // get returned data bundle
- Bundle data = message.getData();
-
- String output;
- switch (mEncryptTarget) {
- case Id.target.clipboard:
- output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING);
- Log.d(Constants.TAG, "output: " + output);
- ClipboardReflection.copyToClipboard(EncryptActivity.this, output);
- AppMsg.makeText(EncryptActivity.this,
- R.string.encryption_to_clipboard_successful, AppMsg.STYLE_INFO)
- .show();
- break;
-
- case Id.target.email:
-
- output = data.getString(KeychainIntentService.RESULT_ENCRYPTED_STRING);
- Log.d(Constants.TAG, "output: " + output);
-
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
-
- // Type is set to text/plain so that encrypted messages can
- // be sent with Whatsapp, Hangouts, SMS etc...
- sendIntent.setType("text/plain");
-
- sendIntent.putExtra(Intent.EXTRA_TEXT, output);
- startActivity(Intent.createChooser(sendIntent,
- getString(R.string.title_send_email)));
- break;
-
- case Id.target.file:
- AppMsg.makeText(EncryptActivity.this, R.string.encryption_successful,
- AppMsg.STYLE_INFO).show();
-
- if (mDeleteAfter.isChecked()) {
- // Create and show dialog to delete original file
- DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
- .newInstance(mInputFilename);
- deleteFileDialog.show(getSupportFragmentManager(), "deleteDialog");
- }
-
- if (mShareAfter.isChecked()) {
- // Share encrypted file
- Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
- sendFileIntent.setType("*/*");
- sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
- startActivity(Intent.createChooser(sendFileIntent,
- getString(R.string.title_send_file)));
- }
- break;
-
- default:
- // shouldn't happen
- break;
-
- }
- }
- }
- };
-
- // Create a new Messenger for the communication back
- Messenger messenger = new Messenger(saveHandler);
- intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
-
- // show progress dialog
- saveHandler.showProgressDialog(this);
-
- // start service with intent
- startService(intent);
- }
-
- /**
- * Fixes bad message characters for gmail
- *
- * @param message
- * @return
- */
- private String fixBadCharactersForGmail(String message) {
- // fix the message a bit, trailing spaces and newlines break stuff,
- // because GMail sends as HTML and such things fuck up the
- // signature,
- // TODO: things like "<" and ">" also fuck up the signature
- message = message.replaceAll(" +\n", "\n");
- message = message.replaceAll("\n\n+", "\n\n");
- message = message.replaceFirst("^\n+", "");
- // make sure there'll be exactly one newline at the end
- message = message.replaceFirst("\n*$", "\n");
-
- return message;
- }
-
- private void initView() {
- mSource = (ViewFlipper) findViewById(R.id.source);
- mSourceLabel = (TextView) findViewById(R.id.sourceLabel);
- mSourcePrevious = (ImageView) findViewById(R.id.sourcePrevious);
- mSourceNext = (ImageView) findViewById(R.id.sourceNext);
-
- mSourcePrevious.setClickable(true);
- mSourcePrevious.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_out));
- mSource.showPrevious();
- updateSource();
- }
- });
-
- mSourceNext.setClickable(true);
- OnClickListener nextSourceClickListener = new OnClickListener() {
- public void onClick(View v) {
- mSource.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_in));
- mSource.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_out));
- mSource.showNext();
- updateSource();
- }
- };
- mSourceNext.setOnClickListener(nextSourceClickListener);
-
- mSourceLabel.setClickable(true);
- mSourceLabel.setOnClickListener(nextSourceClickListener);
-
- mMode = (ViewFlipper) findViewById(R.id.mode);
- mModeLabel = (TextView) findViewById(R.id.modeLabel);
- mModePrevious = (ImageView) findViewById(R.id.modePrevious);
- mModeNext = (ImageView) findViewById(R.id.modeNext);
-
- mModePrevious.setClickable(true);
- mModePrevious.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_in));
- mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_right_out));
- mMode.showPrevious();
- updateMode();
- }
- });
-
- OnClickListener nextModeClickListener = new OnClickListener() {
- public void onClick(View v) {
- mMode.setInAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_in));
- mMode.setOutAnimation(AnimationUtils.loadAnimation(EncryptActivity.this,
- R.anim.push_left_out));
- mMode.showNext();
- updateMode();
- }
- };
- mModeNext.setOnClickListener(nextModeClickListener);
-
- mModeLabel.setClickable(true);
- mModeLabel.setOnClickListener(nextModeClickListener);
-
- mMessage = (EditText) findViewById(R.id.message);
- mSelectKeysButton = (BootstrapButton) findViewById(R.id.btn_selectEncryptKeys);
- mSign = (CheckBox) findViewById(R.id.sign);
- mMainUserId = (TextView) findViewById(R.id.mainUserId);
- mMainUserIdRest = (TextView) findViewById(R.id.mainUserIdRest);
-
- mPassphrase = (EditText) findViewById(R.id.passphrase);
- mPassphraseAgain = (EditText) findViewById(R.id.passphraseAgain);
-
- // measure the height of the source_file view and set the message view's min height to that,
- // so it fills mSource fully... bit of a hack.
- View tmp = findViewById(R.id.sourceFile);
- tmp.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- int height = tmp.getMeasuredHeight();
- mMessage.setMinimumHeight(height);
-
- mFilename = (EditText) findViewById(R.id.filename);
- mBrowse = (BootstrapButton) findViewById(R.id.btn_browse);
- mBrowse.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- FileHelper.openFile(EncryptActivity.this, mFilename.getText().toString(), "*/*",
- Id.request.filename);
- }
- });
-
- mAdvancedSettingsIcon = (FontAwesomeText) findViewById(R.id.advancedSettingsIcon);
- mFileAdvancedSettingsContainer = (LinearLayout) findViewById(R.id.fileAdvancedSettingsContainer);
- mFileAdvancedSettings = (TextView) findViewById(R.id.advancedSettings);
-
- LinearLayout advancedSettingsControl = (LinearLayout) findViewById(R.id.advancedSettingsControl);
- advancedSettingsControl.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- mFileAdvancedSettingsVisible = !mFileAdvancedSettingsVisible;
- if (mFileAdvancedSettingsVisible) {
- mAdvancedSettingsIcon.setIcon("fa-chevron-down");
- mFileAdvancedSettingsContainer.setVisibility(View.VISIBLE);
- AlphaAnimation animation = new AlphaAnimation(0f, 1f);
- animation.setDuration(mShortAnimationDuration);
- mFileAdvancedSettingsContainer.startAnimation(animation);
- mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_hide);
-
- } else {
- mAdvancedSettingsIcon.setIcon("fa-chevron-right");
- AlphaAnimation animation = new AlphaAnimation(1f, 0f);
- animation.setDuration(mShortAnimationDuration);
- animation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- // do nothing
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- // making sure that at the end the container is completely removed from view
- mFileAdvancedSettingsContainer.setVisibility(View.GONE);
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {
- // do nothing
- }
- });
- mFileAdvancedSettingsContainer.startAnimation(animation);
- mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_show);
- }
- }
- });
-
- mFileCompression = (Spinner) findViewById(R.id.fileCompression);
- Choice[] choices = new Choice[]{
- new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zip, "ZIP ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.zlib, "ZLIB ("
- + getString(R.string.compression_fast) + ")"),
- new Choice(Id.choice.compression.bzip2, "BZIP2 ("
- + getString(R.string.compression_very_slow) + ")"), };
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(this,
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mFileCompression.setAdapter(adapter);
-
- int defaultFileCompression = Preferences.getPreferences(this).getDefaultFileCompression();
- for (int i = 0; i < choices.length; ++i) {
- if (choices[i].getId() == defaultFileCompression) {
- mFileCompression.setSelection(i);
- break;
- }
- }
-
- mDeleteAfter = (CheckBox) findViewById(R.id.deleteAfterEncryption);
- mShareAfter = (CheckBox) findViewById(R.id.shareAfterEncryption);
-
- mAsciiArmor = (CheckBox) findViewById(R.id.asciiArmour);
- mAsciiArmor.setChecked(Preferences.getPreferences(this).getDefaultAsciiArmour());
-
- mSelectKeysButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- selectPublicKeys();
- }
- });
-
- mSign.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- CheckBox checkBox = (CheckBox) v;
- if (checkBox.isChecked()) {
- selectSecretKey();
- } else {
- mSecretKeyId = Id.key.none;
- updateView();
- }
- }
- });
-
- mEncryptClipboard = (BootstrapButton) findViewById(R.id.action_encrypt_clipboard);
- mEncryptClipboard.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- encryptToClipboardClicked();
- }
- });
- mEncryptShare = (BootstrapButton) findViewById(R.id.action_encrypt_share);
- mEncryptShare.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- encryptClicked();
- }
- });
- mEncryptFile = (BootstrapButton) findViewById(R.id.action_encrypt_file);
- mEncryptFile.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- encryptClicked();
- }
- });
- }
-
- private void updateView() {
- if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
- mSelectKeysButton.setText(getString(R.string.select_keys_button_default));
- } else {
- mSelectKeysButton.setText(getResources().getQuantityString(
- R.plurals.select_keys_button, mEncryptionKeyIds.length,
- mEncryptionKeyIds.length));
- }
-
- if (mSecretKeyId == Id.key.none) {
- mSign.setChecked(false);
- mMainUserId.setText("");
- mMainUserIdRest.setText("");
- } else {
- String uid = getResources().getString(R.string.user_id_no_name);
- String uidExtra = "";
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this,
- mSecretKeyId);
- if (keyRing != null) {
- PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
- if (key != null) {
- String userId = PgpKeyHelper.getMainUserIdSafe(this, key);
- String chunks[] = userId.split(" <", 2);
- uid = chunks[0];
- if (chunks.length > 1) {
- uidExtra = "<" + chunks[1];
- }
- }
- }
- mMainUserId.setText(uid);
- mMainUserIdRest.setText(uidExtra);
- mSign.setChecked(true);
- }
-
- updateActionBarButtons();
- }
-
- private void selectPublicKeys() {
- Intent intent = new Intent(this, SelectPublicKeyActivity.class);
- Vector<Long> keyIds = new Vector<Long>();
- if (mSecretKeyId != 0) {
- keyIds.add(mSecretKeyId);
- }
- if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
- for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
- keyIds.add(mEncryptionKeyIds[i]);
- }
- }
- long[] initialKeyIds = null;
- if (keyIds.size() > 0) {
- initialKeyIds = new long[keyIds.size()];
- for (int i = 0; i < keyIds.size(); ++i) {
- initialKeyIds[i] = keyIds.get(i);
- }
- }
- intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds);
- startActivityForResult(intent, Id.request.public_keys);
- }
-
- private void selectSecretKey() {
- Intent intent = new Intent(this, SelectSecretKeyActivity.class);
- startActivityForResult(intent, Id.request.secret_keys);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode) {
- case Id.request.filename: {
- if (resultCode == RESULT_OK && data != null) {
- try {
- String path = FileHelper.getPath(this, data.getData());
- Log.d(Constants.TAG, "path=" + path);
-
- mFilename.setText(path);
- } catch (NullPointerException e) {
- Log.e(Constants.TAG, "Nullpointer while retrieving path!");
- }
- }
- return;
- }
-
- case Id.request.public_keys: {
- if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- mEncryptionKeyIds = bundle
- .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS);
- }
- updateView();
- break;
- }
-
- case Id.request.secret_keys: {
- if (resultCode == RESULT_OK) {
- Bundle bundle = data.getExtras();
- mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
- } else {
- mSecretKeyId = Id.key.none;
- }
- updateView();
- break;
- }
-
- default: {
- break;
- }
- }
-
- super.onActivityResult(requestCode, resultCode, data);
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java
new file mode 100644
index 000000000..0786b3a16
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java
@@ -0,0 +1,30 @@
+/*
+ * 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;
+
+public interface EncryptActivityInterface {
+
+ public boolean isModeSymmetric();
+
+ public long getSignatureKey();
+ public long[] getEncryptionKeys();
+
+ public String getPassphrase();
+ public String getPassphraseAgain();
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
new file mode 100644
index 000000000..6e84211cc
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -0,0 +1,269 @@
+/*
+ * 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.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+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 com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.spongycastle.openpgp.PGPPublicKey;
+import org.spongycastle.openpgp.PGPPublicKeyRing;
+import org.spongycastle.openpgp.PGPSecretKey;
+import org.spongycastle.openpgp.PGPSecretKeyRing;
+import org.sufficientlysecure.keychain.Id;
+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 java.util.HashMap;
+import java.util.Vector;
+
+public class EncryptAsymmetricFragment extends Fragment {
+ public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id";
+ public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids";
+
+ public static final int RESULT_CODE_PUBLIC_KEYS = 0x00007001;
+ public static final int RESULT_CODE_SECRET_KEYS = 0x00007002;
+
+ OnAsymmetricKeySelection mKeySelectionListener;
+
+ // view
+ private BootstrapButton mSelectKeysButton;
+ private CheckBox mSign;
+ private TextView mMainUserId;
+ private TextView mMainUserIdRest;
+
+ // model
+ private long mSecretKeyId = Id.key.none;
+ private long mEncryptionKeyIds[] = null;
+
+ // Container Activity must implement this interface
+ public interface OnAsymmetricKeySelection {
+ public void onSigningKeySelected(long signingKeyId);
+
+ public void onEncryptionKeysSelected(long[] encryptionKeyIds);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mKeySelectionListener = (OnAsymmetricKeySelection) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement OnAsymmetricKeySelection");
+ }
+ }
+
+ private void setSignatureKeyId(long signatureKeyId) {
+ mSecretKeyId = signatureKeyId;
+ // update key selection in EncryptActivity
+ mKeySelectionListener.onSigningKeySelected(signatureKeyId);
+ updateView();
+ }
+
+ private void setEncryptionKeyIds(long[] encryptionKeyIds) {
+ mEncryptionKeyIds = encryptionKeyIds;
+ // update key selection in EncryptActivity
+ mKeySelectionListener.onEncryptionKeysSelected(encryptionKeyIds);
+ updateView();
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false);
+
+ mSelectKeysButton = (BootstrapButton) view.findViewById(R.id.btn_selectEncryptKeys);
+ mSign = (CheckBox) view.findViewById(R.id.sign);
+ mMainUserId = (TextView) view.findViewById(R.id.mainUserId);
+ mMainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
+ mSelectKeysButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ selectPublicKeys();
+ }
+ });
+ mSign.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ CheckBox checkBox = (CheckBox) v;
+ if (checkBox.isChecked()) {
+ selectSecretKey();
+ } else {
+ setSignatureKeyId(Id.key.none);
+ }
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ long signatureKeyId = getArguments().getLong(ARG_SIGNATURE_KEY_ID);
+ long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS);
+
+ // preselect keys given by arguments (given by Intent to EncryptActivity)
+ preselectKeys(signatureKeyId, encryptionKeyIds);
+ }
+
+ /**
+ * If an Intent gives a signatureKeyId and/or encryptionKeyIds, preselect those!
+ *
+ * @param preselectedSignatureKeyId
+ * @param preselectedEncryptionKeyIds
+ */
+ private void preselectKeys(long preselectedSignatureKeyId, long[] preselectedEncryptionKeyIds) {
+ if (preselectedSignatureKeyId != 0) {
+ // TODO: don't use bouncy castle objects!
+ PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingWithKeyId(getActivity(),
+ preselectedSignatureKeyId);
+ PGPSecretKey masterKey;
+ if (keyRing != null) {
+ masterKey = keyRing.getSecretKey();
+ if (masterKey != null) {
+ Vector<PGPSecretKey> signKeys = PgpKeyHelper.getUsableSigningKeys(keyRing);
+ if (signKeys.size() > 0) {
+ setSignatureKeyId(masterKey.getKeyID());
+ }
+ }
+ }
+ }
+
+ if (preselectedEncryptionKeyIds != null) {
+ Vector<Long> goodIds = new Vector<Long>();
+ for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
+ long id = ProviderHelper.getMasterKeyId(getActivity(),
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(preselectedEncryptionKeyIds[i]))
+ );
+ // TODO check for available encrypt keys... is this even relevant?
+ goodIds.add(id);
+ }
+ if (goodIds.size() > 0) {
+ long[] keyIds = new long[goodIds.size()];
+ for (int i = 0; i < goodIds.size(); ++i) {
+ keyIds[i] = goodIds.get(i);
+ }
+ setEncryptionKeyIds(keyIds);
+ }
+ }
+ }
+
+ private void updateView() {
+ if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
+ mSelectKeysButton.setText(getString(R.string.select_keys_button_default));
+ } else {
+ mSelectKeysButton.setText(getResources().getQuantityString(
+ R.plurals.select_keys_button, mEncryptionKeyIds.length,
+ mEncryptionKeyIds.length));
+ }
+
+ if (mSecretKeyId == Id.key.none) {
+ mSign.setChecked(false);
+ mMainUserId.setText("");
+ mMainUserIdRest.setText("");
+ } else {
+ String uid = getResources().getString(R.string.user_id_no_name);
+ String uidExtra = "";
+ // See if we can get a user_id from a unified query
+ String user_id = (String) ProviderHelper.getUnifiedData(
+ getActivity(), mSecretKeyId, KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
+ if(user_id != null) {
+ String chunks[] = user_id.split(" <", 2);
+ uid = chunks[0];
+ if (chunks.length > 1) {
+ uidExtra = "<" + chunks[1];
+ }
+ }
+
+ mMainUserId.setText(uid);
+ mMainUserIdRest.setText(uidExtra);
+ mSign.setChecked(true);
+ }
+ }
+
+ private void selectPublicKeys() {
+ Intent intent = new Intent(getActivity(), SelectPublicKeyActivity.class);
+ Vector<Long> keyIds = new Vector<Long>();
+ if (mSecretKeyId != 0) {
+ keyIds.add(mSecretKeyId);
+ }
+ if (mEncryptionKeyIds != null && mEncryptionKeyIds.length > 0) {
+ for (int i = 0; i < mEncryptionKeyIds.length; ++i) {
+ keyIds.add(mEncryptionKeyIds[i]);
+ }
+ }
+ long[] initialKeyIds = null;
+ if (keyIds.size() > 0) {
+ initialKeyIds = new long[keyIds.size()];
+ for (int i = 0; i < keyIds.size(); ++i) {
+ initialKeyIds[i] = keyIds.get(i);
+ }
+ }
+ intent.putExtra(SelectPublicKeyActivity.EXTRA_SELECTED_MASTER_KEY_IDS, initialKeyIds);
+ startActivityForResult(intent, Id.request.public_keys);
+ }
+
+ private void selectSecretKey() {
+ Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
+ startActivityForResult(intent, Id.request.secret_keys);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_PUBLIC_KEYS: {
+ if (resultCode == Activity.RESULT_OK) {
+ Bundle bundle = data.getExtras();
+ setEncryptionKeyIds(bundle
+ .getLongArray(SelectPublicKeyActivity.RESULT_EXTRA_MASTER_KEY_IDS));
+ }
+ break;
+ }
+
+ case RESULT_CODE_SECRET_KEYS: {
+ if (resultCode == Activity.RESULT_OK) {
+ Uri uriMasterKey = data.getData();
+ setSignatureKeyId(Long.valueOf(uriMasterKey.getLastPathSegment()));
+ } else {
+ setSignatureKeyId(Id.key.none);
+ }
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
new file mode 100644
index 000000000..470c85715
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFileFragment.java
@@ -0,0 +1,380 @@
+/*
+ * 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.Handler;
+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.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Spinner;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Id;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.FileHelper;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.util.Choice;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.File;
+
+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 EncryptActivityInterface mEncryptInterface;
+
+ // view
+ private CheckBox mAsciiArmor = null;
+ private Spinner mFileCompression = null;
+ private EditText mFilename = null;
+ private CheckBox mDeleteAfter = null;
+ private CheckBox mShareAfter = null;
+ private BootstrapButton mBrowse = null;
+ private BootstrapButton mEncryptFile;
+
+ private FileDialogFragment mFileDialog;
+
+ // model
+ private String mInputFilename = null;
+ private String mOutputFilename = null;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mEncryptInterface = (EncryptActivityInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
+ }
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_file_fragment, container, false);
+
+ mEncryptFile = (BootstrapButton) view.findViewById(R.id.action_encrypt_file);
+ mEncryptFile.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptClicked();
+ }
+ });
+
+ mFilename = (EditText) view.findViewById(R.id.filename);
+ mBrowse = (BootstrapButton) view.findViewById(R.id.btn_browse);
+ mBrowse.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ FileHelper.openFile(EncryptFileFragment.this, mFilename.getText().toString(), "*/*",
+ Id.request.filename);
+ }
+ });
+
+ mFileCompression = (Spinner) view.findViewById(R.id.fileCompression);
+ Choice[] choices = new Choice[] {
+ new Choice(Id.choice.compression.none, getString(R.string.choice_none) + " ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.zip, "ZIP ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.zlib, "ZLIB ("
+ + getString(R.string.compression_fast) + ")"),
+ new Choice(Id.choice.compression.bzip2, "BZIP2 ("
+ + getString(R.string.compression_very_slow) + ")"),
+ };
+ ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getActivity(),
+ android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mFileCompression.setAdapter(adapter);
+
+ int defaultFileCompression = Preferences.getPreferences(getActivity()).getDefaultFileCompression();
+ for (int i = 0; i < choices.length; ++i) {
+ if (choices[i].getId() == defaultFileCompression) {
+ mFileCompression.setSelection(i);
+ break;
+ }
+ }
+
+ mDeleteAfter = (CheckBox) view.findViewById(R.id.deleteAfterEncryption);
+ mShareAfter = (CheckBox) view.findViewById(R.id.shareAfterEncryption);
+
+ mAsciiArmor = (CheckBox) view.findViewById(R.id.asciiArmor);
+ mAsciiArmor.setChecked(Preferences.getPreferences(getActivity()).getDefaultAsciiArmor());
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String filename = getArguments().getString(ARG_FILENAME);
+ if (filename != null) {
+ mFilename.setText(filename);
+ }
+ boolean asciiArmor = getArguments().getBoolean(ARG_ASCII_ARMOR);
+ if (asciiArmor) {
+ mAsciiArmor.setChecked(asciiArmor);
+ }
+ }
+
+ /**
+ * Guess output filename based on input path
+ *
+ * @param path
+ * @return Suggestion for output filename
+ */
+ private String guessOutputFilename(String path) {
+ // output in the same directory but with additional ending
+ File file = new File(path);
+ String ending = (mAsciiArmor.isChecked() ? ".asc" : ".gpg");
+ String outputFilename = file.getParent() + File.separator + file.getName() + ending;
+
+ return outputFilename;
+ }
+
+ private void showOutputFileDialog() {
+ // Message is received after file is selected
+ Handler returnHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == FileDialogFragment.MESSAGE_OKAY) {
+ Bundle data = message.getData();
+ mOutputFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME);
+ encryptStart();
+ }
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ mFileDialog = FileDialogFragment.newInstance(messenger,
+ getString(R.string.title_encrypt_to_file),
+ getString(R.string.specify_file_to_encrypt_to), mOutputFilename, null);
+
+ mFileDialog.show(getActivity().getSupportFragmentManager(), "fileDialog");
+ }
+
+ private void encryptClicked() {
+ String currentFilename = mFilename.getText().toString();
+ if (mInputFilename == null || !mInputFilename.equals(currentFilename)) {
+ mInputFilename = mFilename.getText().toString();
+ }
+
+ mOutputFilename = guessOutputFilename(mInputFilename);
+
+ if (mInputFilename.equals("")) {
+ AppMsg.makeText(getActivity(), R.string.no_file_selected, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (!mInputFilename.startsWith("content")) {
+ 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();
+ return;
+ }
+ }
+
+ if (mEncryptInterface.isModeSymmetric()) {
+ // symmetric encryption
+
+ 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();
+ return;
+ }
+
+ if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
+ AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+ } else {
+ // asymmetric encryption
+
+ boolean gotEncryptionKeys = (mEncryptInterface.getEncryptionKeys() != null
+ && mEncryptInterface.getEncryptionKeys().length > 0);
+
+ if (!gotEncryptionKeys) {
+ AppMsg.makeText(getActivity(), R.string.select_encryption_key, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
+ AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
+ AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (mEncryptInterface.getSignatureKey() != 0 &&
+ PassphraseCacheService.getCachedPassphrase(getActivity(),
+ mEncryptInterface.getSignatureKey()) == null) {
+ PassphraseDialogFragment.show(getActivity(), mEncryptInterface.getSignatureKey(),
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ showOutputFileDialog();
+ }
+ }
+ });
+
+ return;
+ }
+ }
+
+ showOutputFileDialog();
+ }
+
+ private void encryptStart() {
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_URI);
+
+ if (mEncryptInterface.isModeSymmetric()) {
+ Log.d(Constants.TAG, "Symmetric encryption enabled!");
+ String passphrase = mEncryptInterface.getPassphrase();
+ if (passphrase.length() == 0) {
+ passphrase = null;
+ }
+ data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
+ } else {
+ data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID,
+ mEncryptInterface.getSignatureKey());
+ data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS,
+ mEncryptInterface.getEncryptionKeys());
+ }
+
+ Log.d(Constants.TAG, "mInputFilename=" + mInputFilename + ", mOutputFilename="
+ + mOutputFilename);
+
+ data.putString(KeychainIntentService.ENCRYPT_INPUT_FILE, mInputFilename);
+ data.putString(KeychainIntentService.ENCRYPT_OUTPUT_FILE, mOutputFilename);
+
+ boolean useAsciiArmor = mAsciiArmor.isChecked();
+ data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, useAsciiArmor);
+
+ int compressionId = ((Choice) mFileCompression.getSelectedItem()).getId();
+ data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
+// data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_encrypting), 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(getActivity(), R.string.encryption_successful,
+ AppMsg.STYLE_INFO).show();
+
+ if (mDeleteAfter.isChecked()) {
+ // Create and show dialog to delete original file
+ DeleteFileDialogFragment deleteFileDialog = DeleteFileDialogFragment
+ .newInstance(mInputFilename);
+ deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
+ }
+
+ if (mShareAfter.isChecked()) {
+ // Share encrypted file
+ Intent sendFileIntent = new Intent(Intent.ACTION_SEND);
+ sendFileIntent.setType("*/*");
+ sendFileIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(mOutputFilename));
+ startActivity(Intent.createChooser(sendFileIntent,
+ getString(R.string.title_send_file)));
+ }
+ }
+ }
+ };
+
+ // 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);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_FILE: {
+ if (resultCode == Activity.RESULT_OK && data != null) {
+ try {
+ String path = FileHelper.getPath(getActivity(), data.getData());
+ Log.d(Constants.TAG, "path=" + path);
+
+ mFilename.setText(path);
+ } catch (NullPointerException e) {
+ Log.e(Constants.TAG, "Nullpointer while retrieving path!");
+ }
+ }
+ return;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
new file mode 100644
index 000000000..ba11074fc
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptMessageFragment.java
@@ -0,0 +1,259 @@
+/*
+ * 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.os.Bundle;
+import android.os.Handler;
+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.EditText;
+
+import com.beardedhen.androidbootstrap.BootstrapButton;
+import com.devspark.appmsg.AppMsg;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.PassphraseCacheService;
+import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+import org.sufficientlysecure.keychain.util.Log;
+
+public class EncryptMessageFragment extends Fragment {
+ public static final String ARG_TEXT = "text";
+
+ private EditText mMessage = null;
+ private BootstrapButton mEncryptShare;
+ private BootstrapButton mEncryptClipboard;
+
+ private EncryptActivityInterface mEncryptInterface;
+
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mEncryptInterface = (EncryptActivityInterface) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface");
+ }
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_message_fragment, container, false);
+
+ mMessage = (EditText) view.findViewById(R.id.message);
+ mEncryptClipboard = (BootstrapButton) view.findViewById(R.id.action_encrypt_clipboard);
+ mEncryptShare = (BootstrapButton) view.findViewById(R.id.action_encrypt_share);
+ mEncryptClipboard.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptClicked(true);
+ }
+ });
+ mEncryptShare.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ encryptClicked(false);
+ }
+ });
+
+ return view;
+ }
+
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ String text = getArguments().getString(ARG_TEXT);
+ if (text != null) {
+ mMessage.setText(text);
+ }
+ }
+
+ /**
+ * Fixes bad message characters for gmail
+ *
+ * @param message
+ * @return
+ */
+ private String fixBadCharactersForGmail(String message) {
+ // fix the message a bit, trailing spaces and newlines break stuff,
+ // because GMail sends as HTML and such things fuck up the
+ // signature,
+ // TODO: things like "<" and ">" also fuck up the signature
+ message = message.replaceAll(" +\n", "\n");
+ message = message.replaceAll("\n\n+", "\n\n");
+ message = message.replaceFirst("^\n+", "");
+ // make sure there'll be exactly one newline at the end
+ message = message.replaceFirst("\n*$", "\n");
+
+ return message;
+ }
+
+ private void encryptClicked(final boolean toClipboard) {
+ if (mEncryptInterface.isModeSymmetric()) {
+ // symmetric encryption
+
+ 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();
+ return;
+ }
+
+ if (!mEncryptInterface.getPassphrase().equals(mEncryptInterface.getPassphraseAgain())) {
+ AppMsg.makeText(getActivity(), R.string.passphrases_do_not_match, AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ } else {
+ // asymmetric encryption
+
+ boolean gotEncryptionKeys = (mEncryptInterface.getEncryptionKeys() != null
+ && mEncryptInterface.getEncryptionKeys().length > 0);
+
+ if (!gotEncryptionKeys && mEncryptInterface.getSignatureKey() == 0) {
+ AppMsg.makeText(getActivity(), R.string.select_encryption_or_signature_key,
+ AppMsg.STYLE_ALERT).show();
+ return;
+ }
+
+ if (mEncryptInterface.getSignatureKey() != 0 &&
+ PassphraseCacheService.getCachedPassphrase(getActivity(),
+ mEncryptInterface.getSignatureKey()) == null) {
+ PassphraseDialogFragment.show(getActivity(), mEncryptInterface.getSignatureKey(),
+ new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
+ encryptStart(toClipboard);
+ }
+ }
+ });
+
+ return;
+ }
+ }
+
+ encryptStart(toClipboard);
+ }
+
+ private void encryptStart(final boolean toClipboard) {
+ // Send all information needed to service to edit key in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_ENCRYPT_SIGN);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ data.putInt(KeychainIntentService.TARGET, KeychainIntentService.TARGET_BYTES);
+
+ String message = mMessage.getText().toString();
+
+ if (mEncryptInterface.isModeSymmetric()) {
+ Log.d(Constants.TAG, "Symmetric encryption enabled!");
+ String passphrase = mEncryptInterface.getPassphrase();
+ if (passphrase.length() == 0) {
+ passphrase = null;
+ }
+ data.putString(KeychainIntentService.ENCRYPT_SYMMETRIC_PASSPHRASE, passphrase);
+ } else {
+ data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_ID,
+ mEncryptInterface.getSignatureKey());
+ data.putLongArray(KeychainIntentService.ENCRYPT_ENCRYPTION_KEYS_IDS,
+ mEncryptInterface.getEncryptionKeys());
+
+ boolean signOnly = (mEncryptInterface.getEncryptionKeys() == null
+ || mEncryptInterface.getEncryptionKeys().length == 0);
+ if (signOnly) {
+ message = fixBadCharactersForGmail(message);
+ }
+ }
+
+ data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, message.getBytes());
+
+ data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true);
+
+ int compressionId = Preferences.getPreferences(getActivity()).getDefaultMessageCompression();
+ data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId);
+// data.putBoolean(KeychainIntentService.ENCRYPT_GENERATE_SIGNATURE, mGenerateSignature);
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Message is received after encrypting is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
+ getString(R.string.progress_encrypting), 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 data = message.getData();
+
+ String output = new String(data.getByteArray(KeychainIntentService.RESULT_BYTES));
+ Log.d(Constants.TAG, "output: " + output);
+
+ if (toClipboard) {
+ ClipboardReflection.copyToClipboard(getActivity(), output);
+ AppMsg.makeText(getActivity(),
+ R.string.encryption_to_clipboard_successful, AppMsg.STYLE_INFO)
+ .show();
+ } else {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+
+ // Type is set to text/plain so that encrypted messages can
+ // be sent with Whatsapp, Hangouts, SMS etc...
+ sendIntent.setType("text/plain");
+
+ sendIntent.putExtra(Intent.EXTRA_TEXT, output);
+ startActivity(Intent.createChooser(sendIntent,
+ getString(R.string.title_send_email)));
+ }
+ }
+ }
+ };
+
+ // 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/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java
new file mode 100644
index 000000000..8efa07953
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java
@@ -0,0 +1,98 @@
+/*
+ * 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.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+
+import org.sufficientlysecure.keychain.R;
+
+public class EncryptSymmetricFragment extends Fragment {
+
+ OnSymmetricKeySelection mPassphraseUpdateListener;
+
+ private EditText mPassphrase;
+ private EditText mPassphraseAgain;
+
+ // Container Activity must implement this interface
+ public interface OnSymmetricKeySelection {
+ public void onPassphraseUpdate(String passphrase);
+
+ public void onPassphraseAgainUpdate(String passphrase);
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mPassphraseUpdateListener = (OnSymmetricKeySelection) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement OnSymmetricKeySelection");
+ }
+ }
+
+ /**
+ * Inflate the layout for this fragment
+ */
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.encrypt_symmetric_fragment, container, false);
+
+ mPassphrase = (EditText) view.findViewById(R.id.passphrase);
+ mPassphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain);
+ mPassphrase.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // update passphrase in EncryptActivity
+ mPassphraseUpdateListener.onPassphraseUpdate(s.toString());
+ }
+ });
+ mPassphraseAgain.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // update passphrase in EncryptActivity
+ mPassphraseUpdateListener.onPassphraseAgainUpdate(s.toString());
+ }
+ });
+
+ return view;
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 05bfc613e..9b8b92136 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2011 Senecaso
- *
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
import android.annotation.SuppressLint;
+import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
@@ -34,8 +35,10 @@ import android.support.v7.app.ActionBar;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
import com.devspark.appmsg.AppMsg;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -54,7 +57,6 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
+ "IMPORT_KEY_FROM_QR_CODE";
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEYSERVER";
- // TODO: implement:
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN";
@@ -72,6 +74,10 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
public static final String EXTRA_KEY_ID = "key_id";
public static final String EXTRA_FINGERPRINT = "fingerprint";
+ // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN when used from OpenPgpService
+ public static final String EXTRA_PENDING_INTENT_DATA = "data";
+ private Intent mPendingIntentData;
+
// view
private ImportKeysListFragment mListFragment;
private String[] mNavigationStrings;
@@ -86,7 +92,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
ImportKeysNFCFragment.class
};
- private int mCurrentNavPostition = -1;
+ private int mCurrentNavPosition = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -102,17 +108,22 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
}
});
- getSupportActionBar().setDisplayShowTitleEnabled(false);
+ mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
- setupDrawerNavigation(savedInstanceState);
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
+ setTitle(R.string.nav_import);
+ } else {
+ getSupportActionBar().setDisplayShowTitleEnabled(false);
- // set drop down navigation
- mNavigationStrings = getResources().getStringArray(R.array.import_action_list);
- Context context = getSupportActionBar().getThemedContext();
- ArrayAdapter<CharSequence> navigationAdapter = ArrayAdapter.createFromResource(context,
- R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item);
- getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
- getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this);
+ setupDrawerNavigation(savedInstanceState);
+
+ // set drop down navigation
+ Context context = getSupportActionBar().getThemedContext();
+ ArrayAdapter<CharSequence> navigationAdapter = ArrayAdapter.createFromResource(context,
+ R.array.import_action_list, android.R.layout.simple_spinner_dropdown_item);
+ getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
+ getSupportActionBar().setListNavigationCallbacks(navigationAdapter, this);
+ }
handleActions(savedInstanceState, getIntent());
}
@@ -152,33 +163,52 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// action: directly load data
startListFragment(savedInstanceState, importData, null, null);
}
- } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)) {
- String query = null;
- if (extras.containsKey(EXTRA_QUERY)) {
- query = extras.getString(EXTRA_QUERY);
- } else if (extras.containsKey(EXTRA_KEY_ID)) {
- long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
- if (keyId != 0) {
- query = PgpKeyHelper.convertKeyIdToHex(keyId);
+ } else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)
+ || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(action)) {
+
+ // only used for OpenPgpService
+ if (extras.containsKey(EXTRA_PENDING_INTENT_DATA)) {
+ mPendingIntentData = extras.getParcelable(EXTRA_PENDING_INTENT_DATA);
+ }
+ if (extras.containsKey(EXTRA_QUERY) || extras.containsKey(EXTRA_KEY_ID)) {
+ /* simple search based on query or key id */
+
+ String query = null;
+ if (extras.containsKey(EXTRA_QUERY)) {
+ query = extras.getString(EXTRA_QUERY);
+ } else if (extras.containsKey(EXTRA_KEY_ID)) {
+ long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0);
+ if (keyId != 0) {
+ query = PgpKeyHelper.convertKeyIdToHex(keyId);
+ }
+ }
+
+ if (query != null && query.length() > 0) {
+ // display keyserver fragment with query
+ Bundle args = new Bundle();
+ args.putString(ImportKeysServerFragment.ARG_QUERY, query);
+ loadNavFragment(0, args);
+
+ // action: search immediately
+ startListFragment(savedInstanceState, null, null, query);
+ } else {
+ Log.e(Constants.TAG, "Query is empty!");
+ return;
}
} else if (extras.containsKey(EXTRA_FINGERPRINT)) {
+ /*
+ * search based on fingerprint, here we can enforce a check in the end
+ * if the right key has been downloaded
+ */
+
String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT);
- if (fingerprint != null) {
- query = "0x" + fingerprint;
- }
+ loadFromFingerprint(savedInstanceState, fingerprint);
} else {
Log.e(Constants.TAG,
- "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or 'fingerprint' extra!");
+ "IMPORT_KEY_FROM_KEYSERVER action needs to contain the 'query', 'key_id', or " +
+ "'fingerprint' extra!");
return;
}
-
- // display keyserver fragment with query
- Bundle args = new Bundle();
- args.putString(ImportKeysServerFragment.ARG_QUERY, query);
- loadNavFragment(0, args);
-
- // action: search immediately
- startListFragment(savedInstanceState, null, null, query);
} else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) {
// NOTE: this only displays the appropriate fragment, no actions are taken
@@ -233,14 +263,14 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
* onNavigationItemSelected() should check whether the Fragment is already in existence
* inside your Activity."
* <p/>
- * from http://stackoverflow.com/questions/10983396/fragment-oncreateview-and-onactivitycreated-called-twice/14295474#14295474
+ * from http://stackoverflow.com/a/14295474
* <p/>
* In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint,
* the fragment would be loaded twice resulting in the query being empty after the second load.
* <p/>
* Our solution:
* To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment
- * checks against mCurrentNavPostition.
+ * checks against mCurrentNavPosition.
*
* @param itemPosition
* @param itemId
@@ -256,10 +286,12 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
}
private void loadNavFragment(int itemPosition, Bundle args) {
- if (mCurrentNavPostition != itemPosition) {
- getSupportActionBar().setSelectedNavigationItem(itemPosition);
+ if (mCurrentNavPosition != itemPosition) {
+ if (ActionBar.NAVIGATION_MODE_LIST == getSupportActionBar().getNavigationMode()) {
+ getSupportActionBar().setSelectedNavigationItem(itemPosition);
+ }
loadFragment(NAVIGATION_CLASSES[itemPosition], args, mNavigationStrings[itemPosition]);
- mCurrentNavPostition = itemPosition;
+ mCurrentNavPosition = itemPosition;
}
}
@@ -279,7 +311,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
Log.d(Constants.TAG, "fingerprint: " + fingerprint);
- if (fingerprint.length() < 16) {
+ loadFromFingerprint(savedInstanceState, fingerprint);
+ }
+
+ public void loadFromFingerprint(Bundle savedInstanceState, String fingerprint) {
+ if (fingerprint == null || fingerprint.length() < 40) {
AppMsg.makeText(this, R.string.import_qr_code_too_short_fingerprint,
AppMsg.STYLE_ALERT).show();
return;
@@ -290,6 +326,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
// display keyserver fragment with query
Bundle args = new Bundle();
args.putString(ImportKeysServerFragment.ARG_QUERY, query);
+ args.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true);
loadNavFragment(0, args);
// action: search directly
@@ -300,70 +337,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
mListFragment.loadNew(importData, dataUri, serverQuery, keyServer);
}
- // private void importAndSignOld(final long keyId, final String expectedFingerprint) {
- // if (expectedFingerprint != null && expectedFingerprint.length() > 0) {
- //
- // Thread t = new Thread() {
- // @Override
- // public void run() {
- // try {
- // // TODO: display some sort of spinner here while the user waits
- //
- // // TODO: there should be only 1
- // HkpKeyServer server = new HkpKeyServer(mPreferences.getKeyServers()[0]);
- // String encodedKey = server.get(keyId);
- //
- // PGPKeyRing keyring = PGPHelper.decodeKeyRing(new ByteArrayInputStream(
- // encodedKey.getBytes()));
- // if (keyring != null && keyring instanceof PGPPublicKeyRing) {
- // PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
- //
- // // make sure the fingerprints match before we cache this thing
- // String actualFingerprint = PGPHelper.convertFingerprintToHex(publicKeyRing
- // .getPublicKey().getFingerprint());
- // if (expectedFingerprint.equals(actualFingerprint)) {
- // // store the signed key in our local cache
- // int retval = PGPMain.storeKeyRingInCache(publicKeyRing);
- // if (retval != Id.return_value.ok
- // && retval != Id.return_value.updated) {
- // status.putString(EXTRA_ERROR,
- // "Failed to store signed key in local cache");
- // } else {
- // Intent intent = new Intent(ImportFromQRCodeActivity.this,
- // SignKeyActivity.class);
- // intent.putExtra(EXTRA_KEY_ID, keyId);
- // startActivityForResult(intent, Id.request.sign_key);
- // }
- // } else {
- // status.putString(
- // EXTRA_ERROR,
- // "Scanned fingerprint does NOT match the fingerprint of the received key. You shouldnt trust this key.");
- // }
- // }
- // } catch (QueryException e) {
- // Log.e(TAG, "Failed to query KeyServer", e);
- // status.putString(EXTRA_ERROR, "Failed to query KeyServer");
- // status.putInt(Constants.extras.STATUS, Id.message.done);
- // } catch (IOException e) {
- // Log.e(TAG, "Failed to query KeyServer", e);
- // status.putString(EXTRA_ERROR, "Failed to query KeyServer");
- // status.putInt(Constants.extras.STATUS, Id.message.done);
- // }
- // }
- // };
- //
- // t.setName("KeyExchange Download Thread");
- // t.setDaemon(true);
- // t.start();
- // }
- // }
-
-
/**
* Import keys with mImportData
*/
public void importKeys() {
- // Message is received after importing is done in ApgService
+ // Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
this,
getString(R.string.progress_importing),
@@ -403,6 +381,11 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa
BadImportKeyDialogFragment.newInstance(bad);
badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog");
}
+
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
+ ImportKeysActivity.this.setResult(Activity.RESULT_OK, mPendingIntentData);
+ finish();
+ }
}
}
};
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
index 3f0b4a46e..28e2091a9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysClipboardFragment.java
@@ -17,6 +17,7 @@
package org.sufficientlysecure.keychain.ui;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
@@ -24,9 +25,13 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import java.util.Locale;
+
public class ImportKeysClipboardFragment extends Fragment {
private ImportKeysActivity mImportActivity;
@@ -60,6 +65,10 @@ public class ImportKeysClipboardFragment extends Fragment {
String sendText = "";
if (clipboardText != null) {
sendText = clipboardText.toString();
+ if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) {
+ mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText));
+ return;
+ }
}
mImportActivity.loadCallback(sendText.getBytes(), null, null, null);
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
index 9e8506193..077fa0cab 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java
@@ -29,7 +29,11 @@ import com.devspark.appmsg.AppMsg;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
-import org.sufficientlysecure.keychain.ui.adapter.*;
+import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
+import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.KeyServer;
import org.sufficientlysecure.keychain.util.Log;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
index 44b5848d8..110647284 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java
@@ -57,7 +57,7 @@ public class ImportKeysNFCFragment extends Fragment {
public void onClick(View v) {
// show nfc help
Intent intent = new Intent(getActivity(), HelpActivity.class);
- intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 1);
+ intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 2);
startActivityForResult(intent, 0);
}
});
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
index 10c0752b1..8b553d273 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java
@@ -17,6 +17,8 @@
package org.sufficientlysecure.keychain.ui;
+import com.google.zxing.integration.android.IntentResult;
+
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -28,8 +30,9 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
-import com.google.zxing.integration.android.IntentResult;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index 0b2fff64e..3eb463dac 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.util.Log;
public class ImportKeysServerFragment extends Fragment {
public static final String ARG_QUERY = "query";
public static final String ARG_KEY_SERVER = "key_server";
+ public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit";
private ImportKeysActivity mImportActivity;
@@ -140,6 +141,10 @@ public class ImportKeysServerFragment extends Fragment {
Log.d(Constants.TAG, "keyServer: " + keyServer);
}
+
+ if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) {
+ mQueryEditText.setEnabled(false);
+ }
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 078b998e1..1bc6d4ee1 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -21,8 +21,8 @@ import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
@@ -53,27 +53,21 @@ public class KeyListActivity extends DrawerActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_key_list_import:
- Intent intentImport = new Intent(this, ImportKeysActivity.class);
- startActivityForResult(intentImport, 0);
-
+ callIntentForDrawerItem(Constants.DrawerItems.IMPORT_KEYS);
return true;
- case R.id.menu_key_list_export:
- // TODO fix this for unified keylist
- mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB);
- return true;
case R.id.menu_key_list_create:
createKey();
-
return true;
+
case R.id.menu_key_list_create_expert:
createKeyExpert();
-
return true;
- case R.id.menu_key_list_secret_export:
- mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC);
+ case R.id.menu_key_list_export:
+ mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE_PUB, true);
return true;
+
default:
return super.onOptionsItemSelected(item);
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index a08f4bc74..45db30fe5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -24,7 +24,11 @@ import android.content.Intent;
import android.database.Cursor;
import android.graphics.Color;
import android.net.Uri;
-import android.os.*;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
@@ -33,22 +37,29 @@ import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.text.TextUtils;
-import android.view.*;
+import android.view.ActionMode;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
import android.view.View.OnClickListener;
+import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
-import android.widget.*;
import android.widget.AbsListView.MultiChoiceModeListener;
+import android.widget.AdapterView;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ListView;
+import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
-import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
@@ -56,7 +67,6 @@ import se.emilsjolander.stickylistheaders.ApiLevelTooLowException;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
-import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -142,9 +152,6 @@ public class KeyListFragment extends Fragment
} catch (ApiLevelTooLowException e) {
}
- // this view is made visible if no data is available
- mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
-
/*
* ActionBarSherlock does not support MultiChoiceModeListener. Thus multi-selection is only
* available for Android >= 3.0
@@ -178,18 +185,15 @@ public class KeyListFragment extends Fragment
break;
}
case R.id.menu_key_list_multi_delete: {
- ids = mStickyList.getWrappedList().getCheckedItemIds();
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
showDeleteKeyDialog(mode, ids);
break;
}
case R.id.menu_key_list_multi_export: {
- // todo: public/secret needs to be handled differently here
- ids = mStickyList.getWrappedList().getCheckedItemIds();
+ ids = mAdapter.getCurrentSelectedMasterKeyIds();
ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity());
- mExportHelper
- .showExportKeysDialog(ids,
- Id.type.public_key,
- Constants.Path.APP_DIR_FILE_PUB);
+ mExportHelper.showExportKeysDialog(
+ ids, Constants.Path.APP_DIR_FILE_PUB, mAdapter.isAnySecretSelected());
break;
}
case R.id.menu_key_list_multi_select_all: {
@@ -243,23 +247,17 @@ public class KeyListFragment extends Fragment
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[]{
- KeychainContract.KeyRings._ID,
- KeychainContract.KeyRings.TYPE,
- KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.UserIds.USER_ID,
- KeychainContract.Keys.IS_REVOKED
+ KeyRings._ID,
+ KeyRings.MASTER_KEY_ID,
+ KeyRings.USER_ID,
+ KeyRings.IS_REVOKED,
+ KeyRings.HAS_SECRET
};
- static final int INDEX_TYPE = 1;
- static final int INDEX_MASTER_KEY_ID = 2;
- static final int INDEX_USER_ID = 3;
- static final int INDEX_IS_REVOKED = 4;
-
- static final String SORT_ORDER =
- // show secret before public key
- KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings.TYPE + " DESC, "
- // sort by user id otherwise
- + UserIds.USER_ID + " ASC";
+ static final int INDEX_MASTER_KEY_ID = 1;
+ static final int INDEX_USER_ID = 2;
+ static final int INDEX_IS_REVOKED = 3;
+ static final int INDEX_HAS_SECRET = 4;
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
@@ -269,12 +267,12 @@ public class KeyListFragment extends Fragment
String where = null;
String whereArgs[] = null;
if (mCurQuery != null) {
- where = KeychainContract.UserIds.USER_ID + " LIKE ?";
+ where = KeyRings.USER_ID + " LIKE ?";
whereArgs = new String[]{"%" + mCurQuery + "%"};
}
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, SORT_ORDER);
+ return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, null);
}
@Override
@@ -286,6 +284,9 @@ public class KeyListFragment extends Fragment
mStickyList.setAdapter(mAdapter);
+ // this view is made visible if no data is available
+ mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
+
// NOTE: Not supported by StickyListHeader, but reimplemented here
// The list should now be shown.
if (isResumed()) {
@@ -315,17 +316,15 @@ public class KeyListFragment extends Fragment
viewIntent = new Intent(getActivity(), ViewKeyActivityJB.class);
}
viewIntent.setData(
- KeychainContract
- .KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
- Long.toString(mAdapter.getMasterKeyId(position))));
+ KeyRings.buildGenericKeyRingUri(Long.toString(mAdapter.getMasterKeyId(position))));
startActivity(viewIntent);
}
@TargetApi(11)
- protected void encrypt(ActionMode mode, long[] keyRingMasterKeyIds) {
+ protected void encrypt(ActionMode mode, long[] masterKeyIds) {
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
- intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, keyRingMasterKeyIds);
+ intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, masterKeyIds);
// used instead of startActivity set actionbar based on callingPackage
startActivityForResult(intent, 0);
@@ -335,35 +334,17 @@ public class KeyListFragment extends Fragment
/**
* Show dialog to delete key
*
- * @param keyRingRowIds
+ * @param masterKeyIds
*/
@TargetApi(11)
// TODO: this method needs an overhaul to handle both public and secret keys gracefully!
- public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) {
+ public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds) {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- Bundle returnData = message.getData();
- if (returnData != null
- && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) {
- ArrayList<String> notDeleted =
- returnData.getStringArrayList(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED);
- String notDeletedMsg = "";
- for (String userId : notDeleted) {
- notDeletedMsg += userId + "\n";
- }
- Toast.makeText(getActivity(),
- getString(R.string.error_can_not_delete_contacts, notDeletedMsg)
- + getResources()
- .getQuantityString(
- R.plurals.error_can_not_delete_info,
- notDeleted.size()),
- Toast.LENGTH_LONG).show();
-
- mode.finish();
- }
+ mode.finish();
}
}
};
@@ -372,7 +353,7 @@ public class KeyListFragment extends Fragment
Messenger messenger = new Messenger(returnHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
- keyRingRowIds, Id.type.public_key);
+ masterKeyIds);
deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog");
}
@@ -506,11 +487,15 @@ public class KeyListFragment extends Fragment
}
{ // set edit button and revoked info, specific by key type
+ View statusDivider = (View) view.findViewById(R.id.status_divider);
+ FrameLayout statusLayout = (FrameLayout) view.findViewById(R.id.status_layout);
Button button = (Button) view.findViewById(R.id.edit);
TextView revoked = (TextView) view.findViewById(R.id.revoked);
- if (cursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
+ if (cursor.getInt(KeyListFragment.INDEX_HAS_SECRET) != 0) {
// this is a secret key - show the edit button
+ statusDivider.setVisibility(View.VISIBLE);
+ statusLayout.setVisibility(View.VISIBLE);
revoked.setVisibility(View.GONE);
button.setVisibility(View.VISIBLE);
@@ -518,26 +503,31 @@ public class KeyListFragment extends Fragment
button.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
- editIntent.setData(
- KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(
- Long.toString(id)
- )
- );
+ editIntent.setData(KeyRingData.buildSecretKeyRingUri(Long.toString(id)));
editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
startActivityForResult(editIntent, 0);
}
});
} else {
// this is a public key - hide the edit button, show if it's revoked
+ statusDivider.setVisibility(View.GONE);
button.setVisibility(View.GONE);
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
+ statusLayout.setVisibility(isRevoked ? View.VISIBLE : View.GONE);
revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE);
}
}
}
+ public boolean isSecretAvailable(int id) {
+ if (!mCursor.moveToPosition(id)) {
+ throw new IllegalStateException("couldn't move cursor to position " + id);
+ }
+
+ return mCursor.getInt(INDEX_HAS_SECRET) != 0;
+ }
public long getMasterKeyId(int id) {
if (!mCursor.moveToPosition(id)) {
throw new IllegalStateException("couldn't move cursor to position " + id);
@@ -581,7 +571,7 @@ public class KeyListFragment extends Fragment
throw new IllegalStateException("couldn't move cursor to position " + position);
}
- if (mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
+ if (mCursor.getInt(KeyListFragment.INDEX_HAS_SECRET) != 0) {
{ // set contact count
int num = mCursor.getCount();
String contactsTotal = getResources().getQuantityString(R.plurals.n_contacts, num, num);
@@ -620,7 +610,7 @@ public class KeyListFragment extends Fragment
}
// early breakout: all secret keys are assigned id 0
- if (mCursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) {
+ if (mCursor.getInt(KeyListFragment.INDEX_HAS_SECRET) != 0) {
return 1L;
}
// otherwise, return the first character of the name as ID
@@ -645,6 +635,14 @@ public class KeyListFragment extends Fragment
notifyDataSetChanged();
}
+ public boolean isAnySecretSelected() {
+ for (int pos : mSelection.keySet()) {
+ if(mAdapter.isSecretAvailable(pos))
+ return true;
+ }
+ return false;
+ }
+
public long[] getCurrentSelectedMasterKeyIds() {
long[] ids = new long[mSelection.size()];
int i = 0;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index 04179cb80..265bb2139 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -20,9 +20,15 @@ import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
-import android.preference.*;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
@@ -38,11 +44,11 @@ public class PreferencesActivity extends PreferenceActivity {
public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
private PreferenceScreen mKeyServerPreference = null;
- private static Preferences mPreferences;
+ private static Preferences sPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
- mPreferences = Preferences.getPreferences(this);
+ sPreferences = Preferences.getPreferences(this);
super.onCreate(savedInstanceState);
// final ActionBar actionBar = getSupportActionBar();
@@ -55,11 +61,11 @@ public class PreferencesActivity extends PreferenceActivity {
if (action != null && action.equals(ACTION_PREFS_GEN)) {
addPreferencesFromResource(R.xml.gen_preferences);
- initializePassPassPhraceCacheTtl(
- (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL));
+ initializePassPassphraceCacheTtl(
+ (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
- String servers[] = mPreferences.getKeyServers();
+ String servers[] = sPreferences.getKeyServers();
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
servers.length, servers.length));
mKeyServerPreference
@@ -68,7 +74,7 @@ public class PreferencesActivity extends PreferenceActivity {
Intent intent = new Intent(PreferencesActivity.this,
PreferencesKeyServerActivity.class);
intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
- mPreferences.getKeyServers());
+ sPreferences.getKeyServers());
startActivityForResult(intent, Id.request.key_server_preference);
return false;
}
@@ -104,8 +110,8 @@ public class PreferencesActivity extends PreferenceActivity {
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
entries, values);
- initializeAsciiArmour(
- (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR));
+ initializeAsciiArmor(
+ (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
@@ -125,7 +131,7 @@ public class PreferencesActivity extends PreferenceActivity {
}
String servers[] = data
.getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
- mPreferences.setKeyServers(servers);
+ sPreferences.setKeyServers(servers);
mKeyServerPreference.setSummary(getResources().getQuantityString(
R.plurals.n_key_servers, servers.length, servers.length));
break;
@@ -159,11 +165,11 @@ public class PreferencesActivity extends PreferenceActivity {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.gen_preferences);
- initializePassPassPhraceCacheTtl(
- (IntegerListPreference) findPreference(Constants.Pref.PASS_PHRASE_CACHE_TTL));
+ initializePassPassphraceCacheTtl(
+ (IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS);
- String servers[] = mPreferences.getKeyServers();
+ String servers[] = sPreferences.getKeyServers();
mKeyServerPreference.setSummary(getResources().getQuantityString(R.plurals.n_key_servers,
servers.length, servers.length));
mKeyServerPreference
@@ -172,7 +178,7 @@ public class PreferencesActivity extends PreferenceActivity {
Intent intent = new Intent(getActivity(),
PreferencesKeyServerActivity.class);
intent.putExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS,
- mPreferences.getKeyServers());
+ sPreferences.getKeyServers());
startActivityForResult(intent, Id.request.key_server_preference);
return false;
}
@@ -188,7 +194,7 @@ public class PreferencesActivity extends PreferenceActivity {
}
String servers[] = data
.getStringArrayExtra(PreferencesKeyServerActivity.EXTRA_KEY_SERVERS);
- mPreferences.setKeyServers(servers);
+ sPreferences.setKeyServers(servers);
mKeyServerPreference.setSummary(getResources().getQuantityString(
R.plurals.n_key_servers, servers.length, servers.length));
break;
@@ -241,8 +247,8 @@ public class PreferencesActivity extends PreferenceActivity {
(IntegerListPreference) findPreference(Constants.Pref.DEFAULT_FILE_COMPRESSION),
entries, values);
- initializeAsciiArmour(
- (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOUR));
+ initializeAsciiArmor(
+ (CheckBoxPreference) findPreference(Constants.Pref.DEFAULT_ASCII_ARMOR));
initializeForceV3Signatures(
(CheckBoxPreference) findPreference(Constants.Pref.FORCE_V3_SIGNATURES));
@@ -255,15 +261,15 @@ public class PreferencesActivity extends PreferenceActivity {
|| super.isValidFragment(fragmentName);
}
- private static void initializePassPassPhraceCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
- mPassphraseCacheTtl.setValue("" + mPreferences.getPassPhraseCacheTtl());
+ private static void initializePassPassphraceCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
+ mPassphraseCacheTtl.setValue("" + sPreferences.getPassphraseCacheTtl());
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
mPassphraseCacheTtl
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mPassphraseCacheTtl.setValue(newValue.toString());
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
- mPreferences.setPassPhraseCacheTtl(Integer.parseInt(newValue.toString()));
+ sPreferences.setPassphraseCacheTtl(Integer.parseInt(newValue.toString()));
return false;
}
});
@@ -282,14 +288,14 @@ public class PreferencesActivity extends PreferenceActivity {
}
mEncryptionAlgorithm.setEntries(entries);
mEncryptionAlgorithm.setEntryValues(values);
- mEncryptionAlgorithm.setValue("" + mPreferences.getDefaultEncryptionAlgorithm());
+ mEncryptionAlgorithm.setValue("" + sPreferences.getDefaultEncryptionAlgorithm());
mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
mEncryptionAlgorithm
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mEncryptionAlgorithm.setValue(newValue.toString());
mEncryptionAlgorithm.setSummary(mEncryptionAlgorithm.getEntry());
- mPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue
+ sPreferences.setDefaultEncryptionAlgorithm(Integer.parseInt(newValue
.toString()));
return false;
}
@@ -309,13 +315,13 @@ public class PreferencesActivity extends PreferenceActivity {
}
mHashAlgorithm.setEntries(entries);
mHashAlgorithm.setEntryValues(values);
- mHashAlgorithm.setValue("" + mPreferences.getDefaultHashAlgorithm());
+ mHashAlgorithm.setValue("" + sPreferences.getDefaultHashAlgorithm());
mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
mHashAlgorithm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mHashAlgorithm.setValue(newValue.toString());
mHashAlgorithm.setSummary(mHashAlgorithm.getEntry());
- mPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
+ sPreferences.setDefaultHashAlgorithm(Integer.parseInt(newValue.toString()));
return false;
}
});
@@ -326,14 +332,14 @@ public class PreferencesActivity extends PreferenceActivity {
int[] valueIds, String[] entries, String[] values) {
mMessageCompression.setEntries(entries);
mMessageCompression.setEntryValues(values);
- mMessageCompression.setValue("" + mPreferences.getDefaultMessageCompression());
+ mMessageCompression.setValue("" + sPreferences.getDefaultMessageCompression());
mMessageCompression.setSummary(mMessageCompression.getEntry());
mMessageCompression
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mMessageCompression.setValue(newValue.toString());
mMessageCompression.setSummary(mMessageCompression.getEntry());
- mPreferences.setDefaultMessageCompression(Integer.parseInt(newValue
+ sPreferences.setDefaultMessageCompression(Integer.parseInt(newValue
.toString()));
return false;
}
@@ -344,36 +350,36 @@ public class PreferencesActivity extends PreferenceActivity {
(final IntegerListPreference mFileCompression, String[] entries, String[] values) {
mFileCompression.setEntries(entries);
mFileCompression.setEntryValues(values);
- mFileCompression.setValue("" + mPreferences.getDefaultFileCompression());
+ mFileCompression.setValue("" + sPreferences.getDefaultFileCompression());
mFileCompression.setSummary(mFileCompression.getEntry());
mFileCompression.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mFileCompression.setValue(newValue.toString());
mFileCompression.setSummary(mFileCompression.getEntry());
- mPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
+ sPreferences.setDefaultFileCompression(Integer.parseInt(newValue.toString()));
return false;
}
});
}
- private static void initializeAsciiArmour(final CheckBoxPreference mAsciiArmour) {
- mAsciiArmour.setChecked(mPreferences.getDefaultAsciiArmour());
- mAsciiArmour.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ private static void initializeAsciiArmor(final CheckBoxPreference mAsciiArmor) {
+ mAsciiArmor.setChecked(sPreferences.getDefaultAsciiArmor());
+ mAsciiArmor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
- mAsciiArmour.setChecked((Boolean) newValue);
- mPreferences.setDefaultAsciiArmour((Boolean) newValue);
+ mAsciiArmor.setChecked((Boolean) newValue);
+ sPreferences.setDefaultAsciiArmor((Boolean) newValue);
return false;
}
});
}
private static void initializeForceV3Signatures(final CheckBoxPreference mForceV3Signatures) {
- mForceV3Signatures.setChecked(mPreferences.getForceV3Signatures());
+ mForceV3Signatures.setChecked(sPreferences.getForceV3Signatures());
mForceV3Signatures
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue) {
mForceV3Signatures.setChecked((Boolean) newValue);
- mPreferences.setForceV3Signatures((Boolean) newValue);
+ sPreferences.setForceV3Signatures((Boolean) newValue);
return false;
}
});
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
index d890f35cb..719378274 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java
@@ -91,10 +91,15 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O
}
}
- public void onDeleted(Editor editor) {
+ public void onDeleted(Editor editor, boolean wasNewItem) {
// nothing to do
}
+ @Override
+ public void onEdited() {
+
+ }
+
public void onClick(View v) {
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor,
mEditors, false);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
index 6ab9f1c6e..9bfe3eaa9 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectPublicKeyFragment.java
@@ -32,7 +32,13 @@ import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ListFragmentWorkaround;
@@ -248,9 +254,7 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- // This is called when a new Loader needs to be created. This
- // sample only has one Loader, so we don't care about the ID.
- Uri baseUri = KeyRings.buildPublicKeyRingsUri();
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
// These are the rows that we will retrieve.
long now = new Date().getTime() / 1000;
@@ -258,24 +262,24 @@ public class SelectPublicKeyFragment extends ListFragmentWorkaround implements T
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
UserIds.USER_ID,
- "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys."
- + Keys.CAN_ENCRYPT + " = '1') AS "
- + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
- "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND valid_keys." + Keys.IS_REVOKED + " = '0' AND valid_keys."
- + Keys.CAN_ENCRYPT + " = '1' AND valid_keys." + Keys.CREATION + " <= '"
- + now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys."
- + Keys.EXPIRY + " >= '" + now + "')) AS "
- + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ +" WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_ENCRYPT + " = '1'"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ + " WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_ENCRYPT + " = '1'"
+ + " AND k." + Keys.CREATION + " <= '" + now + "'"
+ + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY + " >= '" + now + "' )"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
String inMasterKeyList = null;
if (mSelectedMasterKeyIds != null && mSelectedMasterKeyIds.length > 0) {
- inMasterKeyList = KeyRings.MASTER_KEY_ID + " IN (";
+ inMasterKeyList = Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + " IN (";
for (int i = 0; i < mSelectedMasterKeyIds.length; ++i) {
if (i != 0) {
inMasterKeyList += ", ";
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
index 1509bc88c..0ff88d97c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java
@@ -1,42 +1,37 @@
/*
- * Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
-import android.view.Menu;
-import org.sufficientlysecure.keychain.Constants;
+
import org.sufficientlysecure.keychain.R;
public class SelectSecretKeyActivity extends ActionBarActivity {
- // Actions for internal use only:
- public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX
- + "SELECT_SECRET_KEYRING";
-
public static final String EXTRA_FILTER_CERTIFY = "filter_certify";
public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id";
- public static final String RESULT_EXTRA_USER_ID = "user_id";
- private boolean mFilterCertify = false;
+ private boolean mFilterCertify;
private SelectSecretKeyFragment mSelectFragment;
@Override
@@ -50,23 +45,8 @@ public class SelectSecretKeyActivity extends ActionBarActivity {
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setHomeButtonEnabled(false);
- setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
-
- // TODO: reimplement!
- // mFilterLayout = findViewById(R.id.layout_filter);
- // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo);
- // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear);
- //
- // mClearFilterButton.setOnClickListener(new OnClickListener() {
- // public void onClick(View v) {
- // handleIntent(new Intent());
- // }
- // });
-
mFilterCertify = getIntent().getBooleanExtra(EXTRA_FILTER_CERTIFY, false);
- handleIntent(getIntent());
-
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.select_secret_key_fragment_container) != null) {
@@ -90,48 +70,14 @@ public class SelectSecretKeyActivity extends ActionBarActivity {
/**
* This is executed by SelectSecretKeyFragment after clicking on an item
*
- * @param masterKeyId
- * @param userId
+ * @param selectedUri
*/
- public void afterListSelection(long masterKeyId, String userId) {
+ public void afterListSelection(Uri selectedUri) {
Intent data = new Intent();
- data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId);
- data.putExtra(RESULT_EXTRA_USER_ID, (String) userId);
+ data.setData(selectedUri);
+
setResult(RESULT_OK, data);
finish();
}
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- // TODO: reimplement!
-
- // String searchString = null;
- // if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
- // searchString = intent.getStringExtra(SearchManager.QUERY);
- // if (searchString != null && searchString.trim().length() == 0) {
- // searchString = null;
- // }
- // }
-
- // if (searchString == null) {
- // mFilterLayout.setVisibility(View.GONE);
- // } else {
- // mFilterLayout.setVisibility(View.VISIBLE);
- // mFilterInfo.setText(getString(R.string.filterInfo, searchString));
- // }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // TODO: reimplement!
- // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon(
- // android.R.drawable.ic_menu_search);
- return true;
- }
-
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
index 47a3fbad3..9987facbc 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,6 +28,7 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -55,10 +56,9 @@ public class SelectSecretKeyFragment extends ListFragment implements
*/
public static SelectSecretKeyFragment newInstance(boolean filterCertify) {
SelectSecretKeyFragment frag = new SelectSecretKeyFragment();
- Bundle args = new Bundle();
+ Bundle args = new Bundle();
args.putBoolean(ARG_FILTER_CERTIFY, filterCertify);
-
frag.setArguments(args);
return frag;
@@ -85,10 +85,10 @@ public class SelectSecretKeyFragment extends ListFragment implements
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
long masterKeyId = mAdapter.getMasterKeyId(position);
- String userId = mAdapter.getUserId(position);
+ Uri result = KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
// return data to activity, which results in finishing it
- mActivity.afterListSelection(masterKeyId, userId);
+ mActivity.afterListSelection(result);
}
});
@@ -112,12 +112,7 @@ public class SelectSecretKeyFragment extends ListFragment implements
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// This is called when a new Loader needs to be created. This
// sample only has one Loader, so we don't care about the ID.
- Uri baseUri = KeyRings.buildSecretKeyRingsUri();
-
- String capFilter = null;
- if (mFilterCertify) {
- capFilter = "(cert > 0)";
- }
+ Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
// These are the rows that we will retrieve.
long now = new Date().getTime() / 1000;
@@ -125,29 +120,36 @@ public class SelectSecretKeyFragment extends ListFragment implements
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
UserIds.USER_ID,
- "(SELECT COUNT(cert_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS cert_keys WHERE cert_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND cert_keys."
- + Keys.CAN_CERTIFY + " = '1') AS cert",
- "(SELECT COUNT(available_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS available_keys WHERE available_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID
- + " AND available_keys." + Keys.IS_REVOKED + " = '0' AND available_keys."
- + Keys.CAN_SIGN + " = '1') AS "
- + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
- "(SELECT COUNT(valid_keys." + Keys._ID + ") FROM " + Tables.KEYS
- + " AS valid_keys WHERE valid_keys." + Keys.KEY_RING_ROW_ID + " = "
- + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND valid_keys."
- + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN
- + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND "
- + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY
- + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ + " WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + KeyRings.MASTER_KEY_ID
+ + " AND k." + Keys.CAN_CERTIFY + " = '1'"
+ + ") AS cert",
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ +" WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_SIGN + " = '1'"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_AVAILABLE,
+ "(SELECT COUNT(*) FROM " + Tables.KEYS + " AS k"
+ + " WHERE k." + Keys.MASTER_KEY_ID + " = "
+ + KeychainDatabase.Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND k." + Keys.IS_REVOKED + " = '0'"
+ + " AND k." + Keys.CAN_SIGN + " = '1'"
+ + " AND k." + Keys.CREATION + " <= '" + now + "'"
+ + " AND ( k." + Keys.EXPIRY + " IS NULL OR k." + Keys.EXPIRY + " >= '" + now + "' )"
+ + ") AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, };
String orderBy = UserIds.USER_ID + " ASC";
+ String where = Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NOT NULL";
+ if (mFilterCertify) {
+ where += " AND (cert > 0)";
+ }
+
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, projection, capFilter, null, orderBy);
+ return new CursorLoader(getActivity(), baseUri, projection, where, null, orderBy);
}
@Override
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
index 3d22ca9f6..514951385 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java
@@ -19,22 +19,26 @@ package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
-import org.spongycastle.openpgp.PGPSecretKey;
-import org.spongycastle.openpgp.PGPSecretKeyRing;
-import org.sufficientlysecure.keychain.Id;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
-public class SelectSecretKeyLayoutFragment extends Fragment {
+public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private TextView mKeyUserId;
private TextView mKeyUserIdRest;
@@ -43,10 +47,23 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
private BootstrapButton mSelectKeyButton;
private Boolean mFilterCertify;
+ private Uri mReceivedUri = null;
+
private SelectSecretKeyCallback mCallback;
private static final int REQUEST_CODE_SELECT_KEY = 8882;
+ private static final int LOADER_ID = 0;
+
+ //The Projection we will retrieve, Master Key ID is for convenience sake,
+ //to avoid having to pass the Key Around
+ final String[] PROJECTION = new String[] {
+ KeychainContract.Keys.MASTER_KEY_ID,
+ KeychainContract.UserIds.USER_ID
+ };
+ final int INDEX_MASTER_KEY_ID = 0;
+ final int INDEX_USER_ID = 1;
+
public interface SelectSecretKeyCallback {
void onKeySelected(long secretKeyId);
}
@@ -59,68 +76,30 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
mFilterCertify = filterCertify;
}
- public void selectKey(long secretKeyId) {
- if (secretKeyId == Id.key.none) {
- mNoKeySelected.setVisibility(View.VISIBLE);
- mKeyUserId.setVisibility(View.GONE);
- mKeyUserIdRest.setVisibility(View.GONE);
- mKeyMasterKeyIdHex.setVisibility(View.GONE);
+ public void setNoKeySelected() {
+ mNoKeySelected.setVisibility(View.VISIBLE);
+ mKeyUserId.setVisibility(View.GONE);
+ mKeyUserIdRest.setVisibility(View.GONE);
+ mKeyMasterKeyIdHex.setVisibility(View.GONE);
+ }
- } else {
- PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(
- getActivity(), secretKeyId);
- if (keyRing != null) {
- PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing);
- String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHex(secretKeyId);
-
- if (key != null) {
- String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key);
-
- String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
- String userName, userEmail;
-
- if (userIdSplit[0] != null) {
- userName = userIdSplit[0];
- } else {
- userName = getActivity().getResources().getString(R.string.user_id_no_name);
- }
-
- if (userIdSplit[1] != null) {
- userEmail = userIdSplit[1];
- } else {
- userEmail = getActivity().getResources().getString(R.string.error_user_id_no_email);
- }
-
- mKeyMasterKeyIdHex.setText(masterkeyIdHex);
- mKeyUserId.setText(userName);
- mKeyUserIdRest.setText(userEmail);
- mKeyMasterKeyIdHex.setVisibility(View.VISIBLE);
- mKeyUserId.setVisibility(View.VISIBLE);
- mKeyUserIdRest.setVisibility(View.VISIBLE);
- mNoKeySelected.setVisibility(View.GONE);
- } else {
- mKeyMasterKeyIdHex.setVisibility(View.GONE);
- mKeyUserId.setVisibility(View.GONE);
- mKeyUserIdRest.setVisibility(View.GONE);
- mNoKeySelected.setVisibility(View.VISIBLE);
- }
- } else {
- mKeyMasterKeyIdHex.setText(
- getActivity().getResources()
- .getString(R.string.no_keys_added_or_updated)
- + " for master id: " + secretKeyId);
- mKeyMasterKeyIdHex.setVisibility(View.VISIBLE);
- mKeyUserId.setVisibility(View.GONE);
- mKeyUserIdRest.setVisibility(View.GONE);
- mNoKeySelected.setVisibility(View.GONE);
- }
+ public void setSelectedKeyData(String userName, String email, String masterKeyHex) {
+
+ mNoKeySelected.setVisibility(View.GONE);
+
+ mKeyUserId.setText(userName);
+ mKeyUserIdRest.setText(email);
+ mKeyMasterKeyIdHex.setText(masterKeyHex);
+
+ mKeyUserId.setVisibility(View.VISIBLE);
+ mKeyUserIdRest.setVisibility(View.VISIBLE);
+ mKeyMasterKeyIdHex.setVisibility(View.VISIBLE);
- }
}
public void setError(String error) {
- mKeyUserId.requestFocus();
- mKeyUserId.setError(error);
+ mNoKeySelected.requestFocus();
+ mNoKeySelected.setError(error);
}
/**
@@ -147,29 +126,78 @@ public class SelectSecretKeyLayoutFragment extends Fragment {
return view;
}
+ //For AppSettingsFragment
+ public void selectKey(long masterKeyId) {
+ Uri buildUri = KeychainContract.KeyRings.buildGenericKeyRingUri(String.valueOf(masterKeyId));
+ mReceivedUri = buildUri;
+ getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this);
+ }
+
private void startSelectKeyActivity() {
Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class);
intent.putExtra(SelectSecretKeyActivity.EXTRA_FILTER_CERTIFY, mFilterCertify);
startActivityForResult(intent, REQUEST_CODE_SELECT_KEY);
}
- // Select Secret Key Activity delivers the intent which was sent by it using interface to Select
- // Secret Key Fragment.Intent contains Master Key Id, User Email, User Name, Master Key Id Hex.
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ Uri uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mReceivedUri);
+ //We don't care about the Loader id
+ return new CursorLoader(getActivity(), uri, PROJECTION, null, null, null);
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ if (data.moveToFirst()) {
+ String userName, email, masterKeyHex;
+ String userID = data.getString(INDEX_USER_ID);
+ long masterKeyID = data.getLong(INDEX_MASTER_KEY_ID);
+
+ String splitUserID[] = PgpKeyHelper.splitUserId(userID);
+
+ if (splitUserID[0] != null) {
+ userName = splitUserID[0];
+ } else {
+ userName = getActivity().getResources().getString(R.string.user_id_no_name);
+ }
+
+ if (splitUserID[1] != null) {
+ email = splitUserID[1];
+ } else {
+ email = getActivity().getResources().getString(R.string.error_user_id_no_email);
+ }
+
+ //TODO Can the cursor return invalid values for the Master Key ?
+ masterKeyHex = PgpKeyHelper.convertKeyIdToHexShort(masterKeyID);
+
+ //Set the data
+ setSelectedKeyData(userName, email, masterKeyHex);
+
+ //Give value to the callback
+ mCallback.onKeySelected(masterKeyID);
+ } else {
+ //Set The empty View
+ setNoKeySelected();
+ }
+
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ return;
+ }
+
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch (requestCode & 0xFFFF) {
+ switch (requestCode) {
case REQUEST_CODE_SELECT_KEY: {
- long secretKeyId;
if (resultCode == Activity.RESULT_OK) {
- Bundle bundle = data.getExtras();
- secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID);
- selectKey(secretKeyId);
+ mReceivedUri = data.getData();
- // remove displayed errors
- mKeyUserId.setError(null);
+ //Must be restartLoader() or the data will not be updated on selecting a new key
+ getActivity().getSupportLoaderManager().restartLoader(0, null, this);
- // give value back to callback
- mCallback.onKeySelected(secretKeyId);
+ mKeyUserId.setError(null);
}
break;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
index 2c8f66488..0e231e6a8 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java
@@ -98,11 +98,11 @@ public class UploadKeyActivity extends ActionBarActivity {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after uploading is done in ApgService
+ // Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this,
getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index 611864d8e..b273955dd 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -37,7 +37,9 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Date;
@@ -50,15 +52,14 @@ public class ViewCertActivity extends ActionBarActivity
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] {
- KeychainContract.Certs._ID,
- KeychainContract.Certs.KEY_ID,
- KeychainContract.UserIds.USER_ID,
- KeychainContract.Certs.CREATION,
- KeychainContract.Certs.KEY_ID_CERTIFIER,
- "signer_uid",
- KeychainContract.Certs.KEY_DATA
+ Certs.MASTER_KEY_ID,
+ Certs.USER_ID,
+ Certs.CREATION,
+ Certs.KEY_ID_CERTIFIER,
+ Certs.SIGNER_UID,
+ Certs.KEY_DATA
};
- private static final int INDEX_KEY_ID = 1;
+ private static final int INDEX_MASTER_KEY_ID = 1;
private static final int INDEX_USER_ID = 2;
private static final int INDEX_CREATION = 3;
private static final int INDEX_KEY_ID_CERTIFIER = 4;
@@ -112,7 +113,7 @@ public class ViewCertActivity extends ActionBarActivity
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if(data.moveToFirst()) {
- String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(INDEX_KEY_ID));
+ String signeeKey = "0x" + PgpKeyHelper.convertKeyIdToHex(data.getLong(INDEX_MASTER_KEY_ID));
mSigneeKey.setText(signeeKey);
String signeeUid = data.getString(INDEX_USER_ID);
@@ -179,17 +180,21 @@ public class ViewCertActivity extends ActionBarActivity
return true;
case R.id.menu_view_cert_view_signer:
// can't do this before the data is initialized
- // TODO notify user of this, maybe offer download?
- if(mSignerKeyId == 0)
- return true;
Intent viewIntent = null;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
viewIntent = new Intent(this, ViewKeyActivity.class);
} else {
viewIntent = new Intent(this, ViewKeyActivityJB.class);
}
- viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
- Long.toString(mSignerKeyId))
+ //
+ long signerMasterKeyId = ProviderHelper.getMasterKeyId(this,
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(mSignerKeyId))
+ );
+ // TODO notify user of this, maybe offer download?
+ if(mSignerKeyId == 0L)
+ return true;
+ viewIntent.setData(KeyRings.buildGenericKeyRingUri(
+ Long.toString(signerMasterKeyId))
);
startActivity(viewIntent);
return true;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 592c4890b..7b9ba4b2d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.ui;
+import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -28,6 +29,7 @@ import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.Window;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -38,10 +40,8 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.TabsAdapter;
-import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareNfcDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareQrCodeDialogFragment;
-import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -60,6 +60,7 @@ public class ViewKeyActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
+ requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
super.onCreate(savedInstanceState);
mExportHelper = new ExportHelper(this);
@@ -83,11 +84,7 @@ public class ViewKeyActivity extends ActionBarActivity {
selectedTab = intent.getExtras().getInt(EXTRA_SELECTED_TAB);
}
- // normalize mDataUri to a "by row id" query, to ensure it works with any
- // given valid /public/ query
- long rowId = ProviderHelper.getRowId(this, getIntent().getData());
- // TODO: handle (rowId == 0) with something else than a crash
- mDataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(rowId));
+ mDataUri = getIntent().getData();
Bundle mainBundle = new Bundle();
mainBundle.putParcelable(ViewKeyMainFragment.ARG_DATA_URI, mDataUri);
@@ -95,7 +92,7 @@ public class ViewKeyActivity extends ActionBarActivity {
ViewKeyMainFragment.class, mainBundle, (selectedTab == 0));
Bundle certBundle = new Bundle();
- certBundle.putLong(ViewKeyCertsFragment.ARG_KEYRING_ROW_ID, rowId);
+ certBundle.putParcelable(ViewKeyCertsFragment.ARG_DATA_URI, mDataUri);
mTabsAdapter.addTab(actionBar.newTab().setText(getString(R.string.key_view_tab_certs)),
ViewKeyCertsFragment.class, certBundle, (selectedTab == 1));
}
@@ -122,8 +119,12 @@ public class ViewKeyActivity extends ActionBarActivity {
uploadToKeyserver(mDataUri);
return true;
case R.id.menu_key_view_export_file:
- long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())};
- mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB);
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
+ mExportHelper.showExportKeysDialog(
+ new long[] { masterKeyId } , Constants.Path.APP_DIR_FILE_PUB,
+ // TODO this doesn't work?
+ ((ViewKeyMainFragment) mTabsAdapter.getItem(0)).isSecretAvailable()
+ );
return true;
case R.id.menu_key_view_share_default_fingerprint:
shareKey(mDataUri, true);
@@ -158,33 +159,37 @@ public class ViewKeyActivity extends ActionBarActivity {
}
private void updateFromKeyserver(Uri dataUri) {
- long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri);
-
- if (updateKeyId == 0) {
- Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
- return;
- }
+ byte[] blob = (byte[]) ProviderHelper.getGenericData(
+ this, 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);
- queryIntent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, updateKeyId);
+ queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);
- // TODO: lookup with onactivityresult!
startActivityForResult(queryIntent, RESULT_CODE_LOOKUP_KEY);
}
private void shareKey(Uri dataUri, boolean fingerprintOnly) {
String content;
if (fingerprintOnly) {
- byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
-
- content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ byte[] data = (byte[]) ProviderHelper.getGenericData(
+ this, KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeychainContract.Keys.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ if(data != null) {
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(data);
+ content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
+ } else {
+ Toast.makeText(getApplicationContext(), "Bad key selected!",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
} else {
// get public keyring as ascii armored string
long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this,
- dataUri, new long[]{masterKeyId});
+ ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
+ this, new long[]{ masterKeyId });
content = keyringArmored.get(0);
@@ -214,8 +219,8 @@ public class ViewKeyActivity extends ActionBarActivity {
private void copyToClipboard(Uri dataUri) {
// get public keyring as ascii armored string
long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(this, dataUri,
- new long[]{masterKeyId});
+ ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
+ this, new long[]{ masterKeyId });
ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
Toast.makeText(getApplicationContext(), R.string.key_copied_to_clipboard, Toast.LENGTH_LONG)
@@ -232,25 +237,29 @@ public class ViewKeyActivity extends ActionBarActivity {
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
- if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) {
- Bundle returnData = message.getData();
- if (returnData != null
- && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) {
- // we delete only this key, so MESSAGE_NOT_DELETED will solely contain this key
- Toast.makeText(ViewKeyActivity.this,
- getString(R.string.error_can_not_delete_contact)
- + getResources()
- .getQuantityString(R.plurals.error_can_not_delete_info, 1),
- Toast.LENGTH_LONG).show();
- } else {
- setResult(RESULT_CANCELED);
- finish();
- }
- }
+ setResult(RESULT_CANCELED);
+ finish();
}
};
- mExportHelper.deleteKey(dataUri, Id.type.public_key, returnHandler);
+ mExportHelper.deleteKey(dataUri, returnHandler);
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case RESULT_CODE_LOOKUP_KEY: {
+ if (resultCode == Activity.RESULT_OK) {
+ // TODO: reload key??? move this into fragment?
+ }
+ break;
+ }
+
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ break;
+ }
+ }
+ }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
index 997ff9c7a..6ce7d9aa8 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivityJB.java
@@ -34,6 +34,9 @@ import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.io.IOException;
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMessageCallback,
@@ -47,26 +50,18 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- initNfc(mDataUri);
}
/**
* NFC: Initialize NFC sharing if OS and device supports it
*/
- private void initNfc(Uri dataUri) {
+ private void initNfc() {
// check if NFC Beam is supported (>= Android 4.1)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// Check for available NFC Adapter
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null) {
// init nfc
-
- // get public keyring as byte array
- long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
- mSharedKeyringBytes = ProviderHelper.getKeyRingsAsByteArray(this, dataUri,
- new long[]{masterKeyId});
-
// Register callback to set NDEF message
mNfcAdapter.setNdefPushMessageCallback(this, this);
// Register callback to listen for message-sent success
@@ -86,9 +81,19 @@ public class ViewKeyActivityJB extends ViewKeyActivity implements CreateNdefMess
* guarantee that this activity starts when receiving a beamed message. For now, this code
* uses the tag dispatch system.
*/
- NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
- mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
- return msg;
+ // get public keyring as byte array
+ long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri);
+ try {
+ mSharedKeyringBytes = ProviderHelper.getPGPPublicKeyRing(this, masterKeyId).getEncoded();
+
+ NdefMessage msg = new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
+ mSharedKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
+ return msg;
+ } catch(IOException e) {
+ // not much trouble, but leave a note
+ Log.e(Constants.TAG, "Error parsing keyring: ", e);
+ return null;
+ }
}
/**
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index c65e9e691..26e72f10b 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -53,20 +53,21 @@ public class ViewKeyCertsFragment extends Fragment
// These are the rows that we will retrieve.
static final String[] PROJECTION = new String[] {
KeychainContract.Certs._ID,
+ KeychainContract.Certs.MASTER_KEY_ID,
KeychainContract.Certs.VERIFIED,
KeychainContract.Certs.RANK,
KeychainContract.Certs.KEY_ID_CERTIFIER,
- KeychainContract.UserIds.USER_ID,
- "signer_uid"
+ KeychainContract.Certs.USER_ID,
+ KeychainContract.Certs.SIGNER_UID
};
// sort by our user id,
static final String SORT_ORDER =
KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " ASC, "
+ KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.VERIFIED + " DESC, "
- + "signer_uid ASC";
+ + KeychainContract.Certs.SIGNER_UID + " ASC";
- public static final String ARG_KEYRING_ROW_ID = "row_id";
+ public static final String ARG_DATA_URI = "data_uri";
private StickyListHeadersListView mStickyList;
private Spinner mSpinner;
@@ -125,14 +126,14 @@ public class ViewKeyCertsFragment extends Fragment
mStickyList = (StickyListHeadersListView) getActivity().findViewById(R.id.list);
- if (!getArguments().containsKey(ARG_KEYRING_ROW_ID)) {
+ if (!getArguments().containsKey(ARG_DATA_URI)) {
Log.e(Constants.TAG, "Data missing. Should be Uri of key!");
getActivity().finish();
return;
}
- long rowId = getArguments().getLong(ARG_KEYRING_ROW_ID);
- mBaseUri = KeychainContract.Certs.buildCertsByKeyRowIdUri(Long.toString(rowId));
+ Uri uri = getArguments().getParcelable(ARG_DATA_URI);
+ mBaseUri = KeychainContract.Certs.buildCertsUri(uri);
mStickyList.setAreHeadersSticky(true);
mStickyList.setDrawingListUnderStickyHeader(false);
@@ -229,12 +230,12 @@ public class ViewKeyCertsFragment extends Fragment
private void initIndex(Cursor cursor) {
if (cursor != null) {
- mIndexCertId = cursor.getColumnIndexOrThrow(KeychainContract.Certs._ID);
+ mIndexCertId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.MASTER_KEY_ID);
mIndexUserId = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.USER_ID);
mIndexRank = cursor.getColumnIndexOrThrow(KeychainContract.UserIds.RANK);
mIndexVerified = cursor.getColumnIndexOrThrow(KeychainContract.Certs.VERIFIED);
mIndexSignerKeyId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.KEY_ID_CERTIFIER);
- mIndexSignerUserId = cursor.getColumnIndexOrThrow("signer_uid");
+ mIndexSignerUserId = cursor.getColumnIndexOrThrow(KeychainContract.Certs.SIGNER_UID);
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index 8f8c13c29..b5a800712 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -30,15 +30,19 @@ import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
+import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter;
import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter;
@@ -52,6 +56,7 @@ public class ViewKeyMainFragment extends Fragment implements
public static final String ARG_DATA_URI = "uri";
+ private LinearLayout mContainer;
private TextView mName;
private TextView mEmail;
private TextView mComment;
@@ -68,7 +73,7 @@ public class ViewKeyMainFragment extends Fragment implements
private ListView mUserIds;
private ListView mKeys;
- private static final int LOADER_ID_KEYRING = 0;
+ private static final int LOADER_ID_UNIFIED = 0;
private static final int LOADER_ID_USER_IDS = 1;
private static final int LOADER_ID_KEYS = 2;
@@ -77,10 +82,14 @@ public class ViewKeyMainFragment extends Fragment implements
private Uri mDataUri;
+ // for activity
+ private boolean mSecretAvailable = false;
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.view_key_main_fragment, container, false);
+ mContainer = (LinearLayout) view.findViewById(R.id.container);
mName = (TextView) view.findViewById(R.id.name);
mEmail = (TextView) view.findViewById(R.id.email);
mComment = (TextView) view.findViewById(R.id.comment);
@@ -119,64 +128,24 @@ public class ViewKeyMainFragment extends Fragment implements
return;
}
+ getActivity().setProgressBarIndeterminateVisibility(Boolean.TRUE);
+ mContainer.setVisibility(View.GONE);
+
mDataUri = dataUri;
Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());
- { // label whether secret key is available, and edit button if it is
- final long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), mDataUri);
- if (ProviderHelper.hasSecretKeyByMasterKeyId(getActivity(), masterKeyId)) {
- // set this attribute. this is a LITTLE unclean, but we have the info available
- // right here, so why not.
- mSecretKey.setTextColor(getResources().getColor(R.color.emphasis));
- mSecretKey.setText(R.string.secret_key_yes);
-
- // certify button
- // TODO this button MIGHT be useful if the user wants to
- // certify a private key with another...
- // mActionCertify.setVisibility(View.GONE);
-
- // edit button
- mActionEdit.setVisibility(View.VISIBLE);
- mActionEdit.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
- editIntent.setData(
- KeychainContract
- .KeyRings.buildSecretKeyRingsByMasterKeyIdUri(
- Long.toString(masterKeyId)));
- editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
- startActivityForResult(editIntent, 0);
- }
- });
- } else {
- mSecretKey.setTextColor(Color.BLACK);
- mSecretKey.setText(getResources().getString(R.string.secret_key_no));
-
- // certify button
- mActionCertify.setVisibility(View.VISIBLE);
- // edit button
- mActionEdit.setVisibility(View.GONE);
- }
-
- // TODO see todo note above, doing this here for now
- mActionCertify.setOnClickListener(new View.OnClickListener() {
- public void onClick(View view) {
- certifyKey(KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
- Long.toString(masterKeyId)
- ));
- }
- });
-
- }
-
mActionEncrypt.setOnClickListener(new View.OnClickListener() {
-
@Override
public void onClick(View v) {
encryptToContact(mDataUri);
}
});
+ mActionCertify.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ certifyKey(mDataUri);
+ }
+ });
mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0);
mUserIds.setAdapter(mUserIdsAdapter);
@@ -186,74 +155,51 @@ public class ViewKeyMainFragment extends Fragment implements
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
- getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
+ getActivity().getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
getActivity().getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
}
- static final String[] KEYRING_PROJECTION =
- new String[]{KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID,
- KeychainContract.UserIds.USER_ID};
- static final int KEYRING_INDEX_ID = 0;
- static final int KEYRING_INDEX_MASTER_KEY_ID = 1;
- static final int KEYRING_INDEX_USER_ID = 2;
-
- static final String[] USER_IDS_PROJECTION = new String[]{
- KeychainContract.UserIds._ID,
- KeychainContract.UserIds.USER_ID,
- KeychainContract.UserIds.RANK,
- "verified",
+ static final String[] UNIFIED_PROJECTION = new String[] {
+ KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_SECRET,
+ KeyRings.USER_ID, KeyRings.FINGERPRINT,
+ KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.EXPIRY,
+
+ };
+ static final int INDEX_UNIFIED_MKI = 1;
+ static final int INDEX_UNIFIED_HAS_SECRET = 2;
+ static final int INDEX_UNIFIED_UID = 3;
+ static final int INDEX_UNIFIED_FINGERPRINT = 4;
+ static final int INDEX_UNIFIED_ALGORITHM = 5;
+ static final int INDEX_UNIFIED_KEY_SIZE = 6;
+ static final int INDEX_UNIFIED_CREATION = 7;
+ static final int INDEX_UNIFIED_EXPIRY = 8;
+
+ static final String[] USER_IDS_PROJECTION = new String[] {
+ UserIds._ID, UserIds.USER_ID, UserIds.RANK,
};
- // not the main user id
- static final String USER_IDS_SELECTION =
- KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " > 0 ";
- static final String USER_IDS_SORT_ORDER =
- KeychainDatabase.Tables.USER_IDS + "." + KeychainContract.UserIds.RANK + " COLLATE LOCALIZED ASC";
-
- static final String[] KEYS_PROJECTION = new String[]{
- KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID,
- KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM,
- KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY,
- KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT,
- KeychainContract.Keys.CREATION, KeychainContract.Keys.EXPIRY,
- KeychainContract.Keys.FINGERPRINT
- };
- static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC";
- static final int KEYS_INDEX_ID = 0;
- static final int KEYS_INDEX_KEY_ID = 1;
- static final int KEYS_INDEX_IS_MASTER_KEY = 2;
- static final int KEYS_INDEX_ALGORITHM = 3;
- static final int KEYS_INDEX_KEY_SIZE = 4;
- static final int KEYS_INDEX_CAN_CERTIFY = 5;
- static final int KEYS_INDEX_CAN_SIGN = 6;
- static final int KEYS_INDEX_CAN_ENCRYPT = 7;
- static final int KEYS_INDEX_CREATION = 8;
- static final int KEYS_INDEX_EXPIRY = 9;
- static final int KEYS_INDEX_FINGERPRINT = 10;
+
+ static final String[] KEYS_PROJECTION = new String[] {
+ Keys._ID,
+ Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE,
+ Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED,
+ Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT
+ };
+ static final int KEYS_INDEX_CAN_ENCRYPT = 6;
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
switch (id) {
- case LOADER_ID_KEYRING: {
- Uri baseUri = mDataUri;
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, KEYRING_PROJECTION, null, null, null);
+ case LOADER_ID_UNIFIED: {
+ Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
}
case LOADER_ID_USER_IDS: {
- Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri);
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, null, null,
- USER_IDS_SORT_ORDER);
+ Uri baseUri = UserIds.buildUserIdsUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, USER_IDS_PROJECTION, null, null, null);
}
case LOADER_ID_KEYS: {
- Uri baseUri = KeychainContract.Keys.buildKeysUri(mDataUri);
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, KEYS_SORT_ORDER);
+ Uri baseUri = Keys.buildKeysUri(mDataUri);
+ return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, null);
}
default:
@@ -262,14 +208,19 @@ public class ViewKeyMainFragment extends Fragment implements
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ /* TODO better error handling? May cause problems when a key is deleted,
+ * because the notification triggers faster than the activity closes.
+ */
+ // Avoid NullPointerExceptions...
+ if(data.getCount() == 0)
+ return;
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
switch (loader.getId()) {
- case LOADER_ID_KEYRING:
+ case LOADER_ID_UNIFIED: {
if (data.moveToFirst()) {
// get name, email, and comment from USER_ID
- String[] mainUserId = PgpKeyHelper.splitUserId(data
- .getString(KEYRING_INDEX_USER_ID));
+ String[] mainUserId = PgpKeyHelper.splitUserId(data.getString(INDEX_UNIFIED_UID));
if (mainUserId[0] != null) {
getActivity().setTitle(mainUserId[0]);
mName.setText(mainUserId[0]);
@@ -279,63 +230,97 @@ public class ViewKeyMainFragment extends Fragment implements
}
mEmail.setText(mainUserId[1]);
mComment.setText(mainUserId[2]);
- }
- break;
- case LOADER_ID_USER_IDS:
- mUserIdsAdapter.swapCursor(data);
- break;
- case LOADER_ID_KEYS:
- // the first key here is our master key
- if (data.moveToFirst()) {
- // get key id from MASTER_KEY_ID
- long keyId = data.getLong(KEYS_INDEX_KEY_ID);
+ if (data.getInt(INDEX_UNIFIED_HAS_SECRET) != 0) {
+ mSecretAvailable = true;
+
+ mSecretKey.setTextColor(getResources().getColor(R.color.emphasis));
+ mSecretKey.setText(R.string.secret_key_yes);
+
+ // edit button
+ mActionEdit.setVisibility(View.VISIBLE);
+ mActionEdit.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View view) {
+ Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
+ editIntent.setData(mDataUri);
+ editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
+ startActivityForResult(editIntent, 0);
+ }
+ });
+ } else {
+ mSecretAvailable = false;
+
+ mSecretKey.setTextColor(Color.BLACK);
+ mSecretKey.setText(getResources().getString(R.string.secret_key_no));
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
+ // certify button
+ mActionCertify.setVisibility(View.VISIBLE);
+ // edit button
+ mActionEdit.setVisibility(View.GONE);
+ }
+
+ // get key id from MASTER_KEY_ID
+ long masterKeyId = data.getLong(INDEX_UNIFIED_MKI);
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHex(masterKeyId);
mKeyId.setText(keyIdStr);
// get creation date from CREATION
- if (data.isNull(KEYS_INDEX_CREATION)) {
+ if (data.isNull(INDEX_UNIFIED_CREATION)) {
mCreation.setText(R.string.none);
} else {
- Date creationDate = new Date(data.getLong(KEYS_INDEX_CREATION) * 1000);
+ Date creationDate = new Date(data.getLong(INDEX_UNIFIED_CREATION) * 1000);
mCreation.setText(
DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- creationDate));
+ creationDate));
}
// get expiry date from EXPIRY
- if (data.isNull(KEYS_INDEX_EXPIRY)) {
+ if (data.isNull(INDEX_UNIFIED_EXPIRY)) {
mExpiry.setText(R.string.none);
} else {
- Date expiryDate = new Date(data.getLong(KEYS_INDEX_EXPIRY) * 1000);
+ Date expiryDate = new Date(data.getLong(INDEX_UNIFIED_EXPIRY) * 1000);
mExpiry.setText(
DateFormat.getDateFormat(getActivity().getApplicationContext()).format(
- expiryDate));
+ expiryDate));
}
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
- data.getInt(KEYS_INDEX_ALGORITHM), data.getInt(KEYS_INDEX_KEY_SIZE));
+ data.getInt(INDEX_UNIFIED_ALGORITHM), data.getInt(INDEX_UNIFIED_KEY_SIZE));
mAlgorithm.setText(algorithmStr);
- byte[] fingerprintBlob = data.getBlob(KEYS_INDEX_FINGERPRINT);
- if (fingerprintBlob == null) {
- // FALLBACK for old database entries
- fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);
- }
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);
+ byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT);
+ String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob);
+ mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint));
- mFingerprint.setText(OtherHelper.colorizeFingerprint(fingerprint));
+ break;
}
+ }
- mKeysAdapter.swapCursor(data);
+ case LOADER_ID_USER_IDS:
+ mUserIdsAdapter.swapCursor(data);
break;
- default:
+ case LOADER_ID_KEYS:
+ // hide encrypt button if no encryption key is available
+ boolean canEncrypt = false;
+ data.moveToFirst();
+ do {
+ if (data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1) {
+ canEncrypt = true;
+ break;
+ }
+ } while (data.moveToNext());
+ if (!canEncrypt) {
+ mActionEncrypt.setVisibility(View.GONE);
+ }
+
+ mKeysAdapter.swapCursor(data);
break;
}
+ getActivity().setProgressBarIndeterminateVisibility(Boolean.FALSE);
+ mContainer.setVisibility(View.VISIBLE);
}
/**
@@ -344,24 +329,25 @@ public class ViewKeyMainFragment extends Fragment implements
*/
public void onLoaderReset(Loader<Cursor> loader) {
switch (loader.getId()) {
- case LOADER_ID_KEYRING:
- // No resources need to be freed for this ID
- break;
case LOADER_ID_USER_IDS:
mUserIdsAdapter.swapCursor(null);
break;
case LOADER_ID_KEYS:
mKeysAdapter.swapCursor(null);
break;
- default:
- break;
}
}
+ /** Returns true if the key current displayed is known to have a secret key. */
+ public boolean isSecretAvailable() {
+ return mSecretAvailable;
+ }
+
private void encryptToContact(Uri dataUri) {
+ // TODO preselect from uri? should be feasible without trivial query
long keyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
- long[] encryptionKeyIds = new long[]{keyId};
+ long[] encryptionKeyIds = new long[]{ keyId };
Intent intent = new Intent(getActivity(), EncryptActivity.class);
intent.setAction(EncryptActivity.ACTION_ENCRYPT);
intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 0f05af447..f322ea980 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -30,6 +30,7 @@ import android.widget.CheckBox;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@@ -43,13 +44,12 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
protected List<ImportKeysListEntry> mData;
static class ViewHolder {
- private TextView mMainUserId;
- private TextView mMainUserIdRest;
- private TextView mKeyId;
- private TextView mFingerprint;
- private TextView mAlgorithm;
- private TextView mStatus;
-
+ public TextView mainUserId;
+ public TextView mainUserIdRest;
+ public TextView keyId;
+ public TextView fingerprint;
+ public TextView algorithm;
+ public TextView status;
}
public ImportKeysAdapter(Activity activity) {
@@ -100,12 +100,12 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
if (convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.import_keys_list_entry, null);
- holder.mMainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
- holder.mMainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
- holder.mKeyId = (TextView) convertView.findViewById(R.id.keyId);
- holder.mFingerprint = (TextView) convertView.findViewById(R.id.fingerprint);
- holder.mAlgorithm = (TextView) convertView.findViewById(R.id.algorithm);
- holder.mStatus = (TextView) convertView.findViewById(R.id.status);
+ holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId);
+ holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest);
+ holder.keyId = (TextView) convertView.findViewById(R.id.keyId);
+ holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint);
+ holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm);
+ holder.status = (TextView) convertView.findViewById(R.id.status);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
@@ -119,36 +119,36 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// show red user id if it is a secret key
if (entry.secretKey) {
userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0];
- holder.mMainUserId.setTextColor(Color.RED);
+ holder.mainUserId.setTextColor(Color.RED);
}
- holder.mMainUserId.setText(userIdSplit[0]);
+ holder.mainUserId.setText(userIdSplit[0]);
} else {
- holder.mMainUserId.setText(R.string.user_id_no_name);
+ holder.mainUserId.setText(R.string.user_id_no_name);
}
// email
if (userIdSplit[1] != null) {
- holder.mMainUserIdRest.setText(userIdSplit[1]);
- holder.mMainUserIdRest.setVisibility(View.VISIBLE);
+ holder.mainUserIdRest.setText(userIdSplit[1]);
+ holder.mainUserIdRest.setVisibility(View.VISIBLE);
} else {
- holder.mMainUserIdRest.setVisibility(View.GONE);
+ holder.mainUserIdRest.setVisibility(View.GONE);
}
- holder.mKeyId.setText(entry.hexKeyId);
+ holder.keyId.setText(entry.keyIdHex);
- if (entry.fingerPrint != null) {
- holder.mFingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint);
- holder.mFingerprint.setVisibility(View.VISIBLE);
+ if (entry.fingerPrintHex != null) {
+ holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex));
+ holder.fingerprint.setVisibility(View.VISIBLE);
} else {
- holder.mFingerprint.setVisibility(View.GONE);
+ holder.fingerprint.setVisibility(View.GONE);
}
- holder.mAlgorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
+ holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm);
if (entry.revoked) {
- holder.mStatus.setText(R.string.revoked);
+ holder.status.setText(R.string.revoked);
} else {
- holder.mStatus.setVisibility(View.GONE);
+ holder.status.setVisibility(View.GONE);
}
LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.list);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
index 19f0d1eaf..5631d40ea 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java
@@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.SparseArray;
+
import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
@@ -34,13 +36,13 @@ import java.util.Date;
public class ImportKeysListEntry implements Serializable, Parcelable {
private static final long serialVersionUID = -7797972103284992662L;
- public ArrayList<String> userIds;
+ public ArrayList<String> userIds;
public long keyId;
+ public String keyIdHex;
public boolean revoked;
public Date date; // TODO: not displayed
- public String fingerPrint;
- public String hexKeyId;
+ public String fingerPrintHex;
public int bitStrength;
public String algorithm;
public boolean secretKey;
@@ -54,8 +56,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.keyId = b.keyId;
this.revoked = b.revoked;
this.date = b.date;
- this.fingerPrint = b.fingerPrint;
- this.hexKeyId = b.hexKeyId;
+ this.fingerPrintHex = b.fingerPrintHex;
+ this.keyIdHex = b.keyIdHex;
this.bitStrength = b.bitStrength;
this.algorithm = b.algorithm;
this.secretKey = b.secretKey;
@@ -73,8 +75,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
dest.writeLong(keyId);
dest.writeByte((byte) (revoked ? 1 : 0));
dest.writeSerializable(date);
- dest.writeString(fingerPrint);
- dest.writeString(hexKeyId);
+ dest.writeString(fingerPrintHex);
+ dest.writeString(keyIdHex);
dest.writeInt(bitStrength);
dest.writeString(algorithm);
dest.writeByte((byte) (secretKey ? 1 : 0));
@@ -91,8 +93,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
vr.keyId = source.readLong();
vr.revoked = source.readByte() == 1;
vr.date = (Date) source.readSerializable();
- vr.fingerPrint = source.readString();
- vr.hexKeyId = source.readString();
+ vr.fingerPrintHex = source.readString();
+ vr.keyIdHex = source.readString();
vr.bitStrength = source.readInt();
vr.algorithm = source.readString();
vr.secretKey = source.readByte() == 1;
@@ -108,8 +110,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
}
};
- public long getKeyId() {
- return keyId;
+ public String getKeyIdHex() {
+ return keyIdHex;
}
public byte[] getBytes() {
@@ -120,6 +122,82 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.mBytes = bytes;
}
+ public boolean isSelected() {
+ return mSelected;
+ }
+
+ public void setSelected(boolean selected) {
+ this.mSelected = selected;
+ }
+
+ public long getKeyId() {
+ return keyId;
+ }
+
+ public void setKeyId(long keyId) {
+ this.keyId = keyId;
+ }
+
+ public void setKeyIdHex(String keyIdHex) {
+ this.keyIdHex = keyIdHex;
+ }
+
+ public boolean isRevoked() {
+ return revoked;
+ }
+
+ public void setRevoked(boolean revoked) {
+ this.revoked = revoked;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getFingerPrintHex() {
+ return fingerPrintHex;
+ }
+
+ public void setFingerPrintHex(String fingerPrintHex) {
+ this.fingerPrintHex = fingerPrintHex;
+ }
+
+ public int getBitStrength() {
+ return bitStrength;
+ }
+
+ public void setBitStrength(int bitStrength) {
+ this.bitStrength = bitStrength;
+ }
+
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ public void setAlgorithm(String algorithm) {
+ this.algorithm = algorithm;
+ }
+
+ public boolean isSecretKey() {
+ return secretKey;
+ }
+
+ public void setSecretKey(boolean secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ public ArrayList<String> getUserIds() {
+ return userIds;
+ }
+
+ public void setUserIds(ArrayList<String> userIds) {
+ this.userIds = userIds;
+ }
+
/**
* Constructor for later querying from keyserver
*/
@@ -131,14 +209,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
userIds = new ArrayList<String>();
}
- public boolean isSelected() {
- return mSelected;
- }
-
- public void setSelected(boolean selected) {
- this.mSelected = selected;
- }
-
/**
* Constructor based on key object, used for import from NFC, QR Codes, files
*/
@@ -164,27 +234,41 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
for (String userId : new IterableIterator<String>(pgpKeyRing.getPublicKey().getUserIDs())) {
userIds.add(userId);
}
+
this.keyId = pgpKeyRing.getPublicKey().getKeyID();
+ this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId);
this.revoked = pgpKeyRing.getPublicKey().isRevoked();
- this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
- .getFingerprint(), true);
- this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId);
+ this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey()
+ .getFingerprint());
this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength();
- int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
- if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL
- || algorithm == PGPPublicKey.RSA_SIGN) {
- this.algorithm = "RSA";
- } else if (algorithm == PGPPublicKey.DSA) {
- this.algorithm = "DSA";
- } else if (algorithm == PGPPublicKey.ELGAMAL_ENCRYPT
- || algorithm == PGPPublicKey.ELGAMAL_GENERAL) {
- this.algorithm = "ElGamal";
- } else if (algorithm == PGPPublicKey.EC || algorithm == PGPPublicKey.ECDSA) {
- this.algorithm = "ECC";
- } else {
- // TODO: with resources
- this.algorithm = "unknown";
- }
+ final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm();
+ this.algorithm = getAlgorithmFromId(algorithm);
+ }
+
+ /**
+ * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
+ */
+ private static final SparseArray<String> ALGORITHM_IDS = new SparseArray<String>() {{
+ put(-1, "unknown"); // TODO: with resources
+ put(0, "unencrypted");
+ put(PGPPublicKey.RSA_GENERAL, "RSA");
+ put(PGPPublicKey.RSA_ENCRYPT, "RSA");
+ put(PGPPublicKey.RSA_SIGN, "RSA");
+ put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal");
+ put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal");
+ put(PGPPublicKey.DSA, "DSA");
+ put(PGPPublicKey.EC, "ECC");
+ put(PGPPublicKey.ECDSA, "ECC");
+ put(PGPPublicKey.ECDH, "ECC");
+ }};
+
+ /**
+ * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
+ */
+ public static String getAlgorithmFromId(int algorithmId) {
+ return (ALGORITHM_IDS.get(algorithmId) != null ?
+ ALGORITHM_IDS.get(algorithmId) :
+ ALGORITHM_IDS.get(-1));
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
index a4dd06e40..259e14319 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
+
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.HkpKeyServer;
import org.sufficientlysecure.keychain.util.KeyServer;
@@ -53,7 +54,12 @@ public class ImportKeysListServerLoader
return mEntryListWrapper;
}
- queryServer(mServerQuery, mKeyServer);
+ if (mServerQuery.startsWith("0x") && mServerQuery.length() == 42) {
+ Log.d(Constants.TAG, "This search is based on a unique fingerprint. Enforce a fingerprint check!");
+ queryServer(mServerQuery, mKeyServer, true);
+ } else {
+ queryServer(mServerQuery, mKeyServer, false);
+ }
return mEntryListWrapper;
}
@@ -84,14 +90,30 @@ public class ImportKeysListServerLoader
/**
* Query keyserver
*/
- private void queryServer(String query, String keyServer) {
+ private void queryServer(String query, String keyServer, boolean enforceFingerprint) {
HkpKeyServer server = new HkpKeyServer(keyServer);
try {
ArrayList<ImportKeysListEntry> searchResult = server.search(query);
mEntryList.clear();
// add result to data
- mEntryList.addAll(searchResult);
+ if (enforceFingerprint) {
+ String fingerprint = query.substring(2);
+ Log.d(Constants.TAG, "fingerprint: " + fingerprint);
+ // query must return only one result!
+ if (searchResult.size() > 0) {
+ ImportKeysListEntry uniqueEntry = searchResult.get(0);
+ /*
+ * set fingerprint explicitly after query
+ * to enforce a check when the key is imported by KeychainIntentService
+ */
+ uniqueEntry.setFingerPrintHex(fingerprint);
+ uniqueEntry.setSelected(true);
+ mEntryList.add(uniqueEntry);
+ }
+ } else {
+ mEntryList.addAll(searchResult);
+ }
mEntryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(mEntryList, null);
} catch (KeyServer.InsufficientQuery e) {
Log.e(Constants.TAG, "InsufficientQuery", e);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
index c997599bd..5b5d316b6 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyValueSpinnerAdapter.java
@@ -20,7 +20,11 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
import android.widget.ArrayAdapter;
-import java.util.*;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
public class KeyValueSpinnerAdapter extends ArrayAdapter<String> {
private final HashMap<Integer, String> mData;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
new file mode 100644
index 000000000..fd864eb09
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/PagerTabStripAdapter.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.ui.adapter;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v7.app.ActionBarActivity;
+
+import java.util.ArrayList;
+
+public class PagerTabStripAdapter extends FragmentPagerAdapter {
+ private final Context mContext;
+ private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
+
+ static final class TabInfo {
+ public final Class<?> clss;
+ public final Bundle args;
+ public final String title;
+
+ TabInfo(Class<?> clss, Bundle args, String title) {
+ this.clss = clss;
+ this.args = args;
+ this.title = title;
+ }
+ }
+
+ public PagerTabStripAdapter(ActionBarActivity activity) {
+ super(activity.getSupportFragmentManager());
+ mContext = activity;
+ }
+
+ public void addTab(Class<?> clss, Bundle args, String title) {
+ TabInfo info = new TabInfo(clss, args, title);
+ mTabs.add(info);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mTabs.size();
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ TabInfo info = mTabs.get(position);
+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabs.get(position).title;
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
index beb76fc10..fbbb9caa4 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java
@@ -115,7 +115,7 @@ public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter {
// TODO: needed to key id to no?
keyId.setText(R.string.no_key);
long masterKeyId = cursor.getLong(mIndexMasterKeyId);
- keyId.setText(PgpKeyHelper.convertKeyIdToHex(masterKeyId));
+ keyId.setText(PgpKeyHelper.convertKeyIdToHexShort(masterKeyId));
// TODO: needed to set unknown_status?
status.setText(R.string.unknown_status);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
index f435d46ef..9ddfa90be 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/TabsAdapter.java
@@ -36,12 +36,12 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo {
- private final Class<?> mClss;
- private final Bundle mArgs;
+ public final Class<?> clss;
+ public final Bundle args;
- TabInfo(Class<?> mClss, Bundle mArgs) {
- this.mClss = mClss;
- this.mArgs = mArgs;
+ TabInfo(Class<?> clss, Bundle args) {
+ this.clss = clss;
+ this.args = args;
}
}
@@ -71,7 +71,7 @@ public class TabsAdapter extends FragmentStatePagerAdapter implements ActionBar.
@Override
public Fragment getItem(int position) {
TabInfo info = mTabs.get(position);
- return Fragment.instantiate(mContext, info.mClss.getName(), info.mArgs);
+ return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
index 153a3f266..64b735bfa 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java
@@ -18,27 +18,36 @@
package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.database.Cursor;
import android.support.v4.widget.CursorAdapter;
+import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
+import java.util.Date;
+
public class ViewKeyKeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private int mIndexKeyId;
private int mIndexAlgorithm;
private int mIndexKeySize;
- private int mIndexIsMasterKey;
+ private int mIndexRank;
private int mIndexCanCertify;
private int mIndexCanEncrypt;
private int mIndexCanSign;
+ private int mIndexRevokedKey;
+ private int mIndexExpiry;
+
+ private ColorStateList mDefaultTextColor;
public ViewKeyKeysAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
@@ -66,10 +75,12 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
mIndexKeyId = cursor.getColumnIndexOrThrow(Keys.KEY_ID);
mIndexAlgorithm = cursor.getColumnIndexOrThrow(Keys.ALGORITHM);
mIndexKeySize = cursor.getColumnIndexOrThrow(Keys.KEY_SIZE);
- mIndexIsMasterKey = cursor.getColumnIndexOrThrow(Keys.IS_MASTER_KEY);
+ mIndexRank = cursor.getColumnIndexOrThrow(Keys.RANK);
mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY);
mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT);
mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN);
+ mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED);
+ mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY);
}
}
@@ -77,20 +88,21 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
public void bindView(View view, Context context, Cursor cursor) {
TextView keyId = (TextView) view.findViewById(R.id.keyId);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
+ TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry);
ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey);
ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey);
ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey);
ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey);
+ ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey);
- String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId));
+ String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(mIndexKeyId));
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm),
cursor.getInt(mIndexKeySize));
keyId.setText(keyIdStr);
-
keyDetails.setText("(" + algorithmStr + ")");
- if (cursor.getInt(mIndexIsMasterKey) != 1) {
+ if (cursor.getInt(mIndexRank) == 0) {
masterKeyIcon.setVisibility(View.INVISIBLE);
} else {
masterKeyIcon.setVisibility(View.VISIBLE);
@@ -113,11 +125,51 @@ public class ViewKeyKeysAdapter extends CursorAdapter {
} else {
signIcon.setVisibility(View.VISIBLE);
}
+
+ boolean valid = true;
+ if (cursor.getInt(mIndexRevokedKey) > 0) {
+ revokedKeyIcon.setVisibility(View.VISIBLE);
+
+ valid = false;
+ } else {
+ keyId.setTextColor(mDefaultTextColor);
+ keyDetails.setTextColor(mDefaultTextColor);
+ keyExpiry.setTextColor(mDefaultTextColor);
+
+ revokedKeyIcon.setVisibility(View.GONE);
+ }
+
+ if (!cursor.isNull(mIndexExpiry)) {
+ Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000);
+
+ valid = valid && expiryDate.after(new Date());
+ keyExpiry.setText("(" +
+ context.getString(R.string.label_expiry) + ": " +
+ DateFormat.getDateFormat(context).format(expiryDate) + ")");
+
+ keyExpiry.setVisibility(View.VISIBLE);
+ } else {
+ keyExpiry.setVisibility(View.GONE);
+ }
+ // if key is expired or revoked, strike through text
+ if (!valid) {
+ keyId.setText(OtherHelper.strikeOutText(keyId.getText()));
+ keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText()));
+ keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText()));
+ }
+ keyId.setEnabled(valid);
+ keyDetails.setEnabled(valid);
+ keyExpiry.setEnabled(valid);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return mInflater.inflate(R.layout.view_key_keys_item, null);
+ View view = mInflater.inflate(R.layout.view_key_keys_item, null);
+ if (mDefaultTextColor == null) {
+ TextView keyId = (TextView) view.findViewById(R.id.keyId);
+ mDefaultTextColor = keyId.getTextColors();
+ }
+ return view;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
index 2778ed08c..2677a1a1a 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyUserIdsAdapter.java
@@ -39,7 +39,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
private int mIndexUserId, mIndexRank;
private int mVerifiedId;
- final private ArrayList<Boolean> mCheckStates;
+ private final ArrayList<Boolean> mCheckStates;
public ViewKeyUserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) {
super(context, c, flags);
@@ -57,9 +57,9 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
@Override
public Cursor swapCursor(Cursor newCursor) {
initIndex(newCursor);
- if(mCheckStates != null) {
+ if (mCheckStates != null) {
mCheckStates.clear();
- if(newCursor != null) {
+ if (newCursor != null) {
int count = newCursor.getCount();
mCheckStates.ensureCapacity(count);
// initialize to true (use case knowledge: we usually want to sign all uids)
@@ -84,7 +84,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
if (cursor != null) {
mIndexUserId = cursor.getColumnIndexOrThrow(UserIds.USER_ID);
mIndexRank = cursor.getColumnIndexOrThrow(UserIds.RANK);
- mVerifiedId = cursor.getColumnIndexOrThrow("verified");
+ // mVerifiedId = cursor.getColumnIndexOrThrow(UserIds.VERIFIED);
}
}
@@ -106,7 +106,7 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
}
vAddress.setText(userId[1]);
- int verified = cursor.getInt(mVerifiedId);
+ int verified = 1; // cursor.getInt(mVerifiedId);
// TODO introduce own resource for this :)
if(verified > 0)
vVerified.setImageResource(android.R.drawable.presence_online);
@@ -114,8 +114,9 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
vVerified.setImageResource(android.R.drawable.presence_invisible);
// don't care further if checkboxes aren't shown
- if(mCheckStates == null)
+ if (mCheckStates == null) {
return;
+ }
final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.checkBox);
final int position = cursor.getPosition();
@@ -138,8 +139,8 @@ public class ViewKeyUserIdsAdapter extends CursorAdapter {
public ArrayList<String> getSelectedUserIds() {
ArrayList<String> result = new ArrayList<String>();
- for(int i = 0; i < mCheckStates.size(); i++) {
- if(mCheckStates.get(i)) {
+ for (int i = 0; i < mCheckStates.size(); i++) {
+ if (mCheckStates.get(i)) {
mCursor.moveToPosition(i);
result.add(mCursor.getString(mIndexUserId));
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
index a41bc2bee..ad558a81e 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java
@@ -25,6 +25,7 @@ import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import org.sufficientlysecure.keychain.Id;
@@ -113,21 +114,8 @@ public class CreateKeyDialogFragment extends DialogFragment {
public void onClick(DialogInterface di, int id) {
di.dismiss();
try {
- int nKeyIndex = keySize.getSelectedItemPosition();
- switch (nKeyIndex) {
- case 0:
- mNewKeySize = 512;
- break;
- case 1:
- mNewKeySize = 1024;
- break;
- case 2:
- mNewKeySize = 2048;
- break;
- case 3:
- mNewKeySize = 4096;
- break;
- }
+ final String selectedItem = (String) keySize.getSelectedItem();
+ mNewKeySize = Integer.parseInt(selectedItem);
} catch (NumberFormatException e) {
mNewKeySize = 0;
}
@@ -145,7 +133,27 @@ public class CreateKeyDialogFragment extends DialogFragment {
}
});
- return dialog.create();
+ final AlertDialog alertDialog = dialog.create();
+
+ final AdapterView.OnItemSelectedListener weakRsaListener = new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ final Choice selectedAlgorithm = (Choice) algorithm.getSelectedItem();
+ final int selectedKeySize = Integer.parseInt((String) keySize.getSelectedItem());
+ final boolean isWeakRsa = (selectedAlgorithm.getId() == Id.choice.algorithm.rsa &&
+ selectedKeySize <= 1024);
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(!isWeakRsa);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ }
+ };
+
+ keySize.setOnItemSelectedListener(weakRsaListener);
+ algorithm.setOnItemSelectedListener(weakRsaListener);
+
+ return alertDialog;
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
index b067010df..b4c38184c 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java
@@ -87,11 +87,11 @@ public class DeleteFileDialogFragment extends DialogFragment {
false,
null);
- // Message is received after deleting is done in ApgService
+ // Message is received after deleting is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler =
new KeychainIntentServiceHandler(activity, deletingDialog) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 1bcf5b33c..72ea4c013 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2013-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
@@ -20,161 +20,125 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
-import android.database.Cursor;
-import android.net.Uri;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
-import java.util.ArrayList;
+import java.util.HashMap;
public class DeleteKeyDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file";
- private static final String ARG_KEY_TYPE = "key_type";
+ private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
public static final int MESSAGE_OKAY = 1;
+ public static final int MESSAGE_ERROR = 0;
+
+ private boolean mIsSingleSelection = false;
- public static final String MESSAGE_NOT_DELETED = "not_deleted";
+ private TextView mMainMessage;
+ private CheckBox mCheckDeleteSecret;
+ private LinearLayout mDeleteSecretKeyView;
+ private View mInflateView;
private Messenger mMessenger;
/**
* Creates new instance of this delete file dialog fragment
*/
- public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds,
- int keyType) {
+ public static DeleteKeyDialogFragment newInstance(Messenger messenger,
+ long[] masterKeyIds) {
DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
- args.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds);
- args.putInt(ARG_KEY_TYPE, keyType);
+ args.putLongArray(ARG_DELETE_MASTER_KEY_IDS, masterKeyIds);
+ //We don't need the key type
frag.setArguments(args);
return frag;
}
- /**
- * Creates dialog
- */
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
+
final FragmentActivity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS);
- final int keyType = getArguments().getInt(ARG_KEY_TYPE);
+ final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setTitle(R.string.warning);
- if (keyRingRowIds.length == 1) {
- Uri dataUri;
- if (keyType == Id.type.public_key) {
- dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0]));
- } else {
- dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0]));
- }
- String userId = ProviderHelper.getUserId(activity, dataUri);
+ //Setup custom View to display in AlertDialog
+ LayoutInflater inflater = activity.getLayoutInflater();
+ mInflateView = inflater.inflate(R.layout.view_key_delete_fragment, null);
+ builder.setView(mInflateView);
+
+ mDeleteSecretKeyView = (LinearLayout) mInflateView.findViewById(R.id.deleteSecretKeyView);
+ mMainMessage = (TextView) mInflateView.findViewById(R.id.mainMessage);
+ mCheckDeleteSecret = (CheckBox) mInflateView.findViewById(R.id.checkDeleteSecret);
- builder.setMessage(getString(
- keyType == Id.type.public_key ? R.string.key_deletion_confirmation
- : R.string.secret_key_deletion_confirmation, userId));
+ builder.setTitle(R.string.warning);
+
+ // If only a single key has been selected
+ if (masterKeyIds.length == 1) {
+ mIsSingleSelection = true;
+
+ long masterKeyId = masterKeyIds[0];
+
+ HashMap<String, Object> data = ProviderHelper.getUnifiedData(activity, masterKeyId, new String[]{
+ KeyRings.USER_ID,
+ KeyRings.HAS_SECRET
+ }, new int[] { ProviderHelper.FIELD_TYPE_STRING, ProviderHelper.FIELD_TYPE_INTEGER });
+ String userId = (String) data.get(KeyRings.USER_ID);
+ boolean hasSecret = ((Long) data.get(KeyRings.HAS_SECRET)) == 1;
+
+ // Hide the Checkbox and TextView since this is a single selection,user will be notified through message
+ mDeleteSecretKeyView.setVisibility(View.GONE);
+ // Set message depending on which key it is.
+ mMainMessage.setText(getString(
+ hasSecret ? R.string.secret_key_deletion_confirmation
+ : R.string.public_key_deletetion_confirmation,
+ userId));
} else {
- builder.setMessage(R.string.key_deletion_confirmation_multi);
+ mDeleteSecretKeyView.setVisibility(View.VISIBLE);
+ mMainMessage.setText(R.string.key_deletion_confirmation_multi);
}
builder.setIcon(R.drawable.ic_dialog_alert_holo_light);
builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
-
@Override
- public void onClick(DialogInterface dialog, int id) {
- ArrayList<String> notDeleted = new ArrayList<String>();
-
- if (keyType == Id.type.public_key) {
- Uri queryUri = KeychainContract.KeyRings.buildPublicKeyRingsUri();
- String[] projection = new String[]{
- KeychainContract.KeyRings._ID, // 0
- KeychainContract.KeyRings.MASTER_KEY_ID, // 1
- KeychainContract.UserIds.USER_ID // 2
- };
-
- // make selection with all entries where _ID is one of the given row ids
- String selection = KeychainDatabase.Tables.KEY_RINGS + "." +
- KeychainContract.KeyRings._ID + " IN(";
- String selectionIDs = "";
- for (int i = 0; i < keyRingRowIds.length; i++) {
- selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'";
- if (i + 1 < keyRingRowIds.length) {
- selectionIDs += ",";
- }
- }
- selection += selectionIDs + ")";
-
- Cursor cursor = activity.getContentResolver().query(queryUri, projection,
- selection, null, null);
-
- long rowId;
- long masterKeyId;
- String userId;
- try {
- while (cursor != null && cursor.moveToNext()) {
- rowId = cursor.getLong(0);
- masterKeyId = cursor.getLong(1);
- userId = cursor.getString(2);
-
- Log.d(Constants.TAG, "rowId: " + rowId + ", masterKeyId: " + masterKeyId
- + ", userId: " + userId);
-
- // check if a corresponding secret key exists...
- Cursor secretCursor = activity.getContentResolver().query(
- KeychainContract.KeyRings
- .buildSecretKeyRingsByMasterKeyIdUri(
- String.valueOf(masterKeyId)),
- null, null, null, null
- );
- if (secretCursor != null && secretCursor.getCount() > 0) {
- notDeleted.add(userId);
- } else {
- // it is okay to delete this key, no secret key found!
- ProviderHelper.deletePublicKeyRing(activity, rowId);
- }
- if (secretCursor != null) {
- secretCursor.close();
- }
- }
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- } else {
- for (long keyRowId : keyRingRowIds) {
- ProviderHelper.deleteSecretKeyRing(activity, keyRowId);
- }
+ public void onClick(DialogInterface dialog, int which) {
+
+ boolean success = false;
+ for(long masterKeyId : masterKeyIds) {
+ int count = activity.getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId)), null, null
+ );
+ success = count > 0;
}
-
- dismiss();
-
- if (notDeleted.size() > 0) {
- Bundle data = new Bundle();
- data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted);
- sendMessageToHandler(MESSAGE_OKAY, data);
- } else {
+ if (success) {
sendMessageToHandler(MESSAGE_OKAY, null);
+ } else {
+ sendMessageToHandler(MESSAGE_ERROR, null);
}
+ dismiss();
}
});
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@@ -184,6 +148,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
dismiss();
}
});
+
return builder.create();
}
@@ -198,7 +163,6 @@ public class DeleteKeyDialogFragment extends DialogFragment {
if (data != null) {
msg.setData(data);
}
-
try {
mMessenger.send(msg);
} catch (RemoteException e) {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index 271219fa3..a3feab959 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -24,10 +24,12 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -59,11 +61,34 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
public static final int MESSAGE_OKAY = 1;
public static final int MESSAGE_CANCEL = 2;
+ public static final String MESSAGE_DATA_PASSPHRASE = "passphrase";
+
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
+ * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
+ * for a symmetric passphrase
+ */
+ public static void show(FragmentActivity context, long keyId, Handler returnHandler) {
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+
+ try {
+ PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(context,
+ messenger, keyId);
+
+ passphraseDialog.show(context.getSupportFragmentManager(), "passphraseDialog");
+ } catch (PgpGeneralException e) {
+ Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
+ // send message to handler to start encryption directly
+ returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
+ }
+ }
+
+ /**
* Creates new instance of this dialog fragment
*
* @param secretKeyId secret key id you want to use
@@ -114,10 +139,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
secretKey = null;
alert.setMessage(R.string.passphrase_for_symmetric_encryption);
} else {
- // TODO: by master key id???
- secretKey = PgpKeyHelper.getMasterKey(ProviderHelper.getPGPSecretKeyRingByKeyId(activity,
- secretKeyId));
- // secretKey = PGPHelper.getMasterKey(PGPMain.getSecretKeyRing(secretKeyId));
+ secretKey = ProviderHelper.getPGPSecretKeyRing(activity, secretKeyId).getSecretKey();
if (secretKey == null) {
alert.setTitle(R.string.title_key_not_found);
@@ -173,7 +195,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
return;
} else {
clickSecretKey = PgpKeyHelper.getKeyNum(ProviderHelper
- .getPGPSecretKeyRingByKeyId(activity, secretKeyId),
+ .getPGPSecretKeyRingWithKeyId(activity, secretKeyId),
curKeyIndex);
curKeyIndex++; // does post-increment work like C?
continue;
@@ -209,7 +231,11 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
passphrase);
}
- sendMessageToHandler(MESSAGE_OKAY);
+ // also return passphrase back to activity
+ Bundle data = new Bundle();
+ data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
+
+ sendMessageToHandler(MESSAGE_OKAY, data);
}
});
@@ -278,4 +304,25 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
}
}
+ /**
+ * Send message back to handler which is initialized in a activity
+ *
+ * @param what Message integer you want to send
+ */
+ private void sendMessageToHandler(Integer what, Bundle data) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ try {
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
+ } catch (NullPointerException e) {
+ Log.w(Constants.TAG, "Messenger is null!", e);
+ }
+ }
+
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
index b501ba230..b6ff139df 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java
@@ -31,6 +31,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.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.QrCodeUtils;
@@ -89,29 +90,33 @@ public class ShareQrCodeDialogFragment extends DialogFragment {
if (mFingerprintOnly) {
alert.setPositiveButton(R.string.btn_okay, null);
- byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri);
- String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);
+ byte[] blob = (byte[]) ProviderHelper.getGenericData(
+ getActivity(), KeyRings.buildUnifiedKeyRingUri(dataUri),
+ KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
+ if(blob == null) {
+ // TODO error handling?!
+ 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);
} else {
mText.setText(R.string.share_qr_code_dialog_start);
- // TODO
+ // TODO works, but
long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);
-
// get public keyring as ascii armored string
ArrayList<String> keyringArmored = ProviderHelper.getKeyRingsAsArmoredString(
- getActivity(), dataUri, new long[]{masterKeyId});
+ getActivity(), new long[] { masterKeyId });
// TODO: binary?
content = keyringArmored.get(0);
// OnClickListener are set in onResume to prevent automatic dismissing of Dialogs
- // http://stackoverflow.com/questions/2620444/how-to-prevent-a-dialog-from-closing-when-a-button-is-clicked
+ // http://bit.ly/O5vfaR
alert.setPositiveButton(R.string.btn_next, null);
alert.setNegativeButton(android.R.string.cancel, null);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
index 1cf510d3a..7b21c189d 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java
@@ -18,8 +18,10 @@ package org.sufficientlysecure.keychain.ui.widget;
public interface Editor {
public interface EditorListener {
- public void onDeleted(Editor editor);
+ public void onDeleted(Editor editor, boolean wasNewItem);
+ public void onEdited();
}
public void setEditorListener(EditorListener listener);
+ public boolean needsSaving();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
new file mode 100644
index 000000000..6b2f3bf06
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
@@ -0,0 +1,203 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.beardedhen.androidbootstrap.FontAwesomeText;
+import org.sufficientlysecure.keychain.R;
+
+/**
+ * Class representing a LinearLayout that can fold and hide it's content when pressed
+ * To use just add the following to your xml layout
+
+ <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED"
+ custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED"
+ custom:foldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_FOLDED"
+ custom:unFoldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_UNFOLDED">
+
+ <include layout="@layout/ELEMENTS_TO_BE_FOLDED"/>
+
+ </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
+
+ */
+public class FoldableLinearLayout extends LinearLayout {
+
+ private FontAwesomeText mFoldableIcon;
+ private boolean mFolded;
+ private boolean mHasMigrated = false;
+ private Integer mShortAnimationDuration = null;
+ private TextView mFoldableTextView = null;
+ private LinearLayout mFoldableContainer = null;
+ private View mFoldableLayout = null;
+
+ private String mFoldedIconName;
+ private String mUnFoldedIconName;
+ private String mFoldedLabel;
+ private String mUnFoldedLabel;
+
+ public FoldableLinearLayout(Context context) {
+ super(context);
+ processAttributes(context, null);
+ }
+
+ public FoldableLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ processAttributes(context, attrs);
+ }
+
+ public FoldableLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs);
+ processAttributes(context, attrs);
+ }
+
+ /**
+ * Load given attributes to inner variables,
+ * @param context
+ * @param attrs
+ */
+ private void processAttributes(Context context, AttributeSet attrs) {
+ if (attrs != null) {
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.FoldableLinearLayout, 0, 0);
+ mFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_foldedIcon);
+ mUnFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_unFoldedIcon);
+ mFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_foldedLabel);
+ mUnFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_unFoldedLabel);
+ a.recycle();
+ }
+ // If any attribute isn't found then set a default one
+ mFoldedIconName = (mFoldedIconName == null) ? "fa-chevron-right" : mFoldedIconName;
+ mUnFoldedIconName = (mUnFoldedIconName == null) ? "fa-chevron-down" : mUnFoldedIconName;
+ mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.id.none) : mFoldedLabel;
+ mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.id.none) : mUnFoldedLabel;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ // if the migration has already happened
+ // there is no need to move any children
+ if (!mHasMigrated) {
+ migrateChildrenToContainer();
+ mHasMigrated = true;
+ }
+
+ initialiseInnerViews();
+
+ super.onFinishInflate();
+ }
+
+ /**
+ * Migrates Child views as declared in xml to the inner foldableContainer
+ */
+ private void migrateChildrenToContainer() {
+ // Collect children of FoldableLinearLayout as declared in XML
+ int childNum = getChildCount();
+ View[] children = new View[childNum];
+
+ for (int i = 0; i < childNum; i++) {
+ children[i] = getChildAt(i);
+ }
+ if (children[0].getId() == R.id.foldableControl) {
+
+ }
+
+ // remove all of them from FoldableLinearLayout
+ detachAllViewsFromParent();
+
+ // Inflate the inner foldable_linearlayout.xml
+ LayoutInflater inflator = (LayoutInflater) getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+
+ mFoldableLayout = inflator.inflate(R.layout.foldable_linearlayout, this, true);
+ mFoldableContainer = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableContainer);
+
+ // Push previously collected children into foldableContainer.
+ for (int i = 0; i < childNum; i++) {
+ addView(children[i]);
+ }
+ }
+
+ private void initialiseInnerViews() {
+ mFoldableIcon = (FontAwesomeText) mFoldableLayout.findViewById(R.id.foldableIcon);
+ mFoldableIcon.setIcon(mFoldedIconName);
+ mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText);
+ mFoldableTextView.setText(mFoldedLabel);
+
+ // retrieve and cache the system's short animation time
+ mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
+
+ LinearLayout foldableControl = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableControl);
+ foldableControl.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mFolded = !mFolded;
+ if (mFolded) {
+ mFoldableIcon.setIcon(mUnFoldedIconName);
+ mFoldableContainer.setVisibility(View.VISIBLE);
+ AlphaAnimation animation = new AlphaAnimation(0f, 1f);
+ animation.setDuration(mShortAnimationDuration);
+ mFoldableContainer.startAnimation(animation);
+ mFoldableTextView.setText(mUnFoldedLabel);
+
+ } else {
+ mFoldableIcon.setIcon(mFoldedIconName);
+ AlphaAnimation animation = new AlphaAnimation(1f, 0f);
+ animation.setDuration(mShortAnimationDuration);
+ animation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) { }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ // making sure that at the end the container is completely removed from view
+ mFoldableContainer.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) { }
+ });
+ mFoldableContainer.startAnimation(animation);
+ mFoldableTextView.setText(mFoldedLabel);
+ }
+ }
+ });
+
+ }
+
+ /**
+ * Adds provided child view to foldableContainer View
+ * @param child
+ */
+ @Override
+ public void addView(View child) {
+ if (mFoldableContainer != null) {
+ mFoldableContainer.addView(child);
+ }
+ }
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
index 7e0acfa28..c7bd1c987 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java
@@ -26,17 +26,30 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.DatePicker;
+import android.widget.LinearLayout;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
-import org.sufficientlysecure.keychain.Id;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Choice;
import java.text.DateFormat;
-import java.util.*;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+import java.util.Vector;
public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
private PGPSecretKey mKey;
@@ -47,11 +60,30 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
BootstrapButton mDeleteButton;
TextView mAlgorithm;
TextView mKeyId;
- Spinner mUsage;
TextView mCreationDate;
BootstrapButton mExpiryDateButton;
GregorianCalendar mCreatedDate;
GregorianCalendar mExpiryDate;
+ GregorianCalendar mOriginalExpiryDate = null;
+ CheckBox mChkCertify;
+ CheckBox mChkSign;
+ CheckBox mChkEncrypt;
+ CheckBox mChkAuthenticate;
+ int mUsage;
+ int mOriginalUsage;
+ boolean mIsNewKey;
+
+ private CheckBox.OnCheckedChangeListener mCheckChanged = new CheckBox.OnCheckedChangeListener()
+ {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
+ {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+ };
+
private int mDatePickerResultCount = 0;
private DatePickerDialog.OnDateSetListener mExpiryDateSetListener =
@@ -61,7 +93,20 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
if (mDatePickerResultCount++ == 0) {
GregorianCalendar date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
date.set(year, monthOfYear, dayOfMonth);
- setExpiryDate(date);
+ if (mOriginalExpiryDate != null) {
+ long numDays = (date.getTimeInMillis() / 86400000) -
+ (mOriginalExpiryDate.getTimeInMillis() / 86400000);
+ if (numDays == 0) {
+ setExpiryDate(mOriginalExpiryDate);
+ } else {
+ setExpiryDate(date);
+ }
+ } else {
+ setExpiryDate(date);
+ }
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
};
@@ -83,21 +128,17 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
mKeyId = (TextView) findViewById(R.id.keyId);
mCreationDate = (TextView) findViewById(R.id.creation);
mExpiryDateButton = (BootstrapButton) findViewById(R.id.expiry);
- mUsage = (Spinner) findViewById(R.id.usage);
- Choice choices[] = {
- new Choice(Id.choice.usage.sign_only, getResources().getString(
- R.string.choice_sign_only)),
- new Choice(Id.choice.usage.encrypt_only, getResources().getString(
- R.string.choice_encrypt_only)),
- new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString(
- R.string.choice_sign_and_encrypt)), };
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
mDeleteButton = (BootstrapButton) findViewById(R.id.delete);
mDeleteButton.setOnClickListener(this);
+ mChkCertify = (CheckBox) findViewById(R.id.chkCertify);
+ mChkCertify.setOnCheckedChangeListener(mCheckChanged);
+ mChkSign = (CheckBox) findViewById(R.id.chkSign);
+ mChkSign.setOnCheckedChangeListener(mCheckChanged);
+ mChkEncrypt = (CheckBox) findViewById(R.id.chkEncrypt);
+ mChkEncrypt.setOnCheckedChangeListener(mCheckChanged);
+ mChkAuthenticate = (CheckBox) findViewById(R.id.chkAuthenticate);
+ mChkAuthenticate.setOnCheckedChangeListener(mCheckChanged);
setExpiryDate(null);
@@ -109,7 +150,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
date = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
}
/*
- * Using custom DatePickerDialog which overrides the setTitle because
+ * Using custom DatePickerDialog which overrides the setTitle because
* the DatePickerDialog title is buggy (unix warparound bug).
* See: https://code.google.com/p/android/issues/detail?id=49066
*/
@@ -125,6 +166,9 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
// Note: Ignore results after the first one - android sends multiples.
if (mDatePickerResultCount++ == 0) {
setExpiryDate(null);
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
});
@@ -152,15 +196,17 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
super.onFinishInflate();
}
- public void setCanEdit(boolean bCanEdit) {
- if (!bCanEdit) {
+ public void setCanBeEdited(boolean canBeEdited) {
+ if (!canBeEdited) {
mDeleteButton.setVisibility(View.INVISIBLE);
- mUsage.setEnabled(false);
mExpiryDateButton.setEnabled(false);
+ mChkSign.setEnabled(false); //certify is always disabled
+ mChkEncrypt.setEnabled(false);
+ mChkAuthenticate.setEnabled(false);
}
}
- public void setValue(PGPSecretKey key, boolean isMasterKey, int usage) {
+ public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) {
mKey = key;
mIsMasterKey = isMasterKey;
@@ -175,47 +221,46 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
Vector<Choice> choices = new Vector<Choice>();
boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT);
boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA);
- if (!isElGamalKey) {
- choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString(
- R.string.choice_sign_only)));
+ if (isElGamalKey) {
+ mChkSign.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow) findViewById(R.id.row_sign);
+ table.removeView(row);
}
- if (!mIsMasterKey && !isDSAKey) {
- choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString(
- R.string.choice_encrypt_only)));
+ if (isDSAKey) {
+ mChkEncrypt.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow) findViewById(R.id.row_encrypt);
+ table.removeView(row);
}
- if (!isElGamalKey && !isDSAKey) {
- choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString(
- R.string.choice_sign_and_encrypt)));
+ if (!mIsMasterKey) {
+ mChkCertify.setVisibility(View.INVISIBLE);
+ TableLayout table = (TableLayout) findViewById(R.id.table_keylayout);
+ TableRow row = (TableRow) findViewById(R.id.row_certify);
+ table.removeView(row);
+ } else {
+ TextView mLabelUsage2 = (TextView) findViewById(R.id.label_usage2);
+ mLabelUsage2.setVisibility(View.INVISIBLE);
}
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(),
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mUsage.setAdapter(adapter);
-
- // Set value in choice dropdown to key
int selectId = 0;
- if (PgpKeyHelper.isEncryptionKey(key)) {
- if (PgpKeyHelper.isSigningKey(key)) {
- selectId = Id.choice.usage.sign_and_encrypt;
- } else {
- selectId = Id.choice.usage.encrypt_only;
- }
+ mIsNewKey = isNewKey;
+ if (isNewKey) {
+ mUsage = usage;
+ mChkCertify.setChecked((usage & KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER);
+ mChkSign.setChecked((usage & KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA);
+ mChkEncrypt.setChecked(((usage & KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) ||
+ ((usage & KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE));
+ mChkAuthenticate.setChecked((usage & KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION);
} else {
- // set usage if it is predefined
- if (usage != -1) {
- selectId = usage;
- } else {
- selectId = Id.choice.usage.sign_only;
- }
-
- }
-
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == selectId) {
- mUsage.setSelection(i);
- break;
+ mUsage = PgpKeyHelper.getKeyUsage(key);
+ mOriginalUsage = mUsage;
+ if (key.isMasterKey()) {
+ mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key));
}
+ mChkSign.setChecked(PgpKeyHelper.isSigningKey(key));
+ mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key));
+ mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key));
}
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
@@ -228,6 +273,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
} else {
cal.setTime(PgpKeyHelper.getExpiryDate(key));
setExpiryDate(cal);
+ mOriginalExpiryDate = cal;
}
}
@@ -241,7 +287,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
if (v == mDeleteButton) {
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, mIsNewKey);
}
}
}
@@ -273,9 +319,48 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
public int getUsage() {
- return ((Choice) mUsage.getSelectedItem()).getId();
+ mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) |
+ (mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0);
+ mUsage = (mUsage & ~KeyFlags.SIGN_DATA) |
+ (mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0);
+ mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) |
+ (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0);
+ mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) |
+ (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0);
+ mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) |
+ (mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0);
+
+ return mUsage;
+ }
+
+ public boolean needsSaving() {
+ if (mIsNewKey) {
+ return true;
+ }
+
+ boolean retval = (getUsage() != mOriginalUsage);
+
+ boolean dateChanged;
+ boolean mOEDNull = (mOriginalExpiryDate == null);
+ boolean mEDNull = (mExpiryDate == null);
+ if (mOEDNull != mEDNull) {
+ dateChanged = true;
+ } else {
+ if (mOEDNull) {
+ //both null, no change
+ dateChanged = false;
+ } else {
+ dateChanged = ((mExpiryDate.compareTo(mOriginalExpiryDate)) != 0);
+ }
+ }
+ retval |= dateChanged;
+
+ return retval;
}
+ public boolean getIsNewKey() {
+ return mIsNewKey;
+ }
}
class ExpiryDatePickerDialog extends DatePickerDialog {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
index f92c7532a..171763672 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java
@@ -66,11 +66,16 @@ public class KeyServerEditor extends LinearLayout implements Editor, OnClickList
if (v == mDeleteButton) {
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, false);
}
}
}
+ @Override
+ public boolean needsSaving() {
+ return false;
+ }
+
public void setEditorListener(EditorListener listener) {
mEditorListener = listener;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
index e5b6003b1..fb59cd3b7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java
@@ -32,7 +32,10 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.beardedhen.androidbootstrap.BootstrapButton;
+
+import org.spongycastle.openpgp.PGPKeyFlags;
import org.spongycastle.openpgp.PGPSecretKey;
+
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
@@ -44,23 +47,33 @@ import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
import org.sufficientlysecure.keychain.util.Choice;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Vector;
-public class SectionView extends LinearLayout implements OnClickListener, EditorListener {
+public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Editor {
private LayoutInflater mInflater;
private BootstrapButton mPlusButton;
private ViewGroup mEditors;
private TextView mTitle;
private int mType = 0;
+ private EditorListener mEditorListener = null;
private Choice mNewKeyAlgorithmChoice;
private int mNewKeySize;
- private boolean mCanEdit = true;
+ private boolean mOldItemDeleted = false;
+ private ArrayList<String> mDeletedIDs = new ArrayList<String>();
+ private ArrayList<PGPSecretKey> mDeletedKeys = new ArrayList<PGPSecretKey>();
+ private boolean mCanBeEdited = true;
private ActionBarActivity mActivity;
private ProgressDialogFragment mGeneratingDialog;
+ public void setEditorListener(EditorListener listener) {
+ mEditorListener = listener;
+ }
+
public SectionView(Context context) {
super(context);
mActivity = (ActionBarActivity) context;
@@ -94,9 +107,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
}
}
- public void setCanEdit(boolean bCanEdit) {
- mCanEdit = bCanEdit;
- if (!mCanEdit) {
+ public void setCanBeEdited(boolean canBeEdited) {
+ mCanBeEdited = canBeEdited;
+ if (!mCanBeEdited) {
mPlusButton.setVisibility(View.INVISIBLE);
}
}
@@ -124,8 +137,26 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
/**
* {@inheritDoc}
*/
- public void onDeleted(Editor editor) {
+ public void onDeleted(Editor editor, boolean wasNewItem) {
+ mOldItemDeleted |= !wasNewItem;
+ if (mOldItemDeleted) {
+ if (mType == Id.type.user_id) {
+ mDeletedIDs.add(((UserIdEditor) editor).getOriginalID());
+ } else if (mType == Id.type.key) {
+ mDeletedKeys.add(((KeyEditor) editor).getValue());
+ }
+ }
this.updateEditorsVisible();
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+
+ @Override
+ public void onEdited() {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
protected void updateEditorsVisible() {
@@ -133,20 +164,118 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE);
}
+ public boolean needsSaving() {
+ //check each view for needs saving, take account of deleted items
+ boolean ret = mOldItemDeleted;
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ ret |= editor.needsSaving();
+ if (mType == Id.type.user_id) {
+ ret |= ((UserIdEditor) editor).primarySwapped();
+ }
+ }
+ return ret;
+ }
+
+ public boolean primaryChanged() {
+ boolean ret = false;
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ if (mType == Id.type.user_id) {
+ ret |= ((UserIdEditor) editor).primarySwapped();
+ }
+ }
+ return ret;
+ }
+
+ public String getOriginalPrimaryID() {
+ //NB: this will have to change when we change how Primary IDs are chosen, and so we need to be
+ // careful about where Master key capabilities are stored... multiple primaries and
+ // revoked ones make this harder than the simple case we are continuing to assume here
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ if (mType == Id.type.user_id) {
+ if (((UserIdEditor) editor).getIsOriginallyMainUserID()) {
+ return ((UserIdEditor) editor).getOriginalID();
+ }
+ }
+ }
+ return null;
+ }
+
+ public ArrayList<String> getOriginalIDs() {
+ ArrayList<String> orig = new ArrayList<String>();
+ if (mType == Id.type.user_id) {
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i);
+ if (editor.isMainUserId()) {
+ orig.add(0, editor.getOriginalID());
+ } else {
+ orig.add(editor.getOriginalID());
+ }
+ }
+ return orig;
+ } else {
+ return null;
+ }
+ }
+
+ public ArrayList<String> getDeletedIDs() {
+ return mDeletedIDs;
+ }
+
+ public ArrayList<PGPSecretKey> getDeletedKeys() {
+ return mDeletedKeys;
+ }
+
+ public List<Boolean> getNeedsSavingArray() {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ Editor editor = (Editor) mEditors.getChildAt(i);
+ mList.add(editor.needsSaving());
+ }
+ return mList;
+ }
+
+ public List<Boolean> getNewIDFlags() {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i);
+ if (editor.isMainUserId()) {
+ mList.add(0, editor.getIsNewID());
+ } else {
+ mList.add(editor.getIsNewID());
+ }
+ }
+ return mList;
+ }
+
+ public List<Boolean> getNewKeysArray() {
+ ArrayList<Boolean> mList = new ArrayList<Boolean>();
+ if (mType == Id.type.key) {
+ for (int i = 0; i < mEditors.getChildCount(); ++i) {
+ KeyEditor editor = (KeyEditor) mEditors.getChildAt(i);
+ mList.add(editor.getIsNewKey());
+ }
+ }
+ return mList;
+ }
+
/**
* {@inheritDoc}
*/
public void onClick(View v) {
- if (mCanEdit) {
+ if (mCanBeEdited) {
switch (mType) {
case Id.type.user_id: {
UserIdEditor view = (UserIdEditor) mInflater.inflate(
R.layout.edit_key_user_id_item, mEditors, false);
view.setEditorListener(this);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
+ view.setValue("", mEditors.getChildCount() == 0, true);
mEditors.addView(view);
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
break;
}
@@ -185,18 +314,15 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item,
mEditors, false);
view.setEditorListener(this);
- view.setValue(userId);
- if (mEditors.getChildCount() == 0) {
- view.setIsMainUserId(true);
- }
- view.setCanEdit(mCanEdit);
+ view.setValue(userId, mEditors.getChildCount() == 0, false);
+ view.setCanBeEdited(mCanBeEdited);
mEditors.addView(view);
}
this.updateEditorsVisible();
}
- public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages) {
+ public void setKeys(Vector<PGPSecretKey> list, Vector<Integer> usages, boolean newKeys) {
if (mType != Id.type.key) {
return;
}
@@ -209,8 +335,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
false);
view.setEditorListener(this);
boolean isMasterKey = (mEditors.getChildCount() == 0);
- view.setValue(list.get(i), isMasterKey, usages.get(i));
- view.setCanEdit(mCanEdit);
+ view.setValue(list.get(i), isMasterKey, usages.get(i), newKeys);
+ view.setCanBeEdited(mCanBeEdited);
mEditors.addView(view);
}
@@ -256,11 +382,11 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
}
});
- // Message is received after generating is done in ApgService
+ // Message is received after generating is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity,
mGeneratingDialog) {
public void handleMessage(Message message) {
- // handle messages by standard ApgHandler first
+ // handle messages by standard KeychainIntentServiceHandler first
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
@@ -289,8 +415,15 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor
KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item,
mEditors, false);
view.setEditorListener(SectionView.this);
- view.setValue(newKey, newKey.isMasterKey(), -1);
+ int usage = 0;
+ if (mEditors.getChildCount() == 0) {
+ usage = PGPKeyFlags.CAN_CERTIFY;
+ }
+ view.setValue(newKey, newKey.isMasterKey(), usage, true);
mEditors.addView(view);
SectionView.this.updateEditorsVisible();
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
index ed81b162e..2253872d5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java
@@ -24,33 +24,36 @@ import android.util.Patterns;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.widget.*;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
import com.beardedhen.androidbootstrap.BootstrapButton;
+
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ContactHelper;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import java.util.regex.Matcher;
-import java.util.regex.Pattern;
public class UserIdEditor extends LinearLayout implements Editor, OnClickListener {
private EditorListener mEditorListener = null;
private BootstrapButton mDeleteButton;
private RadioButton mIsMainUserId;
+ private String mOriginalID;
private EditText mName;
+ private String mOriginalName;
private AutoCompleteTextView mEmail;
+ private String mOriginalEmail;
private EditText mComment;
+ private String mOriginalComment;
+ private boolean mOriginallyMainUserID;
+ private boolean mIsNewId;
- public static class NoNameException extends Exception {
- static final long serialVersionUID = 0xf812773343L;
-
- public NoNameException(String message) {
- super(message);
- }
- }
-
- public void setCanEdit(boolean bCanEdit) {
- if (!bCanEdit) {
+ public void setCanBeEdited(boolean canBeEdited) {
+ if (!canBeEdited) {
mDeleteButton.setVisibility(View.INVISIBLE);
mName.setEnabled(false);
mIsMainUserId.setEnabled(false);
@@ -59,14 +62,6 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
}
}
- public static class NoEmailException extends Exception {
- static final long serialVersionUID = 0xf812773344L;
-
- public NoEmailException(String message) {
- super(message);
- }
- }
-
public static class InvalidEmailException extends Exception {
static final long serialVersionUID = 0xf812773345L;
@@ -83,6 +78,23 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
super(context, attrs);
}
+ TextWatcher mTextWatcher = new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
+ }
+ };
+
@Override
protected void onFinishInflate() {
setDrawingCacheEnabled(true);
@@ -94,8 +106,10 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
mIsMainUserId.setOnClickListener(this);
mName = (EditText) findViewById(R.id.name);
+ mName.addTextChangedListener(mTextWatcher);
mEmail = (AutoCompleteTextView) findViewById(R.id.email);
mComment = (EditText) findViewById(R.id.comment);
+ mComment.addTextChangedListener(mTextWatcher);
mEmail.setThreshold(1); // Start working from first character
@@ -127,36 +141,45 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
// remove drawable if email is empty
mEmail.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
}
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
});
super.onFinishInflate();
}
- public void setValue(String userId) {
+ public void setValue(String userId, boolean isMainID, boolean isNewId) {
+
mName.setText("");
+ mOriginalName = "";
mComment.setText("");
+ mOriginalComment = "";
mEmail.setText("");
-
- Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$");
- Matcher matcher = withComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mComment.setText(matcher.group(2));
- mEmail.setText(matcher.group(3));
- return;
+ mOriginalEmail = "";
+ mIsNewId = isNewId;
+ mOriginalID = userId;
+
+ String[] result = PgpKeyHelper.splitUserId(userId);
+ if (result[0] != null) {
+ mName.setText(result[0]);
+ mOriginalName = result[0];
}
-
- Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$");
- matcher = withoutComment.matcher(userId);
- if (matcher.matches()) {
- mName.setText(matcher.group(1));
- mEmail.setText(matcher.group(2));
- return;
+ if (result[1] != null) {
+ mEmail.setText(result[1]);
+ mOriginalEmail = result[1];
+ }
+ if (result[2] != null) {
+ mComment.setText(result[2]);
+ mOriginalComment = result[2];
}
+
+ mOriginallyMainUserID = isMainID;
+ setIsMainUserId(isMainID);
}
- public String getValue() throws NoNameException, NoEmailException {
+ public String getValue() {
String name = ("" + mName.getText()).trim();
String email = ("" + mEmail.getText()).trim();
String comment = ("" + mComment.getText()).trim();
@@ -173,16 +196,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
// ok, empty one...
return userId;
}
-
- // otherwise make sure that name and email exist
- if (name.equals("")) {
- throw new NoNameException("need a name");
- }
-
- if (email.equals("")) {
- throw new NoEmailException("need an email");
- }
-
+ //TODO: check gpg accepts an entirely empty ID packet. specs say this is allowed
return userId;
}
@@ -192,7 +206,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
boolean wasMainUserId = mIsMainUserId.isChecked();
parent.removeView(this);
if (mEditorListener != null) {
- mEditorListener.onDeleted(this);
+ mEditorListener.onDeleted(this, mIsNewId);
}
if (wasMainUserId && parent.getChildCount() > 0) {
UserIdEditor editor = (UserIdEditor) parent.getChildAt(0);
@@ -207,6 +221,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
editor.setIsMainUserId(false);
}
}
+ if (mEditorListener != null) {
+ mEditorListener.onEdited();
+ }
}
}
@@ -221,4 +238,28 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene
public void setEditorListener(EditorListener listener) {
mEditorListener = listener;
}
+
+ @Override
+ public boolean needsSaving() {
+ boolean retval = false; //(mOriginallyMainUserID != isMainUserId());
+ retval |= !(mOriginalName.equals(("" + mName.getText()).trim()));
+ retval |= !(mOriginalEmail.equals(("" + mEmail.getText()).trim()));
+ retval |= !(mOriginalComment.equals(("" + mComment.getText()).trim()));
+ retval |= mIsNewId;
+ return retval;
+ }
+
+ public boolean getIsOriginallyMainUserID() {
+ return mOriginallyMainUserID;
+ }
+
+ public boolean primarySwapped() {
+ return (mOriginallyMainUserID != isMainUserId());
+ }
+
+ public String getOriginalID() {
+ return mOriginalID;
+ }
+
+ public boolean getIsNewID() { return mIsNewId; }
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
index 42fb03a3e..5efc732e4 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java
@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.util;
-import android.text.Html;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -30,26 +29,30 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
+
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
-import java.net.*;
-import java.util.*;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-/**
- * TODO:
- * rewrite to use machine readable output.
- * <p/>
- * see http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5
- * https://github.com/openpgp-keychain/openpgp-keychain/issues/259
- */
public class HkpKeyServer extends KeyServer {
private static class HttpError extends Exception {
private static final long serialVersionUID = 1718783705229428893L;
@@ -74,16 +77,69 @@ public class HkpKeyServer extends KeyServer {
private String mHost;
private short mPort;
- // example:
- // pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a
- // href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge
- // &lt;joerg@joergrunge.de&gt;</a>
+ /**
+ * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags%
+ * <ul>
+ * <li>%<b>keyid</b>% = this is either the fingerprint or the key ID of the key.
+ * Either the 16-digit or 8-digit key IDs are acceptable, but obviously the fingerprint is best.
+ * </li>
+ * <li>%<b>algo</b>% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc).
+ * See <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a></li>
+ * <li>%<b>keylen</b>% = the key length (i.e. 1024, 2048, 4096, etc.)</li>
+ * <li>%<b>creationdate</b>% = creation date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>expirationdate</b>% = expiration date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>flags</b>% = letter codes to indicate details of the key, if any. Flags may be in any
+ * order. The meaning of "disabled" is implementation-specific. Note that individual flags may
+ * be unimplemented, so the absence of a given flag does not necessarily mean the absence of the
+ * detail.
+ * <ul>
+ * <li>r == revoked</li>
+ * <li>d == disabled</li>
+ * <li>e == expired</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @see <a href="http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5.2">
+ * 5.2. Machine Readable Indexes</a>
+ * in Internet-Draft OpenPGP HTTP Keyserver Protocol Document
+ */
public static final Pattern PUB_KEY_LINE = Pattern
- .compile(
- "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
+ .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]*))+", // one or more uid lines
+ Pattern.CASE_INSENSITIVE);
+
+ /**
+ * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags%
+ * <ul>
+ * <li>%<b>escaped uid string</b>% = the user ID string, with HTTP %-escaping for anything that
+ * isn't 7-bit safe as well as for the ":" character. Any other characters may be escaped, as
+ * desired.</li>
+ * <li>%<b>creationdate</b>% = creation date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>expirationdate</b>% = expiration date of the key in standard
+ * <a href="http://tools.ietf.org/html/rfc2440#section-9.1">RFC-2440</a> form (i.e. number of
+ * seconds since 1/1/1970 UTC time)</li>
+ * <li>%<b>flags</b>% = letter codes to indicate details of the key, if any. Flags may be in any
+ * order. The meaning of "disabled" is implementation-specific. Note that individual flags may
+ * be unimplemented, so the absence of a given flag does not necessarily mean the absence of
+ * the detail.
+ * <ul>
+ * <li>r == revoked</li>
+ * <li>d == disabled</li>
+ * <li>e == expired</li>
+ * </ul>
+ * </li>
+ * </ul>
+ */
+ public static final Pattern UID_LINE = Pattern
+ .compile("uid:(.*):([0-9]+):([0-9]*):([rde]*)",
Pattern.CASE_INSENSITIVE);
- public static final Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE
- | Pattern.CASE_INSENSITIVE);
private static final short PORT_DEFAULT = 11371;
@@ -136,6 +192,7 @@ public class HkpKeyServer extends KeyServer {
for (int i = 0; i < ips.length; ++i) {
try {
String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request;
+ Log.d(Constants.TAG, "hkp keyserver query: " + url);
URL realUrl = new URL(url);
HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
conn.setConnectTimeout(5000);
@@ -173,9 +230,9 @@ public class HkpKeyServer extends KeyServer {
} catch (UnsupportedEncodingException e) {
return null;
}
- String request = "/pks/lookup?op=index&search=" + encodedQuery;
+ String request = "/pks/lookup?op=index&options=mr&search=" + encodedQuery;
- String data = null;
+ String data;
try {
data = query(request);
} catch (HttpError e) {
@@ -193,48 +250,64 @@ public class HkpKeyServer extends KeyServer {
throw new QueryException("querying server(s) for '" + mHost + "' failed");
}
- Matcher matcher = PUB_KEY_LINE.matcher(data);
+ final Matcher matcher = PUB_KEY_LINE.matcher(data);
while (matcher.find()) {
- ImportKeysListEntry info = new ImportKeysListEntry();
- info.bitStrength = Integer.parseInt(matcher.group(1));
- info.algorithm = matcher.group(2);
- info.hexKeyId = "0x" + matcher.group(3);
- info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(3));
- String chunks[] = matcher.group(4).split("-");
-
- GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
- tmpGreg.set(Integer.parseInt(chunks[0]), Integer.parseInt(chunks[1]),
- Integer.parseInt(chunks[2]));
- info.date = tmpGreg.getTime();
- info.userIds = new ArrayList<String>();
- if (matcher.group(5).startsWith("*** KEY")) {
- info.revoked = true;
+ final ImportKeysListEntry entry = new ImportKeysListEntry();
+
+ entry.setBitStrength(Integer.parseInt(matcher.group(3)));
+
+ final int algorithmId = Integer.decode(matcher.group(2));
+ entry.setAlgorithm(ImportKeysListEntry.getAlgorithmFromId(algorithmId));
+
+ // 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);
+ if (fingerprintOrKeyId.length() > 16) {
+ entry.setFingerPrintHex(fingerprintOrKeyId.toLowerCase(Locale.US));
+ entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length()
+ - 16, fingerprintOrKeyId.length()));
} else {
- String tmp = matcher.group(5).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
+ // set key id only
+ entry.setKeyIdHex("0x" + fingerprintOrKeyId);
}
- if (matcher.group(6).length() > 0) {
- Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6));
- while (matcher2.find()) {
- String tmp = matcher2.group(1).replaceAll("<.*?>", "");
- tmp = Html.fromHtml(tmp).toString();
- info.userIds.add(tmp);
+
+ final long creationDate = Long.parseLong(matcher.group(4));
+ final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
+ tmpGreg.setTimeInMillis(creationDate * 1000);
+ entry.setDate(tmpGreg.getTime());
+
+ entry.setRevoked(matcher.group(6).contains("r"));
+
+ ArrayList<String> userIds = new ArrayList<String>();
+ final String uidLines = matcher.group(7);
+ final Matcher uidMatcher = UID_LINE.matcher(uidLines);
+ while (uidMatcher.find()) {
+ String tmp = uidMatcher.group(1).trim();
+ if (tmp.contains("%")) {
+ try {
+ // converts Strings like "Universit%C3%A4t" to a proper encoding form "Universität".
+ tmp = (URLDecoder.decode(tmp, "UTF8"));
+ } catch (UnsupportedEncodingException ignored) {
+ // will never happen, because "UTF8" is supported
+ }
}
+ userIds.add(tmp);
}
- results.add(info);
- }
+ entry.setUserIds(userIds);
+ results.add(entry);
+ }
return results;
}
@Override
- public String get(long keyId) throws QueryException {
+ public String get(String keyIdHex) throws QueryException {
HttpClient client = new DefaultHttpClient();
try {
- HttpGet get = new HttpGet("http://" + mHost + ":" + mPort
- + "/pks/lookup?op=get&search=" + PgpKeyHelper.convertKeyIdToHex(keyId));
-
+ String query = "http://" + mHost + ":" + mPort +
+ "/pks/lookup?op=get&options=mr&search=" + keyIdHex;
+ Log.d(Constants.TAG, "hkp keyserver get: " + query);
+ HttpGet get = new HttpGet(query);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new QueryException("not found");
@@ -257,13 +330,14 @@ public class HkpKeyServer extends KeyServer {
}
@Override
- public void add(String armoredText) throws AddKeyException {
+ public void add(String armoredKey) throws AddKeyException {
HttpClient client = new DefaultHttpClient();
try {
- HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add");
-
+ String query = "http://" + mHost + ":" + mPort + "/pks/add";
+ HttpPost post = new HttpPost(query);
+ Log.d(Constants.TAG, "hkp keyserver add: " + query);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
- nameValuePairs.add(new BasicNameValuePair("keytext", armoredText));
+ nameValuePairs.add(new BasicNameValuePair("keytext", armoredKey));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = client.execute(post);
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
index 092c14e00..28cfa11f2 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/InputData.java
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
index b95b3ee6a..ae87deb31 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IntentIntegratorSupportV4.java
@@ -16,9 +16,10 @@
package org.sufficientlysecure.keychain.util;
+import com.google.zxing.integration.android.IntentIntegrator;
+
import android.content.Intent;
import android.support.v4.app.Fragment;
-import com.google.zxing.integration.android.IntentIntegrator;
/**
* IntentIntegrator for the V4 Android compatibility package.
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
index 40105df4f..3af674526 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/IterableIterator.java
@@ -24,7 +24,7 @@ public class IterableIterator<T> implements Iterable<T> {
public IterableIterator(Iterator<T> iter, boolean failsafe) {
mIter = iter;
- if(failsafe && mIter == null) {
+ if (failsafe && mIter == null) {
// is there a better way?
mIter = new ArrayList<T>().iterator();
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
index a31fdc5ae..7f70867a5 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java
@@ -46,7 +46,7 @@ public abstract class KeyServer {
abstract List<ImportKeysListEntry> search(String query) throws QueryException, TooManyResponses,
InsufficientQuery;
- abstract String get(long keyId) throws QueryException;
+ abstract String get(String keyIdHex) throws QueryException;
- abstract void add(String armoredText) throws AddKeyException;
+ abstract void add(String armoredKey) throws AddKeyException;
}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
index 14b2a2211..b205bd556 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeychainServiceListener.java
@@ -1,16 +1,20 @@
/*
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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.
*
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
+ * 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.util;
public interface KeychainServiceListener {
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
index 377a8d5d6..5ec915810 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PausableThreadPoolExecutor.java
@@ -17,7 +17,11 @@
package org.sufficientlysecure.keychain.util;
-import java.util.concurrent.*;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java
index a783d7820..4fcd3047f 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/PositionAwareInputStream.java
@@ -1,4 +1,6 @@
/*
+ * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
new file mode 100644
index 000000000..23961c05f
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressScaler.java
@@ -0,0 +1,50 @@
+/*
+ * 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.util;
+
+/** This is a simple class that wraps a ProgressDialogUpdater, scaling the progress
+ * values into a specified range.
+ */
+public class ProgressScaler implements ProgressDialogUpdater {
+
+ final ProgressDialogUpdater mWrapped;
+ final int mFrom, mTo, mMax;
+
+ public ProgressScaler(ProgressDialogUpdater wrapped, int from, int to, int max) {
+ this.mWrapped = wrapped;
+ this.mFrom = from;
+ this.mTo = to;
+ this.mMax = max;
+ }
+
+ /**
+ * Set progressDialogUpdater of ProgressDialog by sending message to handler on UI thread
+ */
+ public void setProgress(String message, int progress, int max) {
+ mWrapped.setProgress(message, mFrom + progress * (mTo - mFrom) / max, mMax);
+ }
+
+ public void setProgress(int resourceId, int progress, int max) {
+ mWrapped.setProgress(resourceId, progress, mMax);
+ }
+
+ public void setProgress(int progress, int max) {
+ mWrapped.setProgress(progress, max);
+ }
+
+}
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
index 8c3367bea..af9034aa7 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/QrCodeUtils.java
@@ -18,14 +18,16 @@
package org.sufficientlysecure.keychain.util;
-import android.graphics.Bitmap;
-import android.graphics.Color;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
import org.sufficientlysecure.keychain.Constants;
import java.util.Hashtable;
diff --git a/OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_person.png b/OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_person.png
new file mode 100644
index 000000000..9fd81097b
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_person.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-hdpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-hdpi/icon.png
index 571634090..f5487599b 100644
--- a/OpenPGP-Keychain/src/main/res/drawable-hdpi/icon.png
+++ b/OpenPGP-Keychain/src/main/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png b/OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png
new file mode 100644
index 000000000..75f45eb54
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-ldpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-ldpi/icon.png
index 63bdba209..7cd482bff 100644
--- a/OpenPGP-Keychain/src/main/res/drawable-ldpi/icon.png
+++ b/OpenPGP-Keychain/src/main/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_person.png b/OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_person.png
new file mode 100644
index 000000000..359da1c12
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_person.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-mdpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-mdpi/icon.png
index bab8c56bb..34f1420ac 100644
--- a/OpenPGP-Keychain/src/main/res/drawable-mdpi/icon.png
+++ b/OpenPGP-Keychain/src/main/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-xhdpi/ic_action_person.png b/OpenPGP-Keychain/src/main/res/drawable-xhdpi/ic_action_person.png
new file mode 100644
index 000000000..03eeb8d6a
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/drawable-xhdpi/ic_action_person.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-xhdpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-xhdpi/icon.png
index 79b8e27c6..32584f3ff 100644
--- a/OpenPGP-Keychain/src/main/res/drawable-xhdpi/icon.png
+++ b/OpenPGP-Keychain/src/main/res/drawable-xhdpi/icon.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/ic_action_person.png b/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/ic_action_person.png
new file mode 100644
index 000000000..fd1bcdd45
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/ic_action_person.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/icon.png
index ac8190c93..b2922309f 100644
--- a/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/icon.png
+++ b/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/icon.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable-xxxhdpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-xxxhdpi/icon.png
index cdc0fc9f0..93ea6b0f5 100644
--- a/OpenPGP-Keychain/src/main/res/drawable-xxxhdpi/icon.png
+++ b/OpenPGP-Keychain/src/main/res/drawable-xxxhdpi/icon.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/drawable/revoked_key_small.png b/OpenPGP-Keychain/src/main/res/drawable/revoked_key_small.png
new file mode 100644
index 000000000..f9ed0596f
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/drawable/revoked_key_small.png
Binary files differ
diff --git a/OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml
new file mode 100644
index 000000000..c0021261e
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal" >
+ <android.support.v4.widget.DrawerLayout
+ android:id="@+id/drawer_layout"
+
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+
+ <include layout="@layout/drawer_list"/>
+
+ </android.support.v4.widget.DrawerLayout>
+
+ <include layout="@layout/api_apps_list_content"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml
new file mode 100644
index 000000000..26aed0831
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <android.support.v4.widget.DrawerLayout
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/drawer_list"/>
+
+ </android.support.v4.widget.DrawerLayout>
+
+ <include layout="@layout/decrypt_content"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml
new file mode 100644
index 000000000..7d0d44074
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <android.support.v4.widget.DrawerLayout
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ xmlns:fontawesometext="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/drawer_list"/>
+
+ </android.support.v4.widget.DrawerLayout>
+
+ <include layout="@layout/encrypt_content"/>
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml
new file mode 100644
index 000000000..2cb408441
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v4.widget.DrawerLayout
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+
+ <include layout="@layout/drawer_list"/>
+
+ </android.support.v4.widget.DrawerLayout>
+
+ <include layout="@layout/import_keys_content"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml
new file mode 100644
index 000000000..6636f12ff
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <android.support.v4.widget.DrawerLayout
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include layout="@layout/drawer_list"/>
+
+ </android.support.v4.widget.DrawerLayout>
+
+ <include layout="@layout/key_list_content"/>
+
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml
new file mode 100644
index 000000000..3557c1f00
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ 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>
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml
new file mode 100644
index 000000000..32843eb29
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/listPreferredItemHeight"
+ android:layout_marginBottom="4dp"
+ android:layout_marginTop="4dp"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/api_account_settings_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginRight="6dp"
+ android:src="@drawable/ic_action_person" />
+
+ <TextView
+ android:id="@+id/api_account_settings_acc_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@+id/api_account_settings_icon"
+ android:gravity="center_vertical"
+ android:orientation="vertical"
+ android:text="Name (set in-code)"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+ </RelativeLayout>
+
+ <fragment
+ android:id="@+id/api_account_settings_select_key_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:layout="@layout/select_secret_key_layout_fragment" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/api_account_settings_create_key"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginTop="4dp"
+ android:text="@string/api_settings_create_key"
+ bootstrapbutton:bb_icon_left="fa-key"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+
+ <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ custom:foldedLabel="@string/api_settings_show_advanced"
+ custom:unFoldedLabel="@string/api_settings_hide_advanced"
+ custom:foldedIcon="fa-chevron-right"
+ custom:unFoldedIcon="fa-chevron-down">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/label_encryption_algorithm"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <Spinner
+ android:id="@+id/api_account_settings_encryption_algorithm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/label_hash_algorithm"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <Spinner
+ android:id="@+id/api_account_settings_hash_algorithm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/label_message_compression"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <Spinner
+ android:id="@+id/api_account_settings_compression"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml
new file mode 100644
index 000000000..d31ae52d7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml
@@ -0,0 +1,27 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:gravity="center_vertical"
+ android:singleLine="true"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="8dp"
+ android:paddingRight="4dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:id="@+id/imageView"
+ android:src="@drawable/ic_action_person" />
+
+ <TextView
+ android:id="@+id/api_accounts_adapter_item_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:text="Account Name"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml
index d83c8e87d..1377acf0e 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml
@@ -6,16 +6,28 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical">
<fragment
android:id="@+id/api_app_settings_fragment"
- android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsFragment"
+ 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" />
+ <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>
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml
index a8b68859b..96271d418 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml
@@ -2,6 +2,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -35,64 +36,13 @@
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
- <fragment
- android:id="@+id/api_app_settings_select_key_fragment"
- android:name="org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment"
+ <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- tools:layout="@layout/select_secret_key_layout_fragment" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/api_app_settings_advanced_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:text="@string/api_settings_show_advanced"
- bootstrapbutton:bb_icon_left="fa-caret-up"
- bootstrapbutton:bb_size="default"
- bootstrapbutton:bb_type="default" />
-
- <LinearLayout
- android:id="@+id/api_app_settings_advanced"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/label_encryption_algorithm"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <Spinner
- android:id="@+id/api_app_settings_encryption_algorithm"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/label_hash_algorithm"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <Spinner
- android:id="@+id/api_app_settings_hash_algorithm"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/label_message_compression"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <Spinner
- android:id="@+id/api_app_settings_compression"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="match_parent"
+ custom:foldedLabel="@string/api_settings_show_info"
+ custom:unFoldedLabel="@string/api_settings_hide_info"
+ custom:foldedIcon="fa-chevron-right"
+ custom:unFoldedIcon="fa-chevron-down">
<TextView
android:layout_width="match_parent"
@@ -119,5 +69,7 @@
android:layout_height="wrap_content"
android:text="Base64 encoded signature"
android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
+
+ </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
+
</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml
index 71fbcfb12..9f95e9f3b 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml
@@ -4,16 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <fragment
- android:id="@+id/crypto_consumers_list_fragment"
- android:name="org.sufficientlysecure.keychain.service.remote.RegisteredAppsListFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </FrameLayout>
+ <include layout="@layout/api_apps_list_content"/>
<include layout="@layout/drawer_list" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml
new file mode 100644
index 000000000..9f9b99045
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content_frame"
+ android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment
+ android:id="@+id/crypto_consumers_list_fragment"
+ android:name="org.sufficientlysecure.keychain.remote.ui.AppsListFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml
new file mode 100644
index 000000000..3aee9094f
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/api_remote_create_account_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="3dip"
+ android:text="@string/api_create_account_text"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <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"
+ tools:layout="@layout/api_app_settings_fragment" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_error_message.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_error_message.xml
index 48aa89d4f..48aa89d4f 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_app_error_message.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_remote_error_message.xml
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_register_app.xml
index aa9d59004..f85f3b8f7 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_remote_register_app.xml
@@ -20,7 +20,7 @@
<fragment
android:id="@+id/api_app_settings_fragment"
- android:name="org.sufficientlysecure.keychain.service.remote.AppSettingsFragment"
+ 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" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_select_pub_keys_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_select_pub_keys.xml
index a10592607..a10592607 100644
--- a/OpenPGP-Keychain/src/main/res/layout/api_app_select_pub_keys_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/api_remote_select_pub_keys.xml
diff --git a/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml b/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml
index a2e908433..57a1b865f 100644
--- a/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml
@@ -17,6 +17,13 @@
android:padding="4dp"
android:text="@string/key_creation_el_gamal_info" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:padding="4dp"
+ android:text="@string/key_creation_weak_rsa_info" />
+
<TableRow>
<TextView
diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
index 25c7c000c..c4709a67e 100644
--- a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml
@@ -5,206 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingTop="4dp"
- android:paddingLeft="10dp"
- android:paddingRight="10dp">
-
- <RelativeLayout
- android:id="@+id/signature"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:orientation="horizontal"
- android:padding="4dp"
- android:paddingLeft="10dp"
- android:paddingRight="10dp">
-
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/relativeLayout">
-
- <ImageView
- android:id="@+id/ic_signature"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/signed_large" />
-
- <ImageView
- android:id="@+id/ic_signature_status"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/overlay_error" />
- </RelativeLayout>
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/lookup_key"
- android:visibility="gone"
- android:layout_width="wrap_content"
- android:layout_height="50dp"
- android:padding="4dp"
- android:text="@string/btn_lookup_key"
- bootstrapbutton:bb_icon_left="fa-download"
- bootstrapbutton:bb_type="info"
- bootstrapbutton:bb_size="small"
- android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true" />
-
- <TextView
- android:id="@+id/mainUserId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:text="@string/label_main_user_id"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_toRightOf="@+id/relativeLayout" />
-
- <TextView
- android:id="@+id/mainUserIdRest"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:text="Main User Id Rest"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_below="@+id/mainUserId"
- android:layout_toRightOf="@+id/relativeLayout" />
- </RelativeLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/sourcePrevious"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_previous" />
-
- <TextView
- android:id="@+id/sourceLabel"
- style="@style/SectionHeader"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical"
- android:text="@string/label_message"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <ImageView
- android:id="@+id/sourceNext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_next" />
- </LinearLayout>
-
- <ViewFlipper
- android:id="@+id/source"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-
- <LinearLayout
- android:id="@+id/sourceMessage"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="4dp">
-
- <EditText
- android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="top"
- android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
- android:scrollHorizontally="true" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/sourceFile"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="4dp">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <EditText
- android:id="@+id/filename"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="top|left"
- android:inputType="textMultiLine|textUri"
- android:lines="4"
- android:maxLines="10"
- android:minLines="2"
- android:scrollbars="vertical" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/btn_browse"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="4dp"
- bootstrapbutton:bb_icon_left="fa-folder-open"
- bootstrapbutton:bb_roundedCorners="true"
- bootstrapbutton:bb_size="default"
- bootstrapbutton:bb_type="default" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <CheckBox
- android:id="@+id/deleteAfterDecryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_delete_after_decryption" />
- </LinearLayout>
- </LinearLayout>
- </ViewFlipper>
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:text="@string/section_decrypt_verify" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="4dp">
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/action_decrypt"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:text="@string/btn_decrypt_verify"
- bootstrapbutton:bb_icon_left="fa-unlock"
- bootstrapbutton:bb_type="info" />
- </LinearLayout>
- </LinearLayout>
- </ScrollView>
+ <include layout="@layout/decrypt_content"/>
<include layout="@layout/drawer_list" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml
new file mode 100644
index 000000000..a496d8b9d
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content_frame"
+ android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/decrypt_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v4.view.PagerTabStrip
+ android:id="@+id/decrypt_pager_tab_strip"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:background="@color/emphasis"
+ android:textColor="#fff" />
+ </android.support.v4.view.ViewPager>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_file_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_file_fragment.xml
new file mode 100644
index 000000000..633c9c832
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_file_fragment.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <include layout="@layout/decrypt_signature_include" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <EditText
+ android:id="@+id/decrypt_file_filename"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="top|left"
+ android:inputType="textMultiLine|textUri"
+ android:lines="4"
+ android:maxLines="10"
+ android:minLines="2"
+ android:scrollbars="vertical" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/decrypt_file_browse"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ bootstrapbutton:bb_icon_left="fa-folder-open"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+ </LinearLayout>
+
+ <CheckBox
+ android:id="@+id/decrypt_file_delete_after_decryption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/label_delete_after_decryption" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:text="@string/section_decrypt_verify"
+ android:layout_above="@+id/decrypt_file_action_decrypt"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/decrypt_file_action_decrypt"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:text="@string/btn_decrypt_verify"
+ bootstrapbutton:bb_icon_left="fa-unlock"
+ bootstrapbutton:bb_type="info"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ </RelativeLayout>
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_message_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_message_fragment.xml
new file mode 100644
index 000000000..dfe1bf64a
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_message_fragment.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <include layout="@layout/decrypt_signature_include" />
+
+ <EditText
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:gravity="top"
+ android:hint="@string/decrypt_content_edit_text_hint"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
+ android:scrollHorizontally="true"
+ android:layout_weight="1" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/section_decrypt_verify"
+ android:id="@+id/decrypt_message_section" />
+
+ <LinearLayout
+ android:id="@+id/decrypt_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_decrypt"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_weight="1"
+ android:text="@string/btn_decrypt_verify"
+ bootstrapbutton:bb_icon_left="fa-unlock"
+ bootstrapbutton:bb_type="info" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_decrypt_from_clipboard"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_weight="1"
+ android:text="@string/btn_decrypt_verify_clipboard"
+ bootstrapbutton:bb_icon_left="fa-clipboard"
+ bootstrapbutton:bb_type="info" />
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_signature_include.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_signature_include.xml
new file mode 100644
index 000000000..3e0d35c9b
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_signature_include.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/signature"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:orientation="horizontal"
+ android:padding="4dp"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp">
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/relativeLayout">
+
+ <ImageView
+ android:id="@+id/ic_signature"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/signed_large" />
+
+ <ImageView
+ android:id="@+id/ic_signature_status"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/overlay_error" />
+ </RelativeLayout>
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:text="@string/label_main_user_id"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:layout_toRightOf="@+id/relativeLayout" />
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="left"
+ android:text="Main User Id Rest"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_below="@+id/mainUserId"
+ android:layout_toRightOf="@+id/relativeLayout" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/lookup_key"
+ android:layout_width="wrap_content"
+ android:layout_height="50dp"
+ android:padding="4dp"
+ android:text="@string/btn_lookup_key"
+ bootstrapbutton:bb_icon_left="fa-download"
+ bootstrapbutton:bb_type="info"
+ bootstrapbutton:bb_size="small"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true" />
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml b/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml
index 81ceba20c..ab00c0073 100644
--- a/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml
@@ -9,7 +9,7 @@
-->
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/left_drawer"
- android:layout_width="240dp"
+ android:layout_width="@dimen/drawer_size"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@color/white"
diff --git a/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml b/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml
index 45b8d6511..090115d62 100644
--- a/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml
@@ -11,6 +11,7 @@
android:orientation="horizontal" >
<TableLayout
+ android:id="@+id/table_keylayout"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
@@ -88,7 +89,8 @@
bootstrapbutton:bb_type="default" />
</TableRow>
- <TableRow>
+ <TableRow
+ android:id="@+id/row_certify">
<TextView
android:id="@+id/label_usage"
@@ -97,11 +99,59 @@
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
android:text="@string/label_usage" />
+ <CheckBox
+ android:id="@+id/chkCertify"
+ android:enabled = "false"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/flag_certify" />
+ </TableRow>
- <Spinner
- android:id="@+id/usage"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <TableRow
+ android:id="@+id/row_sign">
+
+ <TextView
+ android:id="@+id/label_usage2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip"
+ android:text="@string/label_usage" />
+ <CheckBox
+ android:id="@+id/chkSign"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/flag_sign" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/row_encrypt">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip" />
+ <CheckBox
+ android:id="@+id/chkEncrypt"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/flag_encrypt" />
+ </TableRow>
+
+ <TableRow
+ android:id="@+id/row_authenticate">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:paddingRight="10dip" />
+ <CheckBox
+ android:id="@+id/chkAuthenticate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/flag_authenticate" />
</TableRow>
</TableLayout>
@@ -122,4 +172,5 @@
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
-</org.sufficientlysecure.keychain.ui.widget.KeyEditor> \ No newline at end of file
+</org.sufficientlysecure.keychain.ui.widget.KeyEditor>
+
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml
index 4fe65e341..6484c9b7b 100644
--- a/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_activity.xml
@@ -6,399 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <ScrollView
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fillViewport="true">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="10dp"
- android:paddingRight="10dp">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="4dp">
-
- <ImageView
- android:id="@+id/modePrevious"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_previous" />
-
- <TextView
- android:id="@+id/modeLabel"
- style="@style/SectionHeader"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical"
- android:text="@string/label_asymmetric" />
-
- <ImageView
- android:id="@+id/modeNext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_next" />
- </LinearLayout>
-
- <ViewFlipper
- android:id="@+id/mode"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:id="@+id/modeAsymmetric"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:padding="4dp">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <CheckBox
- android:id="@+id/sign"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_sign" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="16dp">
-
- <TextView
- android:id="@+id/mainUserId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="@string/label_sign_user_id"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <TextView
- android:id="@+id/mainUserIdRest"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="@string/label_sign_email"
- android:textAppearance="?android:attr/textAppearanceSmall" />
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingBottom="3dip">
-
- <TextView
- android:id="@+id/label_selectPublicKeys"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:text="@string/label_select_public_keys"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/btn_selectEncryptKeys"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_margin="4dp"
- android:text="@string/btn_select_encrypt_keys"
- bootstrapbutton:bb_icon_left="fa-user"
- bootstrapbutton:bb_size="default"
- bootstrapbutton:bb_type="default" />
- </LinearLayout>
- </LinearLayout>
-
- <TableLayout
- android:id="@+id/modeSymmetric"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="4dp"
- android:stretchColumns="1">
-
- <TableRow>
-
- <TextView
- android:id="@+id/label_passphrase"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_passphrase"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <EditText
- android:id="@+id/passphrase"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="textPassword" />
- </TableRow>
-
- <TableRow>
-
- <TextView
- android:id="@+id/label_passphraseAgain"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:paddingRight="10dip"
- android:text="@string/label_passphrase_again"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <EditText
- android:id="@+id/passphraseAgain"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:inputType="textPassword" />
- </TableRow>
- </TableLayout>
- </ViewFlipper>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="4dp">
-
- <ImageView
- android:id="@+id/sourcePrevious"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_previous" />
-
- <TextView
- android:id="@+id/sourceLabel"
- style="@style/SectionHeader"
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical"
- android:text="@string/label_message"
- android:textAppearance="?android:attr/textAppearanceMedium" />
-
- <ImageView
- android:id="@+id/sourceNext"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/ic_next" />
- </LinearLayout>
-
- <ViewFlipper
- android:id="@+id/source"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1">
-
- <LinearLayout
- android:id="@+id/sourceMessage"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="4dp">
-
- <EditText
- android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="top"
- android:inputType="text|textCapSentences|textMultiLine|textLongMessage" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/sourceFile"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:padding="4dp">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <EditText
- android:id="@+id/filename"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="top|left"
- android:inputType="textMultiLine|textUri"
- android:lines="4"
- android:maxLines="10"
- android:minLines="2"
- android:scrollbars="vertical" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/btn_browse"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="4dp"
- bootstrapbutton:bb_icon_left="fa-folder-open"
- bootstrapbutton:bb_roundedCorners="true"
- bootstrapbutton:bb_size="default"
- bootstrapbutton:bb_type="default" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/advancedSettingsControl"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:clickable="true">
-
- <com.beardedhen.androidbootstrap.FontAwesomeText
- android:id="@+id/advancedSettingsIcon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="10dp"
- android:textSize="12sp"
- android:paddingTop="@dimen/padding_medium"
- android:paddingBottom="@dimen/padding_medium"
- fontawesometext:fa_icon="fa-chevron-right" />
-
- <TextView
- android:id="@+id/advancedSettings"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/btn_encryption_advanced_settings_show"
- android:paddingTop="@dimen/padding_medium"
- android:paddingBottom="@dimen/padding_medium"
- android:textColor="@color/emphasis" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/fileAdvancedSettingsContainer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <TextView
- android:id="@+id/label_fileCompression"
- android:layout_width="0dip"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:paddingRight="10dip"
- android:text="@string/label_file_compression"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <Spinner
- android:id="@+id/fileCompression"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <CheckBox
- android:id="@+id/deleteAfterEncryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_delete_after_encryption" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <CheckBox
- android:id="@+id/shareAfterEncryption"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_share_after_encryption" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <CheckBox
- android:id="@+id/asciiArmour"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:text="@string/label_ascii_armor" />
- </LinearLayout>
- </LinearLayout>
- </LinearLayout>
- </ViewFlipper>
-
- <TextView
- style="@style/SectionHeader"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="4dp"
- android:text="@string/section_encrypt_and_or_sign" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:padding="4dp">
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/action_encrypt_share"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:layout_weight="1"
- android:text="@string/btn_share"
- bootstrapbutton:bb_icon_left="fa-lock"
- bootstrapbutton:bb_type="info" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/action_encrypt_clipboard"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:layout_weight="1"
- android:text="@string/btn_clipboard"
- bootstrapbutton:bb_icon_left="fa-lock"
- bootstrapbutton:bb_type="info" />
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/action_encrypt_file"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:visibility="gone"
- android:text="@string/btn_encrypt_file"
- bootstrapbutton:bb_icon_left="fa-lock"
- bootstrapbutton:bb_type="info" />
- </LinearLayout>
- </LinearLayout>
- </ScrollView>
+ <include layout="@layout/encrypt_content"/>
<include layout="@layout/drawer_list" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_asymmetric_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
new file mode 100644
index 000000000..fa1b03889
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_asymmetric_fragment.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingRight="16dp"
+ android:paddingLeft="16dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/sign"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_sign" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="4dip">
+
+ <TextView
+ android:id="@+id/mainUserId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <TextView
+ android:id="@+id/mainUserIdRest"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:text=""
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_selectPublicKeys"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:text="@string/label_select_public_keys"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/btn_selectEncryptKeys"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_margin="4dp"
+ android:text="@string/select_keys_button_default"
+ bootstrapbutton:bb_icon_left="fa-user"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml
new file mode 100644
index 000000000..e719d07e1
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content_frame"
+ android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/encrypt_pager_mode"
+ android:layout_width="match_parent"
+ android:layout_height="150dp">
+
+ <android.support.v4.view.PagerTabStrip
+ android:id="@+id/encrypt_pager_tab_strip_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:background="@color/emphasis"
+ android:textColor="#fff" />
+ </android.support.v4.view.ViewPager>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/encrypt_pager_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v4.view.PagerTabStrip
+ android:id="@+id/encrypt_pager_tab_strip_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:background="@color/emphasis"
+ android:textColor="#fff" />
+ </android.support.v4.view.ViewPager>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml
new file mode 100644
index 000000000..ac990653a
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/label_fileCompression"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:paddingRight="10dip"
+ android:text="@string/label_file_compression"
+ android:textAppearance="?android:attr/textAppearanceSmall"/>
+
+ <Spinner
+ android:id="@+id/fileCompression"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/deleteAfterEncryption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_delete_after_encryption"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/shareAfterEncryption"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_share_after_encryption"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <CheckBox
+ android:id="@+id/asciiArmor"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/label_ascii_armor"/>
+ </LinearLayout>
+</merge>
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_file_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_file_fragment.xml
new file mode 100644
index 000000000..efc4b4641
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_file_fragment.xml
@@ -0,0 +1,86 @@
+<?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"
+ xmlns:custom="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <EditText
+ android:id="@+id/filename"
+ android:layout_width="0dip"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="top|left"
+ android:inputType="textMultiLine|textUri"
+ android:lines="4"
+ android:maxLines="10"
+ android:minLines="2"
+ android:scrollbars="vertical" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/btn_browse"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ bootstrapbutton:bb_icon_left="fa-folder-open"
+ bootstrapbutton:bb_roundedCorners="true"
+ bootstrapbutton:bb_size="default"
+ bootstrapbutton:bb_type="default" />
+ </LinearLayout>
+
+ <org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ custom:foldedLabel="@string/btn_encryption_advanced_settings_show"
+ custom:unFoldedLabel="@string/btn_encryption_advanced_settings_hide"
+ custom:foldedIcon="fa-chevron-right"
+ custom:unFoldedIcon="fa-chevron-down">
+
+ <include layout="@layout/encrypt_content_adv_settings" />
+
+ </org.sufficientlysecure.keychain.ui.widget.FoldableLinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:text="@string/section_encrypt_and_or_sign"
+ android:layout_above="@+id/action_encrypt_file"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_encrypt_file"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:text="@string/btn_encrypt_file"
+ bootstrapbutton:bb_icon_left="fa-lock"
+ bootstrapbutton:bb_type="info"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ </RelativeLayout>
+ </LinearLayout>
+</ScrollView> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_message_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_message_fragment.xml
new file mode 100644
index 000000000..1fa338426
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_message_fragment.xml
@@ -0,0 +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"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fillViewport="true">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <EditText
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:gravity="top"
+ android:inputType="text|textCapSentences|textMultiLine|textLongMessage"
+ android:hint="@string/encrypt_content_edit_text_hint"
+ android:layout_weight="1" />
+
+ <TextView
+ style="@style/SectionHeader"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/section_encrypt_and_or_sign" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_encrypt_share"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_weight="1"
+ android:text="@string/btn_share"
+ bootstrapbutton:bb_icon_left="fa-share-square"
+ bootstrapbutton:bb_type="info" />
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/action_encrypt_clipboard"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:layout_weight="1"
+ android:text="@string/btn_clipboard"
+ bootstrapbutton:bb_icon_left="fa-clipboard"
+ bootstrapbutton:bb_type="info" />
+
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_symmetric_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_symmetric_fragment.xml
new file mode 100644
index 000000000..89381e499
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_symmetric_fragment.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical">
+
+ <TableLayout
+ android:id="@+id/modeSymmetric"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:stretchColumns="1"
+ android:layout_centerVertical="true">
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_passphrase"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="8dp"
+ android:text="@string/label_passphrase"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/passphrase"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:id="@+id/label_passphraseAgain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="8dp"
+ android:text="@string/label_passphrase_again"
+ android:textAppearance="?android:attr/textAppearanceMedium" />
+
+ <EditText
+ android:id="@+id/passphraseAgain"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword" />
+ </TableRow>
+ </TableLayout>
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml b/OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml
new file mode 100644
index 000000000..2b863d52b
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:fontawesometext="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:id="@+id/foldableControl"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:clickable="true">
+
+ <com.beardedhen.androidbootstrap.FontAwesomeText
+ android:id="@+id/foldableIcon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="10dp"
+ android:textSize="12sp"
+ android:paddingTop="@dimen/padding_medium"
+ android:paddingBottom="@dimen/padding_medium"
+ fontawesometext:fa_icon="fa-chevron-right"/>
+
+ <TextView
+ android:id="@+id/foldableText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/none"
+ android:paddingTop="@dimen/padding_medium"
+ android:paddingBottom="@dimen/padding_medium"
+ android:textColor="@color/emphasis"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/foldableContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone"/>
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml
index d7794ace3..c82607a33 100644
--- a/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml
@@ -1,62 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true" >
-
- <FrameLayout
- android:id="@+id/import_navigation_fragment"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:orientation="vertical"
- android:paddingLeft="4dp"
- android:paddingRight="4dp" />
-
- <LinearLayout
- android:id="@+id/import_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:orientation="vertical"
- android:paddingLeft="10dp"
- android:paddingRight="10dp" >
-
- <com.beardedhen.androidbootstrap.BootstrapButton
- android:id="@+id/import_import"
- android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:text="@string/import_import"
- bootstrapbutton:bb_icon_left="fa-download"
- bootstrapbutton:bb_type="info" />
-
- <!-- <com.beardedhen.androidbootstrap.BootstrapButton -->
- <!-- android:id="@+id/import_sign_and_upload" -->
- <!-- android:layout_width="match_parent" -->
- <!-- android:layout_height="60dp" -->
- <!-- android:padding="4dp" -->
- <!-- android:text="@string/import_sign_and_upload" -->
- <!-- bootstrapbutton:bb_type="info" /> -->
- </LinearLayout>
-
- <FrameLayout
- android:id="@+id/import_keys_list_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_above="@+id/import_footer"
- android:layout_alignParentLeft="true"
- android:layout_below="@+id/import_navigation_fragment"
- android:orientation="vertical"
- android:paddingLeft="4dp"
- android:paddingRight="4dp" />
- </RelativeLayout>
+ <include layout="@layout/import_keys_content"/>
<include layout="@layout/drawer_list" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml
new file mode 100644
index 000000000..eb1333704
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:bootstrapbutton="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/content_frame"
+ android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true">
+
+ <FrameLayout
+ android:id="@+id/import_navigation_fragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:orientation="vertical"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp" />
+
+ <LinearLayout
+ android:id="@+id/import_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp">
+
+ <com.beardedhen.androidbootstrap.BootstrapButton
+ android:id="@+id/import_import"
+ android:layout_width="match_parent"
+ android:layout_height="50dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
+ android:text="@string/import_import"
+ bootstrapbutton:bb_icon_left="fa-download"
+ bootstrapbutton:bb_type="info" />
+
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/import_keys_list_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/import_footer"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@+id/import_navigation_fragment"
+ android:orientation="vertical"
+ android:paddingLeft="4dp"
+ android:paddingRight="4dp" />
+</RelativeLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml
index 3cc0bc6dc..f5a39f115 100644
--- a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="3dip"
@@ -23,7 +23,7 @@
android:singleLine="true" >
<LinearLayout
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -52,13 +52,6 @@
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
- android:id="@+id/fingerprint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="fingerprint"
- android:textAppearance="?android:attr/textAppearanceSmall" />
-
- <TextView
android:id="@+id/mainUserIdRest"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -77,10 +70,11 @@
<TextView
android:id="@+id/keyId"
android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:text="BBBBBBBB"
+ android:layout_height="wrap_content"
+ android:text="0xBBBBBBBBBBBBBBBB"
android:textAppearance="?android:attr/textAppearanceSmall"
- android:typeface="monospace" />
+ android:typeface="monospace"
+ android:layout_weight="1" />
<TextView
android:id="@+id/algorithm"
@@ -89,6 +83,14 @@
android:textAppearance="?android:attr/textAppearanceSmall" />
<TextView
+ android:id="@+id/fingerprint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="fingerprint"
+ android:typeface="monospace"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+ <TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -99,10 +101,10 @@
<LinearLayout
android:id="@+id/list"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="36dip"
android:orientation="vertical" >
</LinearLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml
index 65d246d7b..fcb376fa8 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml
@@ -4,16 +4,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <fragment
- android:id="@+id/key_list_fragment"
- android:name="org.sufficientlysecure.keychain.ui.KeyListFragment"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- </FrameLayout>
+ <include layout="@layout/key_list_content"/>
<include layout="@layout/drawer_list" />
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_content.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_content.xml
new file mode 100644
index 000000000..e58e42961
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_content.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/content_frame"
+ android:layout_marginLeft="@dimen/drawer_content_padding"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <fragment
+ android:id="@+id/key_list_fragment"
+ android:name="org.sufficientlysecure.keychain.ui.KeyListFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</FrameLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml
index 77bd6f4e9..f2430f213 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml
@@ -11,7 +11,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone"
+ android:visibility="visible"
android:gravity="center">
<ProgressBar
@@ -51,7 +51,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:visibility="gone">
<TextView
android:layout_width="wrap_content"
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml
index bee56ddfe..0abae8bbb 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml
@@ -39,6 +39,7 @@
</LinearLayout>
<View
+ android:id="@+id/status_divider"
android:layout_width="1dip"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"
@@ -46,6 +47,7 @@
android:background="?android:attr/listDivider" />
<FrameLayout
+ android:id="@+id/status_layout"
android:layout_width="80dp"
android:layout_height="match_parent">
@@ -55,7 +57,6 @@
android:layout_height="match_parent"
android:id="@+id/edit"
android:focusable="false"
- android:visibility="visible"
android:enabled="true"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/black"
@@ -71,7 +72,6 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/revoked"
android:textColor="#e00"
- android:visibility="visible"
android:layout_gravity="center" />
</FrameLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml b/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml
index eddbe3cbf..b8897a7b3 100644
--- a/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/key_server_preference.xml
@@ -6,7 +6,7 @@
android:orientation="vertical" >
<LinearLayout
- android:id="@+android:id/text_layout"
+ android:id="@+id/text_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml
index b2bfe1700..032b9eee6 100644
--- a/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_certs_fragment.xml
@@ -42,4 +42,4 @@
</RelativeLayout>
-</ScrollView> \ No newline at end of file
+</ScrollView>
diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml
new file mode 100644
index 000000000..ef31f7690
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml
@@ -0,0 +1,38 @@
+<?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">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/mainMessage"
+ android:layout_margin="4dp"
+ android:textAppearance="?android:textAppearanceMedium" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:weightSum="1"
+ android:id="@+id/deleteSecretKeyView">
+
+ <CheckBox
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.1"
+ android:layout_margin="4dp"
+ android:id="@+id/checkDeleteSecret" />
+
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_margin="4dp"
+ android:textAppearance="?android:textAppearanceMedium"
+ android:layout_weight="0.9"
+ android:text="@string/secret_key_delete_text" />
+
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml
index 9e4bc70eb..aecedc39b 100644
--- a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml
@@ -4,8 +4,7 @@
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="8dip"
- android:paddingRight="3dip"
- android:singleLine="true" >
+ android:paddingRight="3dip" >
<ImageView
android:id="@+id/ic_masterKey"
@@ -15,31 +14,54 @@
android:paddingRight="6dip"
android:src="@drawable/key_small" />
- <TextView
- android:id="@+id/keyId"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingRight="5dip"
- android:text="@string/label_key_id"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:typeface="monospace" />
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingBottom="2dip"
+ android:paddingTop="2dip" >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:id="@+id/keyId"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="2dip"
+ android:text="@string/label_key_id"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:typeface="monospace" />
- <TextView
- android:id="@+id/keyDetails"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="(RSA, 1024bit)"
- android:textAppearance="?android:attr/textAppearanceSmall" />
+ <TextView
+ android:id="@+id/keyDetails"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingRight="5dip"
+ android:text="(RSA, 1024bit)"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ <TextView
+ android:id="@+id/keyExpiry"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right"
+ android:text="@string/label_expiry"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical"
- android:gravity="right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
+ android:gravity="right"
android:paddingBottom="2dip"
android:paddingTop="2dip" >
+ <ImageView android:id="@+id/ic_revokedKey"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/revoked_key_small"/>
<ImageView
android:id="@+id/ic_certifyKey"
android:layout_width="wrap_content"
@@ -59,4 +81,4 @@
android:src="@drawable/signed_small" />
</LinearLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml
index 6ef3f3072..aa48252ce 100644
--- a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml
+++ b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml
@@ -12,7 +12,8 @@
android:descendantFocusability="beforeDescendants"
android:orientation="vertical"
android:paddingLeft="16dp"
- android:paddingRight="16dp">
+ android:paddingRight="16dp"
+ android:id="@+id/container">
<TextView
style="@style/SectionHeader"
@@ -130,19 +131,23 @@
android:text="" />
</TableRow>
- <TableRow>
+ <TableRow
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/tableRow">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
- android:text="@string/label_creation" />
+ android:text="@string/label_fingerprint" />
<TextView
- android:id="@+id/creation"
+ android:id="@+id/fingerprint"
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="wrap_content"
+ android:typeface="monospace" />
</TableRow>
<TableRow>
@@ -152,31 +157,27 @@
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
- android:text="@string/label_expiry" />
+ android:text="@string/label_creation" />
<TextView
- android:id="@+id/expiry"
+ android:id="@+id/creation"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</TableRow>
- <TableRow
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:id="@+id/tableRow">
+ <TableRow>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingRight="10dip"
- android:text="@string/label_fingerprint" />
+ android:text="@string/label_expiry" />
<TextView
- android:id="@+id/fingerprint"
+ android:id="@+id/expiry"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:typeface="monospace" />
+ android:layout_height="wrap_content" />
</TableRow>
<TableRow>
@@ -226,16 +227,15 @@
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_actions" />
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/action_edit"
android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:layout_marginBottom="10dp"
+ android:layout_height="50dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
android:text="@string/key_view_action_edit"
bootstrapbutton:bb_icon_left="fa-key"
bootstrapbutton:bb_type="info"
@@ -244,24 +244,23 @@
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/action_encrypt"
android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:layout_marginBottom="10dp"
+ android:layout_height="50dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
android:text="@string/key_view_action_encrypt"
bootstrapbutton:bb_icon_left="fa-lock"
bootstrapbutton:bb_type="info" />
-
<com.beardedhen.androidbootstrap.BootstrapButton
android:id="@+id/action_certify"
android:layout_width="match_parent"
- android:layout_height="60dp"
- android:padding="4dp"
- android:layout_marginBottom="10dp"
+ android:layout_height="50dp"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="4dp"
android:text="@string/key_view_action_certify"
bootstrapbutton:bb_icon_left="fa-pencil"
bootstrapbutton:bb_type="info" />
</LinearLayout>
-</ScrollView> \ No newline at end of file
+</ScrollView>
diff --git a/OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml b/OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml
new file mode 100644
index 000000000..d08fc7f42
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/menu_account_settings_delete"
+ android:title="@string/api_settings_delete_account"
+ app:showAsAction="never" />
+ <item
+ android:id="@+id/menu_account_settings_cancel"
+ android:title="@string/api_settings_cancel"
+ app:showAsAction="never" />
+
+</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/menu/api_app_settings.xml b/OpenPGP-Keychain/src/main/res/menu/api_app_settings.xml
index 82ee57a73..a21db6708 100644
--- a/OpenPGP-Keychain/src/main/res/menu/api_app_settings.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/api_app_settings.xml
@@ -6,9 +6,5 @@
android:id="@+id/menu_api_settings_revoke"
android:title="@string/api_settings_revoke"
app:showAsAction="never" />
- <item
- android:id="@+id/menu_api_settings_cancel"
- android:title="@string/api_settings_cancel"
- app:showAsAction="never" />
</menu> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_edit.xml b/OpenPGP-Keychain/src/main/res/menu/key_edit.xml
index 16992affb..f9f7f8f0a 100644
--- a/OpenPGP-Keychain/src/main/res/menu/key_edit.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/key_edit.xml
@@ -3,6 +3,12 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
+ android:id="@+id/menu_key_edit_save"
+ android:icon="@drawable/ic_action_save"
+ app:showAsAction="always"
+ android:title="@string/btn_save">
+ </item>
+ <item
android:id="@+id/menu_key_edit_export_file"
app:showAsAction="never"
android:title="@string/menu_export_key" />
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list.xml b/OpenPGP-Keychain/src/main/res/menu/key_list.xml
index 10223522c..b75f4e9a6 100644
--- a/OpenPGP-Keychain/src/main/res/menu/key_list.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/key_list.xml
@@ -2,11 +2,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
- <item
- android:id="@+id/menu_key_list_import"
- app:showAsAction="ifRoom|withText"
- android:icon="@drawable/ic_action_add_person"
- android:title="@string/menu_import" />
<item
android:id="@+id/menu_key_list_search"
@@ -16,23 +11,33 @@
app:showAsAction="collapseActionView|ifRoom" />
<item
- android:id="@+id/menu_key_list_create"
- app:showAsAction="never"
- android:title="@string/menu_create_key" />
+ android:id="@+id/menu_key_list_add"
+ app:showAsAction="ifRoom|withText"
+ android:icon="@drawable/ic_action_add_person"
+ android:title="@string/menu_add_keys">
+ <menu>
+ <item
+ android:id="@+id/menu_key_list_import"
+ app:showAsAction="never"
+ android:title="@string/menu_import" />
- <item
- android:id="@+id/menu_key_list_create_expert"
- app:showAsAction="never"
- android:title="@string/menu_create_key_expert" />
+ <item
+ android:id="@+id/menu_key_list_create"
+ app:showAsAction="never"
+ android:title="@string/menu_create_key" />
- <item
- android:id="@+id/menu_key_list_export"
- app:showAsAction="never"
- android:title="@string/menu_export_keys" />
+ <item
+ android:id="@+id/menu_key_list_create_expert"
+ app:showAsAction="never"
+ android:title="@string/menu_create_key_expert" />
+ </menu>
+ </item>
<item
- android:id="@+id/menu_key_list_secret_export"
- app:showAsAction="never"
- android:title="@string/menu_export_secret_keys" />
+ android:id="@+id/menu_key_list_export"
+ app:showAsAction="ifRoom|withText"
+ android:icon="@drawable/ic_action_import_export"
+ android:title="@string/menu_export_keys">
+ </item>
</menu>
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml b/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml
index db709052f..50f83026c 100644
--- a/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/key_list_multi.xml
@@ -2,10 +2,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:id="@+id/menu_key_list_multi_select_all"
- android:icon="@drawable/ic_action_select_all"
- android:title="@string/menu_select_all" />
- <item
android:id="@+id/menu_key_list_multi_export"
android:icon="@drawable/ic_action_import_export"
android:title="@string/menu_export_key" />
@@ -17,5 +13,9 @@
android:id="@+id/menu_key_list_multi_delete"
android:icon="@drawable/ic_action_discard"
android:title="@string/menu_delete_key" />
+ <item
+ android:id="@+id/menu_key_list_multi_select_all"
+ android:icon="@drawable/ic_action_select_all"
+ android:title="@string/menu_select_all" />
</menu>
diff --git a/OpenPGP-Keychain/src/main/res/menu/key_view.xml b/OpenPGP-Keychain/src/main/res/menu/key_view.xml
index cd84cc91a..105368cbb 100644
--- a/OpenPGP-Keychain/src/main/res/menu/key_view.xml
+++ b/OpenPGP-Keychain/src/main/res/menu/key_view.xml
@@ -52,7 +52,7 @@
android:id="@+id/menu_key_keyserver"
android:icon="@drawable/ic_action_import_export"
app:showAsAction="always"
- android:title="@string/menu_share">
+ android:title="@string/menu_key_server">
<menu>
<item
android:id="@+id/menu_key_view_update"
diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html
new file mode 100644
index 000000000..99977f75d
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html
@@ -0,0 +1,49 @@
+<html>
+<head></head>
+<body>
+<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
+<p><a href="http://www.openkeychain.org">OpenKeychain</a> is an OpenPGP implementation for Android.</p>
+<p>Licence: GPLv3+</p>
+
+<h2>Developers OpenKeychain</h2>
+<ul>
+<li>Dominik Schürmann (Hlavní vývojář)</li>
+<li>Ash Hughes (crypto patches)</li>
+<li>Brian C. Barnes</li>
+<li>Bahtiar 'kalkin' Gadimov (UI)</li>
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
+</ul>
+<h2>Developers APG 1.x</h2>
+<ul>
+<li>Thialfihar (Lead developer)</li>
+<li>'Senecaso' (QRCode, sign key, upload key)</li>
+<li>Markus Doits</li>
+</ul>
+<h2>Libraries</h2>
+<ul>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Apache License v2)</li>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (MIT License)</li>
+<li>
+<a href="http://code.google.com/p/zxing/">ZXing</a> (Apache License v2)</li>
+<li>
+<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
+<li>
+<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html
new file mode 100644
index 000000000..db65b65f6
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html
@@ -0,0 +1,136 @@
+<html>
+<head></head>
+<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
+<h2>2.3</h2>
+<ul>
+<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
+<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
+<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
+<li>querying keyservers directly from the import screen</li>
+<li>fix layout and dialog style on Android 2.2-3.0</li>
+<li>fix crash on keys with empty user ids</li>
+<li>fix crash and empty lists when coming back from signing screen</li>
+<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
+<li>fix upload of key from signing screen</li>
+</ul>
+<h2>2.2</h2>
+<ul>
+<li>new design with navigation drawer</li>
+<li>new public key list design</li>
+<li>new public key view</li>
+<li>bug fixes for importing of keys</li>
+<li>key cross-certification (thanks to Ash Hughes)</li>
+<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
+<li>first version with new languages (thanks to the contributors on Transifex)</li>
+<li>sharing of keys via QR Codes fixed and improved</li>
+<li>package signature verification for API</li>
+</ul>
+<h2>2.1.1</h2>
+<ul>
+<li>API Updates, preparation for K-9 Mail integration</li>
+</ul>
+<h2>2.1</h2>
+<ul>
+<li>lots of bug fixes</li>
+<li>new API for developers</li>
+<li>PRNG bug fix by Google</li>
+</ul>
+<h2>2.0</h2>
+<ul>
+<li>complete redesign</li>
+<li>share public keys via qr codes, nfc beam</li>
+<li>sign keys</li>
+<li>upload keys to server</li>
+<li>fixes import issues</li>
+<li>new AIDL API</li>
+</ul>
+<h2>1.0.8</h2>
+<ul>
+<li>basic keyserver support</li>
+<li>app2sd</li>
+<li>more choices for pass phrase cache: 1, 2, 4, 8, hours</li>
+<li>translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)</li>
+<li>bugfixes</li>
+<li>optimizations</li>
+</ul>
+<h2>1.0.7</h2>
+<ul>
+<li>fixed problem with signature verification of texts with trailing newline</li>
+<li>more options for pass phrase cache time to live (20, 40, 60 mins)</li>
+</ul>
+<h2>1.0.6</h2>
+<ul>
+<li>account adding crash on Froyo fixed</li>
+<li>secure file deletion</li>
+<li>option to delete key file after import</li>
+<li>stream encryption/decryption (gallery, etc.)</li>
+<li>new options (language, force v3 signatures)</li>
+<li>interface changes</li>
+<li>bugfixes</li>
+</ul>
+<h2>1.0.5</h2>
+<ul>
+<li>German and Italian translation</li>
+<li>much smaller package, due to reduced BC sources</li>
+<li>new preferences GUI</li>
+<li>layout adjustment for localization</li>
+<li>signature bugfix</li>
+</ul>
+<h2>1.0.4</h2>
+<ul>
+<li>fixed another crash caused by some SDK bug with query builder</li>
+</ul>
+<h2>1.0.3</h2>
+<ul>
+<li>fixed crashes during encryption/signing and possibly key export</li>
+</ul>
+<h2>1.0.2</h2>
+<ul>
+<li>filterable key lists</li>
+<li>smarter pre-selection of encryption keys</li>
+<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
+<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
+</ul>
+<h2>1.0.1</h2>
+<ul>
+<li>GMail account listing was broken in 1.0.0, fixed again</li>
+</ul>
+<h2>1.0.0</h2>
+<ul>
+<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
+<li>support of more file managers (including ASTRO)</li>
+<li>Slovenian translation</li>
+<li>new database, much faster, less memory usage</li>
+<li>defined Intents and content provider for other apps</li>
+<li>bugfixes</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html
new file mode 100644
index 000000000..88492731c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<body>
+<h2>How to receive keys</h2>
+<ol>
+<li>Go to your partners contacts and open the contact you want to share.</li>
+<li>Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.</li>
+<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
+<li>Tap the card and the content will then load on the your device.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html
new file mode 100644
index 000000000..0e60c17a7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html
@@ -0,0 +1,19 @@
+<html>
+<head></head>
+<body>
+<h2>Getting started</h2>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+
+<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
+
+<h2>I found a bug in OpenKeychain!</h2>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
+
+<h2>Translations</h2>
+<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
+
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html
new file mode 100644
index 000000000..083e055c7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<ol>
+<li>Make sure that NFC is turned on in Settings &gt; More &gt; NFC and make sure that Android Beam is also on in the same section.</li>
+<li>Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.</li>
+<li>After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
+<li>Tap the card and the content will then load on the other person’s device.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html
index 37d4193f7..7dc0ee7d9 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Entwickler APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Leitender Entwickler)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QR-Code, Schlüssel signtieren, Schlüssel hochladen)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Bibliotheken</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache Lizenz v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Bibliothek</a> (Apache Lizenz v2)</li>
-<li>Icons von <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike Lizenz 3.0)</li>
-<li>Icons von <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html
index 1197869b5..dbf7afe76 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
@@ -8,7 +36,7 @@
<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
<li>querying keyservers directly from the import screen</li>
<li>fix layout and dialog style on Android 2.2-3.0</li>
-<li>fix crash on keys with empty user ids</li>
+<li>Absturz bei leeren Nutzer IDs behoben </li>
<li>fix crash and empty lists when coming back from signing screen</li>
<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
<li>fix upload of key from signing screen</li>
@@ -38,15 +66,15 @@
<h2>2.0</h2>
<ul>
<li>Komlett neu designd</li>
-<li>share public keys via qr codes, nfc beam</li>
+<li>Öffentliche Schlüssel teilen via QR Code, NFC Beam</li>
<li>Schlüssel signieren</li>
<li>Schlüssel auf den Server hochladen</li>
-<li>fixes import issues</li>
+<li>Importprobleme behoben</li>
<li>new AIDL API</li>
</ul>
<h2>1.0.8</h2>
<ul>
-<li>basic keyserver support</li>
+<li>Grundlegende Schlüsselserverunterstützung</li>
<li>app2sd</li>
<li>mehr Auswahlmöglichkeiten für den Passwortcache: 1, 2, 4, 8, Stunden</li>
<li>Übersetzungen: norwegisch (Danke, Sander Danielsen), chinesisch (danke, Zhang Fredrick)</li>
@@ -98,8 +126,8 @@
<h2>1.0.0</h2>
<ul>
<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
-<li>support of more file managers (including ASTRO)</li>
-<li>Slovenian translation</li>
+<li>Unterstützung von mehr Filemanagern (einschließlich ASTRO)</li>
+<li>Slowenische Übersetzung</li>
<li>Neue Datenbank, viel schneller, weniger Speicherbedarf</li>
<li>defined Intents and content provider for other apps</li>
<li>Fehlerbehebungen</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_start.html b/OpenPGP-Keychain/src/main/res/raw-de/help_start.html
index d2735f739..7a652682e 100644
--- a/OpenPGP-Keychain/src/main/res/raw-de/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-de/help_start.html
@@ -1,15 +1,15 @@
<html>
<head></head>
<body>
-<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<h2>Los gehts</h2>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
<h2>Ich habe einen Fehler in OpenKeychain gefunden!</h2>
<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
-<h2>Contribute</h2>
+<h2>Unterstützen</h2>
<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
<h2>Übersetzungen</h2>
diff --git a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html
index 863aeee58..ae7e16aae 100644
--- a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Libraries</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-el/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-el/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-el/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-el/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-el/help_start.html b/OpenPGP-Keychain/src/main/res/raw-el/help_start.html
index 3a6443a2f..0e60c17a7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-el/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-el/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html
index 863aeee58..ae7e16aae 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Libraries</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html
index 3a6443a2f..0e60c17a7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html
index 95189425d..7a4f61127 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (Parches cryptográficos)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Desarrolladores de APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Desarrollador principal)</li>
+<li>Thialfihar (Desarrollador principal)</li>
<li>'Senecaso' (Código QR, clave de firma, carga de clave)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Librerías</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Licencia Apache v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Librería Android AppMsg</a> (Licencia Apache v2)</li>
-<li>Icons de <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Compartir-Igual licencia 3.0)</li>
-<li>Iconos de <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Dominio Público)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-es/help_changelog.html
index dfb51dc81..0b3db3a30 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>corregido descifrado de mensajes/ficheros con pgp simétrico</li>
+<li>rediseñada la pantalla de edición de claves (gracias a Ash Hughes)</li>
+<li>API OpenPGP versión 3 (multiples cuentas api, correcciones internas)</li>
+<li>diseño más moderno para las pantallas de cifrado/descifrado</li>
+</ul>
+<h2>2.4</h2>
+<p>¡Gracias a todos los solicitantes de Google Summer of Code 2014, por hacer esta aplicación productiva y libre de errores!
+Además de varios parches pequeños, un notable número de correcciones fueron hechas por las siguientes personas (en orden alfabético):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>nueva lista unificada de claves</li>
+<li>huella digital de la clave coloreada</li>
+<li>compatibilidad con puertos del servidor de claves</li>
+<li>desactivar la posibilidad de generar claves débiles</li>
+<li>mucho más trabajo en el interior de la API</li>
+<li>certificar las IDs de usuario</li>
+<li>consulta al servidor de claves basadas ​​en lecturas mecánicas</li>
+<li>cerrar navigation drawer en tabletas</li>
+<li>sugerencias para emails en la creación de claves</li>
+<li>buscar en las listas de claves públicas</li>
+<li>y muchas más mejoras y correcciones...</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>corrección del fallo cuando se actualiza desde versiones anteriores</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>elimina la exportación innecesaria de claves públicas cuando se exporta la clave secreta (gracias a Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_start.html b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html
index 2907bbc99..d56399ef0 100644
--- a/OpenPGP-Keychain/src/main/res/raw-es/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Primeros pasos</h2>
-<p>Primero necesitas un par de claves personales. Crea una a través del menú "Mis claves" o importa un par de claves ya existentes a través de "Importar claves". Después, puedes descargar las claves de tus amigos o intercambiarlas a través de códigos QR o NFC.</p>
+<p>Primero necesitas un par de claves personales. Crea una a través de las opciones del menú "Contactos" o importa un par de claves ya existentes a través de "Importar claves". Después, puedes descargar las claves de tus amigos o intercambiarlas a través de códigos QR o NFC.</p>
<p>Es recomendable que instales <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> para una mejor selección de archivos y <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> para escanear los códigos QR generados. Pulsando en los enlaces se abrirá Google Play o F-Droid.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_about.html b/OpenPGP-Keychain/src/main/res/raw-et/help_about.html
new file mode 100644
index 000000000..ae7e16aae
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-et/help_about.html
@@ -0,0 +1,49 @@
+<html>
+<head></head>
+<body>
+<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
+<p><a href="http://www.openkeychain.org">OpenKeychain</a> is an OpenPGP implementation for Android.</p>
+<p>License: GPLv3+</p>
+
+<h2>Developers OpenKeychain</h2>
+<ul>
+<li>Dominik Schürmann (Lead developer)</li>
+<li>Ash Hughes (crypto patches)</li>
+<li>Brian C. Barnes</li>
+<li>Bahtiar 'kalkin' Gadimov (UI)</li>
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
+</ul>
+<h2>Developers APG 1.x</h2>
+<ul>
+<li>Thialfihar (Lead developer)</li>
+<li>'Senecaso' (QRCode, sign key, upload key)</li>
+<li>Markus Doits</li>
+</ul>
+<h2>Libraries</h2>
+<ul>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Apache License v2)</li>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (MIT License)</li>
+<li>
+<a href="http://code.google.com/p/zxing/">ZXing</a> (Apache License v2)</li>
+<li>
+<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
+<li>
+<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html
new file mode 100644
index 000000000..db65b65f6
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html
@@ -0,0 +1,136 @@
+<html>
+<head></head>
+<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
+<h2>2.3</h2>
+<ul>
+<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
+<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
+<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
+<li>querying keyservers directly from the import screen</li>
+<li>fix layout and dialog style on Android 2.2-3.0</li>
+<li>fix crash on keys with empty user ids</li>
+<li>fix crash and empty lists when coming back from signing screen</li>
+<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
+<li>fix upload of key from signing screen</li>
+</ul>
+<h2>2.2</h2>
+<ul>
+<li>new design with navigation drawer</li>
+<li>new public key list design</li>
+<li>new public key view</li>
+<li>bug fixes for importing of keys</li>
+<li>key cross-certification (thanks to Ash Hughes)</li>
+<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
+<li>first version with new languages (thanks to the contributors on Transifex)</li>
+<li>sharing of keys via QR Codes fixed and improved</li>
+<li>package signature verification for API</li>
+</ul>
+<h2>2.1.1</h2>
+<ul>
+<li>API Updates, preparation for K-9 Mail integration</li>
+</ul>
+<h2>2.1</h2>
+<ul>
+<li>lots of bug fixes</li>
+<li>new API for developers</li>
+<li>PRNG bug fix by Google</li>
+</ul>
+<h2>2.0</h2>
+<ul>
+<li>complete redesign</li>
+<li>share public keys via qr codes, nfc beam</li>
+<li>sign keys</li>
+<li>upload keys to server</li>
+<li>fixes import issues</li>
+<li>new AIDL API</li>
+</ul>
+<h2>1.0.8</h2>
+<ul>
+<li>basic keyserver support</li>
+<li>app2sd</li>
+<li>more choices for pass phrase cache: 1, 2, 4, 8, hours</li>
+<li>translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)</li>
+<li>bugfixes</li>
+<li>optimizations</li>
+</ul>
+<h2>1.0.7</h2>
+<ul>
+<li>fixed problem with signature verification of texts with trailing newline</li>
+<li>more options for pass phrase cache time to live (20, 40, 60 mins)</li>
+</ul>
+<h2>1.0.6</h2>
+<ul>
+<li>account adding crash on Froyo fixed</li>
+<li>secure file deletion</li>
+<li>option to delete key file after import</li>
+<li>stream encryption/decryption (gallery, etc.)</li>
+<li>new options (language, force v3 signatures)</li>
+<li>interface changes</li>
+<li>bugfixes</li>
+</ul>
+<h2>1.0.5</h2>
+<ul>
+<li>German and Italian translation</li>
+<li>much smaller package, due to reduced BC sources</li>
+<li>new preferences GUI</li>
+<li>layout adjustment for localization</li>
+<li>signature bugfix</li>
+</ul>
+<h2>1.0.4</h2>
+<ul>
+<li>fixed another crash caused by some SDK bug with query builder</li>
+</ul>
+<h2>1.0.3</h2>
+<ul>
+<li>fixed crashes during encryption/signing and possibly key export</li>
+</ul>
+<h2>1.0.2</h2>
+<ul>
+<li>filterable key lists</li>
+<li>smarter pre-selection of encryption keys</li>
+<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
+<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
+</ul>
+<h2>1.0.1</h2>
+<ul>
+<li>GMail account listing was broken in 1.0.0, fixed again</li>
+</ul>
+<h2>1.0.0</h2>
+<ul>
+<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
+<li>support of more file managers (including ASTRO)</li>
+<li>Slovenian translation</li>
+<li>new database, much faster, less memory usage</li>
+<li>defined Intents and content provider for other apps</li>
+<li>bugfixes</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html
new file mode 100644
index 000000000..88492731c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<body>
+<h2>How to receive keys</h2>
+<ol>
+<li>Go to your partners contacts and open the contact you want to share.</li>
+<li>Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.</li>
+<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
+<li>Tap the card and the content will then load on the your device.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_start.html b/OpenPGP-Keychain/src/main/res/raw-et/help_start.html
new file mode 100644
index 000000000..0e60c17a7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-et/help_start.html
@@ -0,0 +1,19 @@
+<html>
+<head></head>
+<body>
+<h2>Getting started</h2>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+
+<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
+
+<h2>I found a bug in OpenKeychain!</h2>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
+
+<h2>Translations</h2>
+<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
+
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html
new file mode 100644
index 000000000..083e055c7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<ol>
+<li>Make sure that NFC is turned on in Settings &gt; More &gt; NFC and make sure that Android Beam is also on in the same section.</li>
+<li>Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.</li>
+<li>After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
+<li>Tap the card and the content will then load on the other person’s device.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html
index 863aeee58..ae7e16aae 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Libraries</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html
index f8c255232..93a305796 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>شروع کار</h2>
-<p>اول شما نیاز به یک جفت کلید شخصی دارید. از طریق منوها در "کلیدهای من" بسازید و یا از طریق"واردات کلیدهای" جفت کلیدهای موجود را وارد کنید. پس از آن، شما می توانید کلید های دوستان خود را دانلود کنید و یا آنها را از طریق کدهای QR یا NFC رد و بدل کنید.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html
index 3cbbce4d5..00370c77e 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (correctif crypto)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar « kalkin » Gadimov (interface utilisateur)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Les développeurs d'APG 1.x</h2>
<ul>
-<li>« Thialfihar (développeur principal)</li>
+<li>Thialfihar (développeur principal)</li>
<li>« Senecaso » (Code QR, signer/téléverser la clef)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Bibliothèques</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Licence Apache v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Bibliothèque Android AppMsg</a> (Licence Apache v2)</li>
-<li>Icônes du <a href="http://rrze-icon-set.berlios.de/">jeu d'icônes RRZE</a> (Licence Creative Commons Paternité - Partage des Conditions Initiales à l'Identique 3.0)</li>
-<li>Icônes du <a href="http://tango.freedesktop.org/">jeu d'icônes Tango</a> (domaine public)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html
index c86c4a465..08a84c5fb 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Merci à tous les participants de « Google Summer of Code 2014 » qui ont rendu cette version riche en fonctions et sans bogue !
+À part plusieurs petits correctifs, un nombre notable de correctifs ont été apportés par les personnes suivantes (par ordre alphabétique) :
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>Nouvelle liste de clefs unifiée</li>
+<li>empreinte de clef colorée</li>
+<li>prise en charge des ports du serveur de clefs</li>
+<li>désactiver la possibilité de générer des clefs faibles</li>
+<li>bien plus de travail interne sur l'API</li>
+<li>certifier les ID des utilisateurs</li>
+<li>requête du serveur de clef basée sur une sortie lisible par la machine</li>
+<li>verrouiller le tiroir de navigation sur les tablettes</li>
+<li>suggestions de courriels à la création des clefs</li>
+<li>recherche dans les listes de clefs publiques</li>
+<li>et bien plus d'améliorations et de correctifs...</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>correctif de plantage lors de la mise à niveau des anciennes versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>supprimer l'exportation non nécessaire des clefs publiques lors de l'exportation d'une clef secrète</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html
index 0c1610f0c..ddaac44b1 100644
--- a/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Commencer</h2>
-<p>Vous avez d'abord besoin d'une paire de clefs personelles. Créez-en une avec l'option du menu « Mes clefs » ou importez des paires de clefs existantes avec « Importer des clefs ». Ensuite vous pouvez télécharger les clefs de vos amis, ou les échanger par codes QR ou NFC.</p>
+<p>Il vous faut d'abord une paire de clefs personnelles. Créez-en une avec le menu des options dans « Contacts » ou importez des paires de clefs existantes avec « Importer des clefs ». Ensuite vous pouvez télécharger les clefs de vos amis, ou les échanger par codes QR ou NFC.</p>
<p>Il vous est recommendé d'installer le <a href="market://details?id=org.openintents.filemanager">gestionnaire de fichiers OI</a> pour sa fonction améliorée de séléction des fichiers et le <a href="market://details?id=com.google.zxing.client.android">lecteur de codes à barres</a> pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html
index ba0676f3e..8644d3fc6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (Patch crittografia)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (Interfaccia Utente)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Sviluppatori APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Capo Sviluppatore)</li>
+<li>Thialfihar (Capo Sviluppatore)</li>
<li>'Senecaso' (QRCode, firma chiavi, caricamento chiavi)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Librerie</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Licenza Apache v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Licenza Apache v2)</li>
-<li>Icone da <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Licenza Creative Commons Attribution Share-Alike 3.0)</li>
-<li>Icone da <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Pubblico Dominio)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_changelog.html
index 050d2c9ef..abe268523 100644
--- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Grazie a tutti i partecipanti di Google Summer of Code 2014 che hanno reso questo rilascio ricco di caratteristiche e privo di bug!
+Oltre a numerose piccole correzioni, un notevole numero di patch sono fatte dalle seguenti persone (in ordine alfabetico):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paolo Sarbinowski, Sreeram Boyapati, Vincent Breitmoser. </p>
+<ul>
+<li>nuova lista chiave unificata</li>
+<li>impronta chiave colorata</li>
+<li>supporto per porte</li>
+<li>disattiva la possibilità di generare chiavi deboli</li>
+<li>molto più lavoro interno sulle API</li>
+<li>certificazione ID utente</li>
+<li>interrogazione keyserver basate su output leggibile a livello macchina</li>
+<li>blocco del menu di navigazione sui tablet</li>
+<li>suggerimenti per e-mail sulla creazione di chiavi</li>
+<li>ricerca nelle liste di chiavi pubbliche</li>
+<li>e molti altri miglioramenti e correzioni ...</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>correzione del crash quando si aggiorna da versioni precedenti</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>rimossa esportazione non necessaria delle chiavi pubbliche quando si esportano le chiavi private (grazie a Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html
index 4eadd82fc..0fd24178c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Per iniziare</h2>
-<p>Per prima cosa hai bisogno di un paio di chiavi personali. Creane una tramite i menu di opzione sotto 'Mie Chiavi' o importane di esistenti attraverso "Importa Chiavi". Dopodiche' puoi scaricare le chiavi dei tuoi amici o scambiarle tramite Codici QR o NFC.</p>
+<p>In primo luogo è necessario una coppia di chiavi personale. Creane una tramite l'opzione nel menu "Contatti" o importando coppie di chiavi esistenti tramite "Importa Chiavi". Successivamente, è possibile scaricare le chiavi dei vostri amici o scambiarle con i codici QR o NFC.</p>
<p>Si raccomanda di installare <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> per una migliore selezione dei file e <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> per scansionare i codici QR. I collegamenti verranno aperti in Google Play Store o F-Droid per l'installazione.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html
index 206fc9f8d..e60add867 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (暗号関係パッチ提供)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>APG 1.xの開発者達</h2>
<ul>
-<li>'Thialfihar' (主任開発者)</li>
+<li>Thialfihar (主任開発者)</li>
<li>'Senecaso' (QRコード, 鍵署名, 鍵アップロード関係)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>ライブラリ</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (パブリックドメイン)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_changelog.html
index 4878a3b55..f99656c1d 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ja/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>このリリースにおいて適用したリッチでバグのない機能を作ってくれたGoogle Summer of Code 2014の参加者たちに感謝を!
+また、以下の人達(アルファベット順)の作ってくれたいくつもののさなパッチや相当数のパッチにも:
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>新しい統合キーリスト</li>
+<li>鍵指紋のカラー化</li>
+<li>鍵サーバのポート設定のサポート</li>
+<li>弱い鍵の生成が可能だったのを無効化</li>
+<li>さらなるAPIでの内部動作</li>
+<li>ユーザーIDの検証</li>
+<li>鍵サーバへの要求をマシンリーダブル出力を基盤にした</li>
+<li>タブレットでのナビゲーションドロワーのロック</li>
+<li>鍵の生成についてメールでのサジェスト</li>
+<li>公開鍵リスト内での検索</li>
+<li>そしてさらなる改善と修正...</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>古いバージョンからのアップデートでクラッシュすることに対するホットフィックス</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>秘密鍵のエクスポート時における必要でない公開鍵のエクスポートの削除 (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html
index 9764e876a..04ad31352 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>入門</h2>
-<p>最初にあなたの個人用鍵ペアが必要になります。オプションメニューの"自分の鍵"で生成するか、"鍵のインポート"から既存の鍵ペアをインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。</p>
+<p>最初にあなたの個人用鍵ペアが必要になります。オプションメニューの"連絡先"で生成するか、"鍵のインポート"から既存の鍵ペアをインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。</p>
<p>ファイルの選択を拡張するには<a href="market://details?id=org.openintents.filemanager">OI File Manager</a>、<a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a>を生成したQRコードのスキャンのため、それぞれのインストールを必要とします。 リンクをクリックして、Google Play Store上かF-Droidからインストールしてください。</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html
index 863aeee58..ae7e16aae 100644
--- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Libraries</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html
index 3a6443a2f..0e60c17a7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html
new file mode 100644
index 000000000..a033c084a
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html
@@ -0,0 +1,49 @@
+<html>
+<head></head>
+<body>
+<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
+<p><a href="http://www.openkeychain.org">OpenKeychain</a> to implementacja OpenPGP na platformę Android.</p>
+<p>Licencja: GPLv3+</p>
+
+<h2>Deweloperzy OpenKeychain</h2>
+<ul>
+<li>Dominik Schürmann (Wiodący developer)</li>
+<li>Ash Hughes (łatki crypto)</li>
+<li>Brian C. Barnes</li>
+<li>Bahtiar 'kalkin' Gadimov (Interfejs Użytkownika)</li>
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
+</ul>
+<h2>Deweloperzy APG 1.x</h2>
+<ul>
+<li>Thialfihar (Wiodący deweloper)</li>
+<li>'Senecaso' (kody QR, podpisy kluczy, wysyłanie kluczy)</li>
+<li>Markus Doits</li>
+</ul>
+<h2>Biblioteki</h2>
+<ul>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Licencja Apache v2)</li>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Licencja Apache v2)</li>
+<li>
+<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Licencja Apache v2)</li>
+<li>
+<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (Licencja MIT)</li>
+<li>
+<a href="http://code.google.com/p/zxing/">ZXing</a> (Licencja Apache v2)</li>
+<li>
+<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (Licencja MIT X11)</li>
+<li>
+<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Licencja Apache v2)</li>
+<li>
+<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Licencja Apache v2)</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html
new file mode 100644
index 000000000..9d77a3e05
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html
@@ -0,0 +1,136 @@
+<html>
+<head></head>
+<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Podziękowania dla wszystkich kandydatów do Google Summer of Code 2014 którzy uczynili to wydanie bogatym w nowe funkcje i pozbawione błedów!
+Poza kilkoma małymi poprawkami, znaczna ilość aktualizacji została wykonana przez poniższe osoby (w kolejności alfabetycznej):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>nowa ujednolicona lista kluczy</li>
+<li>pokolowane odciski klucza</li>
+<li>obsługa portów w serwerach kluczy</li>
+<li>zablokowana możliwość generowania słabych kluczy</li>
+<li>wiele wewnętrznych prac nad API</li>
+<li>podpisywanie identyfikatorów użytkowników</li>
+<li>zapytania do serwera kluczy wykorzystują wydajniejszą komunikację maszynową</li>
+<li>zablokowany panel nawigacyjny na tabletach</li>
+<li>podpowiedzi do adresu email przy tworzeniu kluczy</li>
+<li>wyszukiwanie w liście publicznych kluczy</li>
+<li>i wiele innych usprawnień i poprawek...</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>szybka poprawka awarii aplikacji przy aktualizacji ze starszej wersji</li>
+</ul>
+<h2>2.3</h2>
+<ul>
+<li>usunięto zbędne eksportowanie kluczy publicznych przy eksportowaniu kluczy prywatnych (podziękowania dla Ash Hughes)</li>
+<li>naprawiono błąd z ustawianiem daty wygaśnięcia kluczy (podziękowania dla Ash Hugens)</li>
+<li>więcej wewnętrznych poprawek przy edytowaniu kluczy (podziękowania dla Ash Hughes)</li>
+<li>wysyłanie zapytań do serwera kluczy bezpośrednio z ekranu importu</li>
+<li>poprawiony wygląd interfejsu i okienek na Androidzie 2.2-3.0</li>
+<li>naprawiono awarię programu dla kluczy z pustym identyfikatorem użytkownika</li>
+<li>naprawiono awarię aplikacji przy powrocie z ekranu podpisywania</li>
+<li>Bouncy Castle (biblioteka kryptograficzna) zaktualizowana z wersji 1.47 do 1.50 i kompilowana ze źródeł</li>
+<li>naprawiony błąd przy wysyłaniu klucza z ekranu podpisywania</li>
+</ul>
+<h2>2.2</h2>
+<ul>
+<li>nowy wygląd z panelem nawigacji</li>
+<li>nowy wygląd listy kluczy publicznych</li>
+<li>nowy widok klucza publicznego</li>
+<li>naprawiono błędy związane z importowaniem kluczy</li>
+<li>krzyżowa certyfikacja kluczy (podziękowania dla Ash Hughes)</li>
+<li>hasła zapisane w UTF-8 są teraz prawidłowo obsługiwane (podziękowania dla Ash Hughes)</li>
+<li>pierwsza wersja z nowymi językami (podziękowania dla tłumaczy-wolontariuszy z Transifex)</li>
+<li>udostępnianie kluczy przez kody QR zostało poprawione i ulepszone</li>
+<li>weryfikacja podpisu paczki dla API</li>
+</ul>
+<h2>2.1.1</h2>
+<ul>
+<li>aktualizacje API, przygotowanie do integracji z K-9 Mail</li>
+</ul>
+<h2>2.1</h2>
+<ul>
+<li>wiele poprawek błędów</li>
+<li>nowe API dla programistów</li>
+<li>Naprawiono błąd generatora liczb losowych (PRNG), Google.</li>
+</ul>
+<h2>2.0</h2>
+<ul>
+<li>kompletna przebudowa</li>
+<li>udostępnianie kluczy publicznych przez kody QR oraz NFC</li>
+<li>możliwość podpisywania kluczem</li>
+<li>wysyłanie kluczy na serwer</li>
+<li>naprawiono problemy związane z importowaniem</li>
+<li>nowy AIDL API</li>
+</ul>
+<h2>1.0.8</h2>
+<ul>
+<li>podstawowa obsługa serwerów kluczy</li>
+<li>app2sd</li>
+<li>dodano więcej przedziałów czasowych zapamiętywania hasła: 1, 2, 4, 8 godzin</li>
+<li>tłumaczenia: norweski (podziękowania dla Sander Danielsen), chiński (podziękowania dla Zhang Fredrick)</li>
+<li>naprawione błędy</li>
+<li>usprawnienia</li>
+</ul>
+<h2>1.0.7</h2>
+<ul>
+<li>naprawiono problem z weryfikowaniem podpisu tekstów kończących się znakiem nowej linii</li>
+<li>dodano więcej przedziałów czasowych zapamiętywania hasła (20, 40, 60 minut)</li>
+</ul>
+<h2>1.0.6</h2>
+<ul>
+<li>naprawiono błąd powodujący awarię aplikacji przy dodawaniu nowego konta na Androidzie 2.2 Froyo</li>
+<li>dodano bezpieczne usuwanie plików</li>
+<li>Dodano możliwość usuwania plików kluczy po zaimportowaniu</li>
+<li>możliwość strumieniowego szyfrowania/deszyfrowania (galeria i inne)</li>
+<li>nowe opcje (języki, wymuszanie podpisów v3)</li>
+<li>zmiany w interfejsie</li>
+<li>naprawione błędy</li>
+</ul>
+<h2>1.0.5</h2>
+<ul>
+<li>tłumaczenie na niemiecki i włoski</li>
+<li>znaczne zmniejszenie rozmiaru paczki, z powodu zredukowania źródeł BC</li>
+<li>nowy interfejs graficzny Właściwości</li>
+<li>usprawnienia wyglądu dla lokalizacji</li>
+<li>naprawa błędu z podpisami</li>
+</ul>
+<h2>1.0.4</h2>
+<ul>
+<li>naprawiono kolejny błąd powodujący awarię aplikacji, spowodowany przez jakąś usterkę w SDK przy budowaniu zapytań</li>
+</ul>
+<h2>1.0.3</h2>
+<ul>
+<li>naprawiono błąd w trakcie szyfrowania/podpisywania i prawdopodobnie eksportowania klucza</li>
+</ul>
+<h2>1.0.2</h2>
+<ul>
+<li>dodano możliwość filtrowania listy kluczy</li>
+<li>sprytniejsze automatyczne wybieranie kluczy szyfrujących</li>
+<li>dodano nowy sposób obsługi intencji "wyświetl" i "wyślij", umożliwia szyfrowanie/deszyfrowanie plików wprost z menadżera plików.</li>
+<li>poprawki i dodatkowe funkcje (podpowiedź wyboru klucza) dla K-9 Mail, nowe wydanie beta dostępne</li>
+</ul>
+<h2>1.0.1</h2>
+<ul>
+<li>wyświetlanie kont w GMailu było zepsute w 1.0.0, naprawiono je ponownie</li>
+</ul>
+<h2>1.0.0</h2>
+<ul>
+<li>integracja z K-9 Mail, APG obsługuje wersję beta K-9 Mail</li>
+<li>dodano wsparcie dla większej liczby menadżerów plików (włącznie z ASTRO)</li>
+<li>tłumaczenie na słoweński</li>
+<li>Wykorzystanie nowej bazy danych, która jest znacznie szybsza i mniej pamięciożerna</li>
+<li>zdefiniowano intecję i dostawców treści dla pozostałych aplikacji</li>
+<li>naprawione błędy</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html
new file mode 100644
index 000000000..53db5e80c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<body>
+<h2>Jak odbierać klucze</h2>
+<ol>
+<li>Wejdź do listy kontaktów Twojego partnera i otwórz kontakt, który chcesz przesłać.</li>
+<li>Przytrzymaj oba urządzenia plecami do siebie (powinny się niemal dotykać) i poczujesz wibrację.</li>
+<li>Po zakończeniu wibracji zobaczysz, że zawartość urządzenia partnera zamienia się w obiekt zbliżony do wizytówki, z animacją rodem ze Star Treka w tle.</li>
+<li>Dotknij wizytówkę, a jej zawartość zostanie wysłana na Twoje urządzenie.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html
new file mode 100644
index 000000000..e88a1ad6d
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html
@@ -0,0 +1,19 @@
+<html>
+<head></head>
+<body>
+<h2>Pierwsze kroki</h2>
+<p>Po pierwsze potrzebujesz swoją osobistą parę kluczy. Stwórz ją, korzystając z odpowiedniej opcji w sekcji "Kontakty" albo zainportuj istniejącą parę korzystając z sekcji "Inportuj klucze". Następnie możesz porać klucze Twoich znajomych lub wymieniać się z nimi za pośrednictwem kodów QR lub technologii NFC.</p>
+
+<p>Zalecana jest instalacja menadżera plików <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> w celu zapewnienia wygodniejszego wyboru plików oraz programu <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a>, który jest w stanie skanować wygenerowane kody QR. Kliknięcie na powyższe linki przekieruje Cię do sklepu Google Play / F-Droid.</p>
+
+<h2>Znalazłem błąd w OpenKeychain!</h2>
+<p>Zgłoś błąd korzystając z <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">systemu śledzenia błędów OpenKeychain</a>.</p>
+
+<h2>Wkład</h2>
+<p>Jeżeli chcesz pomóc nam rozwijać OpenKeychain jako programista, <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">zapoznaj się z naszym małym poradnikiem na Githubie</a>.</p>
+
+<h2>Tłumaczenia</h2>
+<p>Pomóż przetłumaczyć OpenKeychain! Każdy może wziąć udział przez stronę <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain w serwisie Transifex</a>.</p>
+
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html
new file mode 100644
index 000000000..f17e44079
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<ol>
+<li>Upewnij się, że NFC (Near Field Communication, pol.: komunikacja bliskiego zasięgu) jest włączone. W tym celu wejdź w Ustawienia &gt; Inne &gt; NFC. Upewnij się również, że włączona jest funkcja Android Beam (znajduje się w tym samym miejscu).</li>
+<li>Przytrzymaj oba urządzenia plecami do siebie (powinny się niemal dotykać) i poczujesz wibrację.</li>
+<li>Po zakończeniu wibracji zobaczysz, że zawartość urządzenia partnera zamienia się w obiekt zbliżony do wizytówki, z animacją rodem ze Star Treka w tle.</li>
+<li>Dotknij wizytówkę, a jej zawartość zostanie wysłana na urządzenie drugiej osoby.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html
index 863aeee58..ae7e16aae 100644
--- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Libraries</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html
index 3a6443a2f..0e60c17a7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html
index 655e98758..29cc1af83 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (патчи криптографии)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Разработчики APG 1.x</h2>
<ul>
-<li>'Thialfihar' (главный разработчик)</li>
+<li>Thialfihar (главный разработчик)</li>
<li>'Senecaso' (QR коды, подписание и загрузка ключей)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Компоненты</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Библиотека Android AppMsg</a> (Apache License v2)</li>
-<li>Иконки <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Иконки <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html
index 2a324202f..0646e7dda 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Спасибо всем участникам Google Summer of Code 2014, которые помогли сделать этот выпуск, добавив функции и исправив ошибки!
+Из общего числа патчей, особенный вклад внесли следующие люди (в алфавитном порядке):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>новый объединенный список ключей</li>
+<li>цветовая индикация отпечатков ключей</li>
+<li>поддержка портов серверов ключей</li>
+<li>отключена возможность создавать слабые ключи</li>
+<li>ещё больше улучшений работы API</li>
+<li>сертификация пользовательских данных</li>
+<li>keyserver query based on machine-readable output</li>
+<li>фиксация панели на планшетах</li>
+<li>подсказки email при создании ключей</li>
+<li>поиск в списках публичных ключей</li>
+<li>и множество других исправлений и улучшений...</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>исправление ошибки при обновлении со старых версий</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>удален не требующийся экспорт публичного ключа при экспорте секретного ключа (спасибо, Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html
index 9b2b99e96..78db598ec 100644
--- a/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Приступая</h2>
-<p>Для начала вам понадобится своя пара ключей. Воспользуйтесь меню в разделе "Мои ключи", что бы создать новую, или добавьте ранее созданную пару в разделе "Импорт ключей". После этого вы сможете скачать ключи ваших друзей или обменяться ключами посредством QR кодов или NFC.</p>
+<p>Для начала вам понадобится своя пара ключей. Создайте её через меню раздела "Контакты" или импортируйте ранее созданный секретный ключ через меню "Импорт ключей". После этого вы сможете скачать ключи ваших друзей или обменяться ключами посредством QR кодов или NFC.</p>
<p>Рекомендуется установить <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> для удобного выбора файлов и <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> для распознавания QR кодов. Перейдите по ссылкам на соответствующие страницы Google Play или F-Droid для дальнейшей установки.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html
index 863aeee58..ae7e16aae 100644
--- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Libraries</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html
index 3a6443a2f..0e60c17a7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html
index eb262b242..7d2c24f9c 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html
@@ -11,13 +11,19 @@
<li>Ash Hughes (kripto yamaları)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (Arayüz)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Geliştiriciler APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Baş geliştirici)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QR Kodu, anahtar imzalama, anahtar yükleme)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
<h2>Kütüphaneler</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>İkonlar <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>İkonlar <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html
index 3a6443a2f..0e60c17a7 100644
--- a/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html
index c8a5a82c5..b51b80617 100644
--- a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html
@@ -11,13 +11,19 @@
<li>Аш Гюдж (латки шифрування)</li>
<li>Браян С. Барнс</li>
<li>Бахтіяр 'kalkin' Ґадімов (інтерфейс)</li>
-
+<li>Даніель Гаман</li>
+<li>Даніель Габ</li>
+<li>Ґреґ Вітчак</li>
+<li>Міроджін Бакші</li>
+<li>Ніхіл Петер Радж</li>
+<li>Пауль Сарбіновський</li>
+<li>Срірам Вояпаті</li>
+<li>Вінсент Брейтмозер</li>
</ul>
<h2>Розробники APG 1.x</h2>
<ul>
-<li>'Thialfihar' (основний розробник)</li>
+<li>Thialfihar (основний розробник)</li>
<li>'Senecaso' (штрих-код, підпис і завантаження ключів)</li>
-<li>Олівер Ранж</li>
<li>Маркус Дойтс</li>
</ul>
<h2>Бібліотеки</h2>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (ліцензія Apache в.2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Бібліотека Android AppMsg Library</a> (Ліцензія Apache в. 2)</li>
-<li>Піктограми із <a href="http://rrze-icon-set.berlios.de/">набору піктограм RRZE</a> (ліцензія Creative Commons - Із зазначенням авторства - Розповсюдження на тих самих умовах - версія 3.0)</li>
-<li>Піктограми із <a href="http://tango.freedesktop.org/">набору піктограм Tango</a> (відкритий домен)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html
index 0b67fa3a9..77e39d434 100644
--- a/OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>виправлено опис симетричних повідомлень/файлів pgp</li>
+<li>перероблено екран редагування ключа (завдяки Ash Hughes)</li>
+<li>OpenPGP API версія 3 (підтримка кількох профілів, внутрішні зміни)</li>
+<li>новий сучасний дизайн для екранів шифрування/розшифрування</li>
+</ul>
+<h2>2.4</h2>
+<p>Дякуємо усім заявникам Google Summer of Code 2014, які зробили цю версію багатшу на функції та вільну від помилок!
+Крім окремих незначних латок, значне число латок зробили наступні люди (у алфавітному порядку):
+Даніель Гаман, Даніель Габ, Ґреґ Вітчак, Міроджін Бакші, Ніхіл Петер Радж, Пауль Сарбіновський, Срірам Бояпаті, Вінсент Брейтмосер.</p>
+<ul>
+<li>новий єдиний перелік ключів</li>
+<li>кольоровий відбиток ключа</li>
+<li>підтримка для портів сервера ключів</li>
+<li>деактивувати можливість генерувати слабкі ключі</li>
+<li>набагато більше внутрішньої роботи на API</li>
+<li>сертифікувати ідентифікатори користувача</li>
+<li>запит сервера ключів на основі машиночитабельного виводу</li>
+<li>блокувати панель навігації на планшетах</li>
+<li>пропозиції для листів при створенні ключів</li>
+<li>пошук у списках відкритих ключів</li>
+<li>і багато інших покращень та виправлень…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>свіже виправлення збою при оновленні із старих версій</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>видалений непотрібний експорт публічного ключа при експорті секретного ключа (завдяки Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html
index 3bfb40f8a..45a3edb6a 100644
--- a/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html
@@ -2,7 +2,7 @@
<head></head>
<body>
<h2>Приступаючи до роботи</h2>
-<p>Спершу вам потрібна персональна в'язка ключів. Створіть одну через меню параметрів у "Мої Ключі" або імпортуйте наявні в'язки ключів через "Імпорт ключів". Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.</p>
+<p>Спершу вам потрібна персональна в'язка ключів. Створіть одну через меню параметрів у "Контактах" або імпортуйте наявні в'язки ключів через "Імпорт ключів". Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.</p>
<p>Рекомендуємо вам встановити <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> для поліпшеного виділення файлів та <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> для сканування згенерованих штрих-кодів. Натискання посилань відкриє Google Play або F-Droid для встановлення.</p>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html
new file mode 100644
index 000000000..ae7e16aae
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html
@@ -0,0 +1,49 @@
+<html>
+<head></head>
+<body>
+<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
+<p><a href="http://www.openkeychain.org">OpenKeychain</a> is an OpenPGP implementation for Android.</p>
+<p>License: GPLv3+</p>
+
+<h2>Developers OpenKeychain</h2>
+<ul>
+<li>Dominik Schürmann (Lead developer)</li>
+<li>Ash Hughes (crypto patches)</li>
+<li>Brian C. Barnes</li>
+<li>Bahtiar 'kalkin' Gadimov (UI)</li>
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
+</ul>
+<h2>Developers APG 1.x</h2>
+<ul>
+<li>Thialfihar (Lead developer)</li>
+<li>'Senecaso' (QRCode, sign key, upload key)</li>
+<li>Markus Doits</li>
+</ul>
+<h2>Libraries</h2>
+<ul>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Apache License v2)</li>
+<li>
+<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v7 'appcompat'</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/emilsjolander/StickyListHeaders">StickyListHeaders</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/Bearded-Hen/Android-Bootstrap">Android-Bootstrap</a> (MIT License)</li>
+<li>
+<a href="http://code.google.com/p/zxing/">ZXing</a> (Apache License v2)</li>
+<li>
+<a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
+<li>
+<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
+<li>
+<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html
new file mode 100644
index 000000000..db65b65f6
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html
@@ -0,0 +1,136 @@
+<html>
+<head></head>
+<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
+<h2>2.3</h2>
+<ul>
+<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
+<li>fix setting expiry dates on keys (thanks to Ash Hughes)</li>
+<li>more internal fixes when editing keys (thanks to Ash Hughes)</li>
+<li>querying keyservers directly from the import screen</li>
+<li>fix layout and dialog style on Android 2.2-3.0</li>
+<li>fix crash on keys with empty user ids</li>
+<li>fix crash and empty lists when coming back from signing screen</li>
+<li>Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source</li>
+<li>fix upload of key from signing screen</li>
+</ul>
+<h2>2.2</h2>
+<ul>
+<li>new design with navigation drawer</li>
+<li>new public key list design</li>
+<li>new public key view</li>
+<li>bug fixes for importing of keys</li>
+<li>key cross-certification (thanks to Ash Hughes)</li>
+<li>handle UTF-8 passwords properly (thanks to Ash Hughes)</li>
+<li>first version with new languages (thanks to the contributors on Transifex)</li>
+<li>sharing of keys via QR Codes fixed and improved</li>
+<li>package signature verification for API</li>
+</ul>
+<h2>2.1.1</h2>
+<ul>
+<li>API Updates, preparation for K-9 Mail integration</li>
+</ul>
+<h2>2.1</h2>
+<ul>
+<li>lots of bug fixes</li>
+<li>new API for developers</li>
+<li>PRNG bug fix by Google</li>
+</ul>
+<h2>2.0</h2>
+<ul>
+<li>complete redesign</li>
+<li>share public keys via qr codes, nfc beam</li>
+<li>sign keys</li>
+<li>upload keys to server</li>
+<li>fixes import issues</li>
+<li>new AIDL API</li>
+</ul>
+<h2>1.0.8</h2>
+<ul>
+<li>basic keyserver support</li>
+<li>app2sd</li>
+<li>more choices for pass phrase cache: 1, 2, 4, 8, hours</li>
+<li>translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)</li>
+<li>bugfixes</li>
+<li>optimizations</li>
+</ul>
+<h2>1.0.7</h2>
+<ul>
+<li>fixed problem with signature verification of texts with trailing newline</li>
+<li>more options for pass phrase cache time to live (20, 40, 60 mins)</li>
+</ul>
+<h2>1.0.6</h2>
+<ul>
+<li>account adding crash on Froyo fixed</li>
+<li>secure file deletion</li>
+<li>option to delete key file after import</li>
+<li>stream encryption/decryption (gallery, etc.)</li>
+<li>new options (language, force v3 signatures)</li>
+<li>interface changes</li>
+<li>bugfixes</li>
+</ul>
+<h2>1.0.5</h2>
+<ul>
+<li>German and Italian translation</li>
+<li>much smaller package, due to reduced BC sources</li>
+<li>new preferences GUI</li>
+<li>layout adjustment for localization</li>
+<li>signature bugfix</li>
+</ul>
+<h2>1.0.4</h2>
+<ul>
+<li>fixed another crash caused by some SDK bug with query builder</li>
+</ul>
+<h2>1.0.3</h2>
+<ul>
+<li>fixed crashes during encryption/signing and possibly key export</li>
+</ul>
+<h2>1.0.2</h2>
+<ul>
+<li>filterable key lists</li>
+<li>smarter pre-selection of encryption keys</li>
+<li>new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers</li>
+<li>fixes and additional features (key preselection) for K-9 Mail, new beta build available</li>
+</ul>
+<h2>1.0.1</h2>
+<ul>
+<li>GMail account listing was broken in 1.0.0, fixed again</li>
+</ul>
+<h2>1.0.0</h2>
+<ul>
+<li>K-9 Mail integration, APG supporting beta build of K-9 Mail</li>
+<li>support of more file managers (including ASTRO)</li>
+<li>Slovenian translation</li>
+<li>new database, much faster, less memory usage</li>
+<li>defined Intents and content provider for other apps</li>
+<li>bugfixes</li>
+</ul>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html
new file mode 100644
index 000000000..88492731c
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html
@@ -0,0 +1,12 @@
+<html>
+<head></head>
+<body>
+<h2>How to receive keys</h2>
+<ol>
+<li>Go to your partners contacts and open the contact you want to share.</li>
+<li>Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.</li>
+<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
+<li>Tap the card and the content will then load on the your device.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html
new file mode 100644
index 000000000..0e60c17a7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html
@@ -0,0 +1,19 @@
+<html>
+<head></head>
+<body>
+<h2>Getting started</h2>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+
+<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
+
+<h2>I found a bug in OpenKeychain!</h2>
+<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
+
+<h2>Contribute</h2>
+<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
+
+<h2>Translations</h2>
+<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
+
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html
new file mode 100644
index 000000000..083e055c7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html
@@ -0,0 +1,11 @@
+<html>
+<head></head>
+<body>
+<ol>
+<li>Make sure that NFC is turned on in Settings &gt; More &gt; NFC and make sure that Android Beam is also on in the same section.</li>
+<li>Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.</li>
+<li>After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
+<li>Tap the card and the content will then load on the other person’s device.</li>
+</ol>
+</body>
+</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html
index 863aeee58..813676ea2 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html
@@ -3,24 +3,30 @@
<body>
<p><a href="http://www.openkeychain.org">http://www.openkeychain.org</a></p>
<p><a href="http://www.openkeychain.org">OpenKeychain</a> is an OpenPGP implementation for Android.</p>
-<p>License: GPLv3+</p>
+<p>授權:GPLv3+</p>
<h2>Developers OpenKeychain</h2>
<ul>
<li>Dominik Schürmann (Lead developer)</li>
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
-<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Bahtiar 'kalkin' Gadimov (介面)</li>
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
-<h2>Libraries</h2>
+<h2>函式庫</h2>
<ul>
<li>
<a href="http://developer.android.com/tools/support-library/index.html">Android Support Library v4</a> (Apache License v2)</li>
@@ -38,8 +44,6 @@
<a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li>
<a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html
index abf660ba8..db65b65f6 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_changelog.html
@@ -1,6 +1,34 @@
<html>
<head></head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+</ul>
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html
index 88492731c..7a90a794b 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html
@@ -1,12 +1,12 @@
<html>
<head></head>
<body>
-<h2>How to receive keys</h2>
+<h2>如何接收金要</h2>
<ol>
-<li>Go to your partners contacts and open the contact you want to share.</li>
-<li>Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.</li>
-<li>After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
-<li>Tap the card and the content will then load on the your device.</li>
+<li>前往你夥伴裝置上的聯絡人清單,並點選你要分享的聯絡人。</li>
+<li>將兩部裝置背對背貼近(幾乎接觸),你會感覺到一股震動。</li>
+<li>震動之後你會看見你夥伴的畫面變成卡片狀,並且背景帶有如 Star Trek 般的特效。</li>
+<li>輕觸卡片,內容隨即顯示在你的裝置上。</li>
</ol>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html
index 3a6443a2f..22ac99882 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html
@@ -1,18 +1,18 @@
<html>
<head></head>
<body>
-<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<h2>快速上手</h2>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
-<h2>I found a bug in OpenKeychain!</h2>
-<p>Please report the bug using the <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">issue tracker of OpenKeychain</a>.</p>
+<h2>我在OpenKeychain發現問題!</h2>
+<p>請利用 <a href="https://github.com/openpgp-keychain/openpgp-keychain/issues">OpenKeychain 項目回報系統</a>回報問題。</p>
-<h2>Contribute</h2>
+<h2>發布</h2>
<p>If you want to help us developing OpenKeychain by contributing code <a href="https://github.com/openpgp-keychain/openpgp-keychain#contribute-code">follow our small guide on Github</a>.</p>
-<h2>Translations</h2>
+<h2>翻譯</h2>
<p>Help translating OpenKeychain! Everybody can participate at <a href="https://www.transifex.com/projects/p/openpgp-keychain/">OpenKeychain on Transifex</a>.</p>
</body>
diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html
index 083e055c7..99ffe4c12 100644
--- a/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html
+++ b/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html
@@ -2,10 +2,10 @@
<head></head>
<body>
<ol>
-<li>Make sure that NFC is turned on in Settings &gt; More &gt; NFC and make sure that Android Beam is also on in the same section.</li>
-<li>Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.</li>
-<li>After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.</li>
-<li>Tap the card and the content will then load on the other person’s device.</li>
+<li>確定在 "設定" &gt; "更多內容…" &gt; "NFC" 裡面已經開啟 NFC 和 Android Beam。</li>
+<li>將兩部裝置背對背貼近(幾乎接觸),你會感覺到一股震動。</li>
+<li>震動之後你會看見你夥伴的畫面變成卡片狀,並且背景帶有如 Star Trek 般的特效。</li>
+<li>輕觸卡片,內容隨即顯示在你的裝置上。</li>
</ol>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_about.html b/OpenPGP-Keychain/src/main/res/raw/help_about.html
index 51e3f1325..847168446 100644
--- a/OpenPGP-Keychain/src/main/res/raw/help_about.html
+++ b/OpenPGP-Keychain/src/main/res/raw/help_about.html
@@ -15,13 +15,20 @@ And don't add newlines before or after p tags because of transifex -->
<li>Ash Hughes (crypto patches)</li>
<li>Brian C. Barnes</li>
<li>Bahtiar 'kalkin' Gadimov (UI)</li>
-
+<li>Daniel Hammann</li>
+<li>Daniel Haß</li>
+<li>Greg Witczak</li>
+<li>Miroojin Bakshi</li>
+<li>Nikhil Peter Raj</li>
+<li>Paul Sarbinowski</li>
+<li>Sreeram Boyapati</li>
+<li>Vincent Breitmoser</li>
</ul>
+
<h2>Developers APG 1.x</h2>
<ul>
-<li>'Thialfihar' (Lead developer)</li>
+<li>Thialfihar (Lead developer)</li>
<li>'Senecaso' (QRCode, sign key, upload key)</li>
-<li>Oliver Runge</li>
<li>Markus Doits</li>
</ul>
@@ -35,8 +42,6 @@ And don't add newlines before or after p tags because of transifex -->
<li><a href="http://rtyley.github.com/spongycastle/">SpongyCastle</a> (MIT X11 License)</li>
<li><a href="https://github.com/dschuermann/html-textview">HtmlTextView</a> (Apache License v2)</li>
<li><a href="https://github.com/johnkil/Android-AppMsg">Android AppMsg Library</a> (Apache License v2)</li>
-<li>Icons from <a href="http://rrze-icon-set.berlios.de/">RRZE Icon Set</a> (Creative Commons Attribution Share-Alike licence 3.0)</li>
-<li>Icons from <a href="http://tango.freedesktop.org/">Tango Icon Set</a> (Public Domain)</li>
</ul>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw/help_changelog.html
index 17ad853de..64a91e5f1 100644
--- a/OpenPGP-Keychain/src/main/res/raw/help_changelog.html
+++ b/OpenPGP-Keychain/src/main/res/raw/help_changelog.html
@@ -5,6 +5,37 @@ And don't add newlines before or after p tags because of transifex -->
<head>
</head>
<body>
+<h2>2.5</h2>
+<ul>
+<li>fix decryption of symmetric pgp messages/files</li>
+<li>refactored edit key screen (thanks to Ash Hughes)</li>
+<li>new modern design for encrypt/decrypt screens</li>
+<li>OpenPGP API version 3 (multiple api accounts, internal fixes, key lookup)</li>
+</ul>
+
+<h2>2.4</h2>
+<p>Thanks to all applicants of Google Summer of Code 2014 who made this release feature rich and bug free!
+Besides several small patches, a notable number of patches are made by the following people (in alphabetical order):
+Daniel Hammann, Daniel Haß, Greg Witczak, Miroojin Bakshi, Nikhil Peter Raj, Paul Sarbinowski, Sreeram Boyapati, Vincent Breitmoser.</p>
+<ul>
+<li>new unified key list</li>
+<li>colorized key fingerprint</li>
+<li>support for keyserver ports</li>
+<li>deactivate possibility to generate weak keys</li>
+<li>much more internal work on the API</li>
+<li>certify user ids</li>
+<li>keyserver query based on machine-readable output</li>
+<li>lock navigation drawer on tablets</li>
+<li>suggestions for emails on creation of keys</li>
+<li>search in public key lists</li>
+<li>and much more improvements and fixes…</li>
+</ul>
+
+<h2>2.3.1</h2>
+<ul>
+<li>hotfix for crash when upgrading from old versions</li>
+</ul>
+
<h2>2.3</h2>
<ul>
<li>remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)</li>
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_faq.html b/OpenPGP-Keychain/src/main/res/raw/help_faq.html
index b3d5b3a11..bfd43eafd 100644
--- a/OpenPGP-Keychain/src/main/res/raw/help_faq.html
+++ b/OpenPGP-Keychain/src/main/res/raw/help_faq.html
@@ -5,8 +5,9 @@ And don't add newlines before or after p tags because of transifex -->
<head>
</head>
<body>
-<h2>TODO</h2>
-<p>text</p>
+<h2>How can I specify connection port for Keyserver?</h2>
+<p>Add a new Keyserver (or modify existing one) by going to Preferences -> General -> Keyservers. Enter the port number after the Keyserver address and preceded it by a colon. For example, "p80.pool.sks-keyservers.net:80" (without quotation marks) means that server "p80.pool.sks-keyservers.net" is working on a port 80.</p>
+<p>Default connection port is 11371 and it doesn't need to be specified.</p>
</body>
</html>
diff --git a/OpenPGP-Keychain/src/main/res/raw/help_start.html b/OpenPGP-Keychain/src/main/res/raw/help_start.html
index 7afac0f08..56c02b1fd 100644
--- a/OpenPGP-Keychain/src/main/res/raw/help_start.html
+++ b/OpenPGP-Keychain/src/main/res/raw/help_start.html
@@ -6,7 +6,7 @@ And don't add newlines before or after p tags because of transifex -->
</head>
<body>
<h2>Getting started</h2>
-<p>First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
+<p>First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.</p>
<p>It is recommended that you install <a href="market://details?id=org.openintents.filemanager">OI File Manager</a> for enhanced file selection and <a href="market://details?id=com.google.zxing.client.android">Barcode Scanner</a> to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.</p>
diff --git a/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml b/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml
new file mode 100644
index 000000000..a2108f652
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml
@@ -0,0 +1,50 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <string name="title_manage_public_keys">Kontakty</string>
+ <string name="title_manage_secret_keys">Tajné klíče</string>
+ <string name="title_select_recipients">Zvolit veřejný klíč</string>
+ <string name="title_select_secret_key">Zvolit tajný klíč</string>
+ <string name="title_encrypt">Zašifrovat</string>
+ <string name="title_decrypt">Dešifrovat</string>
+ <string name="title_authentication">Heslo</string>
+ <string name="title_create_key">Vytvořit klíč</string>
+ <string name="title_edit_key">Upravit klíč</string>
+ <string name="title_preferences">Nastavení</string>
+ <string name="title_api_registered_apps">Registrované aplikace</string>
+ <string name="title_key_server_preference">Nastavení serveru s klíči</string>
+ <string name="title_set_passphrase">Zadat heslo</string>
+ <string name="title_send_email">Poslat zprávu...</string>
+ <string name="title_import_keys">Importovat klíče</string>
+ <string name="title_export_key">Exportovat klíč</string>
+ <string name="title_export_keys">Exportovat klíče</string>
+ <string name="title_key_not_found">Klíč nenalezen</string>
+ <string name="title_send_key">Nahrát na server s klíči</string>
+ <string name="title_help">Nápověda</string>
+ <!--section-->
+ <string name="section_keys">Klíče</string>
+ <!--button-->
+ <string name="btn_sign">Podepsat</string>
+ <!--menu-->
+ <!--label-->
+ <string name="unknown_status"></string>
+ <!--choice-->
+ <!--key flags-->
+ <!--sentences-->
+ <!--errors
+ no punctuation, all lowercase,
+ they will be put after "error_message", e.g. "Error: file not found"-->
+ <!--progress dialogs, usually ending in '…'-->
+ <!--action strings-->
+ <!--key bit length selections-->
+ <!--compression-->
+ <!--Help-->
+ <!--Import-->
+ <!--Intent labels-->
+ <!--Remote API-->
+ <!--Share-->
+ <!--Key list-->
+ <!--Key view-->
+ <!--Navigation Drawer-->
+ <!--hints-->
+</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-de/strings.xml b/OpenPGP-Keychain/src/main/res/values-de/strings.xml
index 1e0fda3bc..c0ff7345e 100644
--- a/OpenPGP-Keychain/src/main/res/values-de/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-de/strings.xml
@@ -13,8 +13,10 @@
<string name="title_preferences">Einstellungen</string>
<string name="title_api_registered_apps">Registrierte Anwendungen</string>
<string name="title_key_server_preference">Schlüsselserver</string>
+ <string name="title_change_passphrase">Passphrase ändern</string>
<string name="title_set_passphrase">Passwort setzen</string>
<string name="title_send_email">E-Mail senden...</string>
+ <string name="title_send_file">Datei senden</string>
<string name="title_encrypt_to_file">In eine Datei verschlüsseln</string>
<string name="title_decrypt_to_file">In eine Datei entschlüsseln</string>
<string name="title_import_keys">Schlüssel importieren</string>
@@ -53,8 +55,6 @@
<string name="btn_delete">Löschen</string>
<string name="btn_no_date">Keine</string>
<string name="btn_okay">Okay</string>
- <string name="btn_change_passphrase">Passwort ändern</string>
- <string name="btn_set_passphrase">Passwort setzen</string>
<string name="btn_search">Suchen</string>
<string name="btn_export_to_server">Auf Schlüsselserver hochladen</string>
<string name="btn_next">Weiter</string>
@@ -62,6 +62,8 @@
<string name="btn_clipboard">Zwischenablage</string>
<string name="btn_share">Teilen mit…</string>
<string name="btn_lookup_key">Schlüssel nachschlagen</string>
+ <string name="btn_encryption_advanced_settings_show">Erweiterte Einstellungen anzeigen</string>
+ <string name="btn_encryption_advanced_settings_hide">Erweiterte Einstellungen verbergen</string>
<!--menu-->
<string name="menu_preferences">Einstellungen</string>
<string name="menu_help">Hilfe</string>
@@ -69,16 +71,17 @@
<string name="menu_import_from_qr_code">QR-Code</string>
<string name="menu_import">Importieren</string>
<string name="menu_import_from_nfc">NFC</string>
- <string name="menu_export_keys">Alle Schlüssel exportieren</string>
+ <string name="menu_export_secret_keys">Alle geheimen Schlüssel exportieren</string>
<string name="menu_export_key">In Datei exportieren</string>
<string name="menu_delete_key">Schlüssel löschen</string>
<string name="menu_create_key">Schlüssel erstellen</string>
<string name="menu_create_key_expert">Schlüssel erstellen (Experte)</string>
<string name="menu_search">Suchen</string>
- <string name="menu_key_server">Schlüsselserver</string>
+ <string name="menu_import_from_key_server">Schlüsselserver</string>
+ <string name="menu_key_server">Schlüsselserver…</string>
<string name="menu_update_key">Von einem Schlüsselserver aktualisieren</string>
<string name="menu_export_key_to_server">Auf Schlüsselserver hochladen</string>
- <string name="menu_share">Teilen</string>
+ <string name="menu_share">Teilen…</string>
<string name="menu_share_title_fingerprint">Teile Fingerabdruck…</string>
<string name="menu_share_title">Teile gesamten Schlüssel…</string>
<string name="menu_share_default_fingerprint">mit…</string>
@@ -91,6 +94,7 @@
<string name="menu_beam_preferences">Beam-Einstellungen</string>
<string name="menu_key_edit_cancel">Abbrechen</string>
<string name="menu_encrypt_to">Verschlüsseln nach…</string>
+ <string name="menu_select_all">Alles auswählen</string>
<!--label-->
<string name="label_sign">Signieren</string>
<string name="label_message">Nachricht</string>
@@ -103,6 +107,7 @@
<string name="label_select_public_keys">Empfänger</string>
<string name="label_delete_after_encryption">Nach Verschlüsselung löschen</string>
<string name="label_delete_after_decryption">Nach Entschlüsselung löschen</string>
+ <string name="label_share_after_encryption">Nach dem Verschlüsseln teilen</string>
<string name="label_encryption_algorithm">Verschlüsselungsalgorithmus</string>
<string name="label_hash_algorithm">Hash-Algorithmus</string>
<string name="label_asymmetric">Öffentlicher Schlüssel</string>
@@ -124,6 +129,7 @@
<string name="label_send_key">Schlüssel nach Beglaubigung auf ausgewählten Schlüsselserver hochladen</string>
<string name="label_fingerprint">Fingerabdruck</string>
<string name="select_keys_button_default">Auswählen</string>
+ <string name="expiry_date_dialog_title">Ablaufdatum festsetzen</string>
<plurals name="select_keys_button">
<item quantity="one">%d ausgewählt</item>
<item quantity="other">%d ausgewählt</item>
@@ -131,11 +137,17 @@
<string name="user_id_no_name">&lt;kein Name&gt;</string>
<string name="none">&lt;keine&gt;</string>
<string name="no_key">&lt;kein Schlüssel&gt;</string>
+ <string name="no_email">&lt;Keine E-Mail&gt;</string>
<string name="unknown_status"></string>
<string name="can_encrypt">kann verschlüsseln</string>
<string name="can_sign">kann signieren</string>
<string name="expired">abgelaufen</string>
<string name="revoked">zurückgezogen</string>
+ <string name="user_id">Benutzer ID</string>
+ <plurals name="n_contacts">
+ <item quantity="one">1 Kontakt</item>
+ <item quantity="other">%d Kontakte</item>
+ </plurals>
<plurals name="n_key_servers">
<item quantity="one">%d Schlüsselserver</item>
<item quantity="other">%d Schlüsselserver</item>
@@ -144,9 +156,6 @@
<string name="secret_key">Privater Schlüssel:</string>
<!--choice-->
<string name="choice_none">Keine</string>
- <string name="choice_sign_only">Nur Signieren</string>
- <string name="choice_encrypt_only">Nur Verschlüsseln</string>
- <string name="choice_sign_and_encrypt">Signieren und Verschlüsseln</string>
<string name="choice_15secs">15 s</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 min</string>
@@ -166,21 +175,18 @@
<string name="warning">Warnung</string>
<string name="error">Fehler</string>
<string name="error_message">Fehler: %s</string>
+ <!--key flags-->
<!--sentences-->
<string name="wrong_passphrase">Falsches Passwort.</string>
<string name="using_clipboard_content">Verwende Inhalt der Zwischenablage.</string>
<string name="set_a_passphrase">Zuerst ein Passwort setzen.</string>
<string name="no_filemanager_installed">Kein passender Dateimanager installiert.</string>
<string name="passphrases_do_not_match">Die Passwörter stimmten nicht überein.</string>
- <string name="passphrase_must_not_be_empty">Leere Passwörter sind nicht erlaubt.</string>
<string name="passphrase_for_symmetric_encryption">Symmetrische Verschlüsselung.</string>
<string name="passphrase_for">Passwort für \'%s\' eingeben</string>
<string name="file_delete_confirmation">%s\nwirklich löschen?</string>
<string name="file_delete_successful">Erfolgreich gelöscht.</string>
<string name="no_file_selected">Zuerst eine Datei auswählen.</string>
- <string name="decryption_successful">Erfolgreich entschlüsselt.</string>
- <string name="encryption_successful">Erfolgreich verschlüsselt.</string>
- <string name="encryption_to_clipboard_successful">Erfolgreich in die Zwischenablage verschlüsselt.</string>
<string name="enter_passphrase_twice">Passwort zweimal eingeben.</string>
<string name="select_encryption_key">Mindestens einen Schlüssel zum verschlüsseln auswählen.</string>
<string name="select_encryption_or_signature_key">Mindestens einen Schlüssel zum Verschlüsseln oder einen zum Signieren auswählen.</string>
@@ -191,6 +197,7 @@
<string name="key_deletion_confirmation">Soll der Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden! </string>
<string name="key_deletion_confirmation_multi">Möchtest du wirklich alle ausgewählten Schlüssel löschen?\nDies kann nicht rückgängig gemacht werden!</string>
<string name="secret_key_deletion_confirmation">Soll der PRIVATE Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden!</string>
+ <string name="also_export_secret_keys">Private Schlüssel auch exportieren</string>
<plurals name="keys_added_and_updated_1">
<item quantity="one">%d Schlüssel erfolgreich hinzugefügt</item>
<item quantity="other">%d Schlüssel erfolgreich hinzugefügt</item>
@@ -212,6 +219,7 @@
<string name="keys_exported">%d Schlüssel erfolgreich exportiert.</string>
<string name="no_keys_exported">Keine Schlüssel exportiert.</string>
<string name="key_creation_el_gamal_info">Beachte: nur Unterschlüssel unterstützen ElGamal. Für ElGamal wird die am nächsten liegende Schlüssellänge von 1536, 2048, 3072, 4096 oder 8192 verwendet.</string>
+ <string name="key_creation_weak_rsa_info">Beachte: RSA-Schlüssel mit einer Schlüssellänge von 1024-Bits oder weniger werden als unsicher angesehen und können daher nicht für neue Schlüssel erstellt werden.</string>
<string name="key_not_found">Schlüssel %08X konnte nicht gefunden werden.</string>
<plurals name="keys_found">
<item quantity="one">%d Schlüssel gefunden.</item>
@@ -243,6 +251,7 @@
<string name="error_master_key_must_not_be_el_gamal">Der Hauptschlüssel kann kein ElGamal Schlüssel sein</string>
<string name="error_unknown_algorithm_choice">Unbekannte Auswahl für Algorithmus</string>
<string name="error_user_id_needs_a_name">ein Name muss angegeben werden</string>
+ <string name="error_user_id_no_email">keine E-Mail gefunden</string>
<string name="error_user_id_needs_an_email_address">eine E-Mail-Adresse muss angegeben werden</string>
<string name="error_key_needs_a_user_id">Mindestens eine Benutzer-ID wird benötigt</string>
<string name="error_main_user_id_must_not_be_empty">Hauptbenutzer-ID darf nicht leer sein</string>
@@ -265,48 +274,59 @@
<string name="error_expiry_must_come_after_creation">Ablaufdatum muss später sein als das Erstellungsdatum</string>
<string name="error_can_not_delete_contact">Sie können diesen Kontakt nicht löschen, denn es ist ihr eigener.</string>
<string name="error_can_not_delete_contacts">Sie können folgende Kontakte nicht löschen, denn sie gehören Ihnen selbst:\n%s</string>
+ <string name="error_keyserver_insufficient_query">Unzureichende Serveranfrage</string>
<string name="error_keyserver_query">Keyserveranfrage fehlgeschlagen</string>
+ <string name="error_keyserver_too_many_responses">Zu viele Antworten</string>
+ <string name="error_import_file_no_content">Datei ist leer</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_can_not_delete_info">
<item quantity="one">Bitte lösche ihn unter \'Meine Schlüssel\'!</item>
<item quantity="other">Bitte lösche sie unter \'Meine Schlüssel\'!</item>
</plurals>
+ <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>
+ <item quantity="other">Teile der geladenen Dateien sind gültige OpenPGP Objekte aber keine OpenPGP Schlüssel</item>
+ </plurals>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">fertig.</string>
+ <string name="progress_done">Erledigt</string>
<string name="progress_cancel">Abbrechen</string>
- <string name="progress_saving">speichern...</string>
- <string name="progress_importing">importieren...</string>
- <string name="progress_exporting">exportieren...</string>
- <string name="progress_generating">erstelle Schlüssel, dies kann bis zu 3 Minuten dauern...</string>
- <string name="progress_building_key">erstelle Schlüssel...</string>
- <string name="progress_preparing_master_key">Hauptschlüssel wird vorbereitet...</string>
- <string name="progress_certifying_master_key">Hauptschlüssel wird beglaubigt...</string>
- <string name="progress_building_master_key">erstelle Hauptring...</string>
- <string name="progress_adding_sub_keys">füge Unterschlüssel hinzu...</string>
- <string name="progress_saving_key_ring">Schlüssel wird gespeichert...</string>
+ <string name="progress_saving">speichern…</string>
+ <string name="progress_importing">importieren…</string>
+ <string name="progress_exporting">exportieren…</string>
+ <string name="progress_building_key">erstelle Schlüssel…</string>
+ <string name="progress_preparing_master_key">Hauptschlüssel wird vorbereitet…</string>
+ <string name="progress_certifying_master_key">Hauptschlüssel wird beglaubigt…</string>
+ <string name="progress_building_master_key">erstelle Hauptring…</string>
+ <string name="progress_adding_sub_keys">füge Unterschlüssel hinzu…</string>
+ <string name="progress_saving_key_ring">Schlüssel wird gespeichert…</string>
<plurals name="progress_exporting_key">
<item quantity="one">Schlüssel wird exportiert…</item>
<item quantity="other">Schlüssel werden exportiert…</item>
</plurals>
- <string name="progress_extracting_signature_key">extrahiere Signaturschlüssel...</string>
- <string name="progress_extracting_key">extrahiere Schlüssel...</string>
- <string name="progress_preparing_streams">Datenstrom wird vorbereitet...</string>
- <string name="progress_encrypting">Daten werden verschlüsselt...</string>
- <string name="progress_decrypting">Daten werden entschlüsselt...</string>
- <string name="progress_preparing_signature">Signatur wird vorbereitet...</string>
- <string name="progress_generating_signature">Signatur wird erstellt...</string>
- <string name="progress_processing_signature">Signatur wird verarbeitet...</string>
- <string name="progress_verifying_signature">Signatur wird verifiziert...</string>
- <string name="progress_signing">signiere...</string>
- <string name="progress_reading_data">Daten werden gelesen...</string>
- <string name="progress_finding_key">Schlüssel wird gesucht...</string>
- <string name="progress_decompressing_data">Daten werden entpackt...</string>
- <string name="progress_verifying_integrity">Integrität wird überprüft...</string>
- <string name="progress_deleting_securely">\'%s\' wird sicher gelöscht...</string>
- <string name="progress_querying">Anfrage wird gestellt...</string>
+ <plurals name="progress_generating">
+ <item quantity="one">erstelle Schlüssel, das kann bis zu 3 Minuten dauern…</item>
+ <item quantity="other">erstelle Schlüssel, das kann bis zu 3 Minuten dauern…</item>
+ </plurals>
+ <string name="progress_extracting_signature_key">extrahiere Signaturschlüssel…</string>
+ <string name="progress_extracting_key">extrahiere Schlüssel…</string>
+ <string name="progress_preparing_streams">Datenstrom wird vorbereitet…</string>
+ <string name="progress_encrypting">Daten werden verschlüsselt…</string>
+ <string name="progress_decrypting">Daten werden entschlüsselt…</string>
+ <string name="progress_preparing_signature">Signatur wird vorbereitet…</string>
+ <string name="progress_generating_signature">Signatur wird erstellt…</string>
+ <string name="progress_processing_signature">Signatur wird verarbeitet…</string>
+ <string name="progress_verifying_signature">Signatur wird verifiziert…</string>
+ <string name="progress_signing">signiere…</string>
+ <string name="progress_reading_data">Daten werden gelesen…</string>
+ <string name="progress_finding_key">Schlüssel wird gesucht…</string>
+ <string name="progress_decompressing_data">Daten werden entpackt…</string>
+ <string name="progress_verifying_integrity">Integrität wird überprüft…</string>
+ <string name="progress_deleting_securely">\'%s\' wird sicher gelöscht…</string>
+ <string name="progress_querying">Anfrage wird gestellt…</string>
<!--action strings-->
<string name="hint_public_keys">Öffentliche Schlüssel suchen</string>
<string name="hint_secret_keys">Private Schlüssel suchen</string>
- <string name="action_share_key_with">Teile Schlüssel über...</string>
+ <string name="action_share_key_with">Teile Schlüssel über…</string>
<!--key bit length selections-->
<string name="key_size_512">512</string>
<string name="key_size_1024">1024</string>
@@ -317,6 +337,7 @@
<string name="compression_very_slow">sehr langsam</string>
<!--Help-->
<string name="help_tab_start">Start</string>
+ <string name="help_tab_faq">FAQ</string>
<string name="help_tab_nfc_beam">NFC-Beam</string>
<string name="help_tab_changelog">Changelog</string>
<string name="help_tab_about">Über</string>
@@ -338,6 +359,10 @@
<string name="import_nfc_help_button">Hilfe</string>
<string name="import_clipboard_button">Füge den Schlüssel aus der Zwischenablage ein</string>
<!--Intent labels-->
+ <string name="intent_decrypt_file">Datei mit OpenKeychain entschlüsseln</string>
+ <string name="intent_import_key">Schlüssel mit OpenKeychain importieren</string>
+ <string name="intent_send_encrypt">Mit OpenKeychain verschlüsseln</string>
+ <string name="intent_send_decrypt">Mit OpenKeychain entschlüsseln</string>
<!--Remote API-->
<string name="api_no_apps">Keine registrierten Anwendungen vorhanden!\n\nAnwendungen von Dritten können Zugriff auf OpenKeychain erbitten. Nachdem Zugriff gewährt wurde, werden diese hier aufgelistet.</string>
<string name="api_settings_show_advanced">Erweiterte Einstellungen anzeigen</string>
@@ -356,7 +381,7 @@
<string name="api_select_pub_keys_missing_text">Für diese Benutzer-IDs wurden keine öffentlichen Schlüssel gefunden:</string>
<string name="api_select_pub_keys_dublicates_text">Für diese Benutzer-IDs existieren mehrere öffentliche Schlüssel:</string>
<string name="api_select_pub_keys_text">Bitte die Liste der Empfänger überprüfen!</string>
- <string name="api_error_wrong_signature">Signaturüberprüfung fehlgeschlagen! Haben Sie diese App von einer anderen Quelle installiert? Wenn Sie eine Attacke ausschließen können, sollten Sie die Registrierung der App in OpenKeychain widerrufen und die App erneut registrieren.</string>
+ <string name="api_error_wrong_signature">Signaturüberprüfung fehlgeschlagen! Haben Sie diese App von einer anderen Quelle installiert? Wenn Sie eine Attacke ausschliessen können, sollten Sie die Registrierung der App in OpenKeychain widerrufen und die App erneut registrieren.</string>
<!--Share-->
<string name="share_qr_code_dialog_title">Über QR Code teilen</string>
<string name="share_qr_code_dialog_start">Mit \'Weiter\' durch alle QR-Codes gehen und diese nacheinander scannen.</string>
@@ -374,17 +399,23 @@
<string name="key_list_empty_button_create">deinen eigenen Schlüssel erstellst</string>
<string name="key_list_empty_button_import">existierende Schlüssel importierst.</string>
<!--Key view-->
+ <string name="key_view_action_edit">Diesen Schlüssel bearbeiten</string>
<string name="key_view_action_encrypt">Für diesen Kontakt verschlüsseln</string>
<string name="key_view_action_certify">Schlüssel dieses Kontakts beglaubigen</string>
<string name="key_view_tab_main">Info</string>
<string name="key_view_tab_certs">Zertifikationen</string>
<!--Navigation Drawer-->
- <string name="nav_contacts">Kontakte</string>
- <string name="nav_encrypt">Verschlüsseln</string>
- <string name="nav_decrypt">Entschlüsseln</string>
<string name="nav_import">Schlüssel Importieren</string>
<string name="nav_secret_keys">Meine Schlüssel</string>
<string name="nav_apps">Registrierte Anwendungen</string>
<string name="drawer_open">Menu öffnen</string>
<string name="drawer_close">Menu schließen</string>
+ <string name="edit">Bearbeiten</string>
+ <string name="my_keys">Meine Schlüssel</string>
+ <string name="label_secret_key">Geheime Schlüssel</string>
+ <string name="secret_key_yes">verfügbar</string>
+ <string name="secret_key_no">nicht verfügbar</string>
+ <string name="section_uids_to_sign">Benutzer-IDs, die beglaubigt werden sollen</string>
+ <string name="progress_re_adding_certs">Wiederhinzufügen der Zertifikate</string>
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-el/strings.xml b/OpenPGP-Keychain/src/main/res/values-el/strings.xml
index 84b39c221..824399722 100644
--- a/OpenPGP-Keychain/src/main/res/values-el/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-el/strings.xml
@@ -15,8 +15,6 @@
<string name="btn_delete">Διαγραφή</string>
<string name="btn_no_date">Κανένα</string>
<string name="btn_okay">ΟΚ</string>
- <string name="btn_change_passphrase">Αλλαγή κωδικού</string>
- <string name="btn_set_passphrase">Επέλεξε Κωδικό</string>
<!--menu-->
<string name="menu_delete_key">Διαγραφής κλειδιού</string>
<string name="menu_create_key">Δημιουργίας κλειδιού</string>
@@ -34,6 +32,7 @@
<string name="label_email">Ηλεκτρονικό ταχυδρομίο</string>
<string name="unknown_status"></string>
<!--choice-->
+ <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
@@ -50,4 +49,5 @@
<!--Key list-->
<!--Key view-->
<!--Navigation Drawer-->
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml b/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml
index 41dc629aa..11d84cdee 100644
--- a/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml
@@ -36,8 +36,6 @@
<string name="btn_delete">Borrar</string>
<string name="btn_no_date">Ninguno</string>
<string name="btn_okay">Ok</string>
- <string name="btn_change_passphrase">Cambiar contraseña</string>
- <string name="btn_set_passphrase">Establecer contraseña</string>
<string name="btn_search">Buscar</string>
<string name="btn_next">Siguiente</string>
<string name="btn_back">Atrás</string>
@@ -46,7 +44,6 @@
<string name="menu_import_from_file">Importar desde archivo</string>
<string name="menu_import_from_qr_code">Importar desde código QR</string>
<string name="menu_import_from_nfc">Importar desde NFC</string>
- <string name="menu_export_keys">Exportar todas las claves</string>
<string name="menu_export_key">Exportar a archivo</string>
<string name="menu_delete_key">Borrar clave</string>
<string name="menu_create_key">Crear clave</string>
@@ -81,6 +78,7 @@
<string name="label_email">Correo electrónico</string>
<string name="unknown_status"></string>
<!--choice-->
+ <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
@@ -97,4 +95,5 @@
<!--Key list-->
<!--Key view-->
<!--Navigation Drawer-->
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-es/strings.xml b/OpenPGP-Keychain/src/main/res/values-es/strings.xml
index c643a3cd8..55136642c 100644
--- a/OpenPGP-Keychain/src/main/res/values-es/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-es/strings.xml
@@ -16,6 +16,7 @@
<string name="title_change_passphrase">Cambiar frase de contraseña</string>
<string name="title_set_passphrase">Establecer frase de contraseña</string>
<string name="title_send_email">Enviar email...</string>
+ <string name="title_send_file">Enviar archivo...</string>
<string name="title_encrypt_to_file">Cifrar hacia archivo</string>
<string name="title_decrypt_to_file">Descifrar hacia archivo</string>
<string name="title_import_keys">Importar claves</string>
@@ -47,6 +48,7 @@
<string name="btn_certify">Certificar</string>
<string name="btn_decrypt">Descifrar</string>
<string name="btn_decrypt_verify">Descifrar y verificar</string>
+ <string name="btn_decrypt_verify_clipboard">Desde el portapapeles</string>
<string name="btn_select_encrypt_keys">Seleccionar destinatarios</string>
<string name="btn_encrypt_file">Cifrar archivo</string>
<string name="btn_save">Guardar</string>
@@ -54,8 +56,8 @@
<string name="btn_delete">Eliminar</string>
<string name="btn_no_date">Ninguno</string>
<string name="btn_okay">De acuerdo</string>
- <string name="btn_change_passphrase">Cambiar la frase de contraseña</string>
- <string name="btn_set_passphrase">Establecer frase de contraseña</string>
+ <string name="btn_change_passphrase">Cambiar nueva frase de contraseña</string>
+ <string name="btn_set_passphrase">Establecer nueva frase de contraseña</string>
<string name="btn_search">Buscar</string>
<string name="btn_export_to_server">Cargar al servidor de claves</string>
<string name="btn_next">Siguiente</string>
@@ -63,6 +65,8 @@
<string name="btn_clipboard">Portapapeles</string>
<string name="btn_share">Compartir con...</string>
<string name="btn_lookup_key">Buscar clave</string>
+ <string name="btn_encryption_advanced_settings_show">Mostrar ajustes avanzados</string>
+ <string name="btn_encryption_advanced_settings_hide">Ocultar ajustes avanzados</string>
<!--menu-->
<string name="menu_preferences">Ajustes</string>
<string name="menu_help">Ayuda</string>
@@ -70,16 +74,18 @@
<string name="menu_import_from_qr_code">Importar desde código QR</string>
<string name="menu_import">Importar</string>
<string name="menu_import_from_nfc">Importar desde NFC</string>
- <string name="menu_export_keys">Exportar todas las claves</string>
+ <string name="menu_export_public_keys">Exportar todas las claves públicas</string>
+ <string name="menu_export_secret_keys">Exportar todas las claves secretas</string>
<string name="menu_export_key">Exportar hacia archivo</string>
<string name="menu_delete_key">Borrar clave</string>
<string name="menu_create_key">Crear clave</string>
<string name="menu_create_key_expert">Crear clave (experto)</string>
<string name="menu_search">Buscar</string>
- <string name="menu_key_server">Importar desde servidor de claves</string>
+ <string name="menu_import_from_key_server">Servidor de claves...</string>
+ <string name="menu_key_server">Servidor de claves...</string>
<string name="menu_update_key">Actualizar desde servidor de claves</string>
<string name="menu_export_key_to_server">Cargar al servidor de claves</string>
- <string name="menu_share">Compartir</string>
+ <string name="menu_share">Compartir...</string>
<string name="menu_share_title_fingerprint">Compartir la huella digital...</string>
<string name="menu_share_title">Compartir la clave completa...</string>
<string name="menu_share_default_fingerprint">con...</string>
@@ -93,6 +99,8 @@
<string name="menu_key_edit_cancel">Cancelar</string>
<string name="menu_encrypt_to">Cifrar hacia...</string>
<string name="menu_select_all">Seleccionar todo</string>
+ <string name="menu_add_keys">Añadir claves</string>
+ <string name="menu_export_keys">Exportar claves</string>
<!--label-->
<string name="label_sign">Firmar</string>
<string name="label_message">Mensaje</string>
@@ -105,6 +113,7 @@
<string name="label_select_public_keys">Destinatarios</string>
<string name="label_delete_after_encryption">Borrar después del cifrado</string>
<string name="label_delete_after_decryption">Borrar después del descifrado</string>
+ <string name="label_share_after_encryption">Compartir después del cifrado</string>
<string name="label_encryption_algorithm">Algoritmo de cifrado</string>
<string name="label_hash_algorithm">Algoritmo de Hash</string>
<string name="label_asymmetric">Clave pública</string>
@@ -134,11 +143,17 @@
<string name="user_id_no_name">&lt;sin nombre&gt;</string>
<string name="none">&lt;ninguna&gt;</string>
<string name="no_key">&lt;sin clave&gt;</string>
+ <string name="no_email">&lt;No hay un email&gt;</string>
<string name="unknown_status"></string>
<string name="can_encrypt">se puede cifrar</string>
<string name="can_sign">se puede firmar</string>
<string name="expired">caducado</string>
<string name="revoked">revocado</string>
+ <string name="user_id">ID de usuario</string>
+ <plurals name="n_contacts">
+ <item quantity="one">1 contacto</item>
+ <item quantity="other">%d contactos</item>
+ </plurals>
<plurals name="n_key_servers">
<item quantity="one">%d servidor de claves</item>
<item quantity="other">%d servidores de claves</item>
@@ -147,9 +162,6 @@
<string name="secret_key">Clave secreta:</string>
<!--choice-->
<string name="choice_none">Ninguna</string>
- <string name="choice_sign_only">Solo firmar</string>
- <string name="choice_encrypt_only">Solo cifrar</string>
- <string name="choice_sign_and_encrypt">Firmar y cifrar</string>
<string name="choice_15secs">15 segs</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 mins</string>
@@ -169,21 +181,26 @@
<string name="warning">Advertencia</string>
<string name="error">Error</string>
<string name="error_message">Error: %s</string>
+ <!--key flags-->
+ <string name="flag_certify">Certificar</string>
+ <string name="flag_sign">Firmar</string>
+ <string name="flag_encrypt">Cifrar</string>
+ <string name="flag_authenticate">Autentificar</string>
<!--sentences-->
<string name="wrong_passphrase">Frase de contraseña incorrecta.</string>
<string name="using_clipboard_content">Usando el contenido del portapapeles.</string>
<string name="set_a_passphrase">Establece una frase de contraseña antes.</string>
<string name="no_filemanager_installed">No hay un gestor de archivos compatible instalado.</string>
<string name="passphrases_do_not_match">Las frases de contraseña no coinciden.</string>
- <string name="passphrase_must_not_be_empty">Las frases de contraseña no pueden estar vacías.</string>
+ <string name="passphrase_must_not_be_empty">Por favor, introduce una frase de contraseña.</string>
<string name="passphrase_for_symmetric_encryption">Cifrado simétrico.</string>
<string name="passphrase_for">Introducir la frase de contraseña para \'%s\'</string>
<string name="file_delete_confirmation">¿Estás seguro de que quieres borrar\n%s?</string>
<string name="file_delete_successful">Borrado satisfactoriamente.</string>
<string name="no_file_selected">Selecciona un archivo antes.</string>
- <string name="decryption_successful">Descifrado satisfactoriamente.</string>
- <string name="encryption_successful">Cifrado satisfactoriamente.</string>
- <string name="encryption_to_clipboard_successful">Cifrado satisfactoriamente al portapapeles.</string>
+ <string name="decryption_successful">Descifrado y/o verificado satisfactoriamente.</string>
+ <string name="encryption_successful">Firmado y/o cifrado satisfactoriamente.</string>
+ <string name="encryption_to_clipboard_successful">Firmado y/o cifrado al portapapeles satisfactoriamente.</string>
<string name="enter_passphrase_twice">Introduce la frase de contraseña dos veces.</string>
<string name="select_encryption_key">Selecciona al menos una clave de cifrado.</string>
<string name="select_encryption_or_signature_key">Selecciona al menos una clave de cifrado o de firma.</string>
@@ -194,6 +211,11 @@
<string name="key_deletion_confirmation">¿Quieres realmente borrar la clave \'%s\'?\n¡No podrás deshacerlo!</string>
<string name="key_deletion_confirmation_multi">¿Quieres realmente borrar todas las claves seleccionadas?\n¡No podrás deshacerlo!</string>
<string name="secret_key_deletion_confirmation">¿Quieres realmente borrar la clave SECRETA \'%s\'?\n¡No podrás deshacerlo!</string>
+ <string name="ask_save_changed_key">Has hecho cambios en el almacén de claves, ¿quieres guardarlos?</string>
+ <string name="ask_empty_id_ok">Has añadido una ID de usuario vacía, ¿Estás seguro que quieres continuar?</string>
+ <string name="public_key_deletetion_confirmation">¿Quieres realmente borrar la clave PÚBLICA \'%s\'?\n¡No podrás deshacerlo!</string>
+ <string name="secret_key_delete_text">¿Borrar claves secretas?</string>
+ <string name="also_export_secret_keys">¿Exportar también las claves secretas?</string>
<plurals name="keys_added_and_updated_1">
<item quantity="one">%d clave añadida satisfactoriamente</item>
<item quantity="other">%d claves añadidas satisfactoriamente</item>
@@ -215,6 +237,7 @@
<string name="keys_exported">%d claves exportadas satisfactoriamente.</string>
<string name="no_keys_exported">No se han exportado claves.</string>
<string name="key_creation_el_gamal_info">Nota: solo las subclaves son compatibles con ElGamal, y para ElGamal debe usarse el tamaño de clave más próximo de 1536, 2048, 3072, 4096, o 8192.</string>
+ <string name="key_creation_weak_rsa_info">Nota: generar una clave RSA de longitud 1024-bit o menos está considerado inseguro y desactivado para generar nuevas claves.</string>
<string name="key_not_found">No se puede encontrar la clave %08X.</string>
<plurals name="keys_found">
<item quantity="one">Se ha encontrado %d clave.</item>
@@ -246,6 +269,7 @@
<string name="error_master_key_must_not_be_el_gamal">la clave maestra no puede ser una clave ElGamal</string>
<string name="error_unknown_algorithm_choice">elegido algoritmo desconocido</string>
<string name="error_user_id_needs_a_name">necesitas determinar un nombre</string>
+ <string name="error_user_id_no_email">no se ha encontrado un email</string>
<string name="error_user_id_needs_an_email_address">tienes que determinar una dirección de email</string>
<string name="error_key_needs_a_user_id">necesitas al menos una ID de usuario</string>
<string name="error_main_user_id_must_not_be_empty">la ID del usuario principal no puede estar vacía</string>
@@ -266,22 +290,29 @@
<string name="error_nfc_needed">¡NFC no está disponible en tu dispositivo!</string>
<string name="error_nothing_import">¡Nada que importar!</string>
<string name="error_expiry_must_come_after_creation">la fecha de caducidad debe ser posterior a la fecha de creación</string>
+ <string name="error_save_first">Por favor, guarda el almacén de claves antes</string>
<string name="error_can_not_delete_contact">no puedes eliminar este contacto porque eres tú mismo.</string>
<string name="error_can_not_delete_contacts">no puedes eliminar los siguientes contactos porque son tú mismo:\n%s</string>
<string name="error_keyserver_insufficient_query">Consulta al servidor insuficiente</string>
<string name="error_keyserver_query">La consulta al servidor de claves ha fallado</string>
<string name="error_keyserver_too_many_responses">Demasiadas respuestas</string>
+ <string name="error_import_file_no_content">El archivo está vacio</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_can_not_delete_info">
<item quantity="one">Por favor, bórralo desde la pantalla \'Mis claves\'!</item>
<item quantity="other">Por favor, bórralos desde la pantalla \'Mis claves\'!</item>
</plurals>
+ <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>
+ <item quantity="other">partes del archivo cargado son objetos OpenPGP válidos pero no claves OpenPGP</item>
+ </plurals>
+ <string name="error_change_something_first">Debes hacer cambios en el almacén de claves antes de que puedas guardarlo</string>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">hecho.</string>
- <string name="progress_cancel">cancelar</string>
+ <string name="progress_done">Hecho.</string>
+ <string name="progress_cancel">Cancelar</string>
<string name="progress_saving">guardando...</string>
<string name="progress_importing">importando...</string>
<string name="progress_exporting">exportando...</string>
- <string name="progress_generating">generando la clave, esto puede tardar más de 3 minutos...</string>
<string name="progress_building_key">construyendo la clave...</string>
<string name="progress_preparing_master_key">preparando la clave maestra...</string>
<string name="progress_certifying_master_key">certificando la clave maestra...</string>
@@ -292,6 +323,10 @@
<item quantity="one">exportando clave...</item>
<item quantity="other">exportando claves...</item>
</plurals>
+ <plurals name="progress_generating">
+ <item quantity="one">generando clave, esto puede tardar más de 3 minutos...</item>
+ <item quantity="other">generando claves, esto puede tardar más de 3 minutos...</item>
+ </plurals>
<string name="progress_extracting_signature_key">extrayendo la clave de firma...</string>
<string name="progress_extracting_key">extrayendo la clave...</string>
<string name="progress_preparing_streams">preparando las transmisiones...</string>
@@ -322,6 +357,7 @@
<string name="compression_very_slow">muy lento</string>
<!--Help-->
<string name="help_tab_start">Comenzar</string>
+ <string name="help_tab_faq">FAQ</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Registro de cambios</string>
<string name="help_tab_about">A cerca de</string>
@@ -349,15 +385,22 @@
<string name="intent_send_decrypt">Descifrar con OpenKeychain</string>
<!--Remote API-->
<string name="api_no_apps">¡No hay aplicaciones registradas!\n\nLas aplicaciones de terceros pueden pedir permiso de acceso a OpenKeychain. Después de obtener acceso, serán enumeradas aquí.</string>
+ <string name="api_settings_show_info">Mostrar información avanzada</string>
+ <string name="api_settings_hide_info">Ocultar información avanzada</string>
<string name="api_settings_show_advanced">Mostrar la configuración avanzada</string>
<string name="api_settings_hide_advanced">Ocultar la configuración avanzada</string>
<string name="api_settings_no_key">No se ha seleccionado ninguna clave</string>
<string name="api_settings_select_key">Seleccionar clave</string>
+ <string name="api_settings_create_key">Crear una nueva clave para esta cuenta</string>
<string name="api_settings_save">Guardar</string>
<string name="api_settings_cancel">Cancelar</string>
<string name="api_settings_revoke">Revocar acceso</string>
+ <string name="api_settings_delete_account">Borrar cuenta</string>
<string name="api_settings_package_name">Nombre de paquete</string>
<string name="api_settings_package_signature">SHA-256 de firma de paquete</string>
+ <string name="api_settings_accounts">Cuentas</string>
+ <string name="api_settings_accounts_empty">No hay cuentas asociadas a esta aplicación.</string>
+ <string name="api_create_account_text">La aplicación solicita la creación de una nueva cuenta. Por favor, selecciona una clave privada que ya exista o crea una nueva.\n¡Las aplicaciones tienen restringido el uso de claves a las que tú selecciones aquí!</string>
<string name="api_register_text">La aplicación mostrada solicita acceso a OpenKeychain.\n¿Permitir el acceso?\n\nAVISO: Si no sabes por qué aparece esta pantalla, ¡deniega el acceso! Puedes revocarlo después usando la pantalla \'Aplicaciones registradas\'.</string>
<string name="api_register_allow">Permitir el acceso</string>
<string name="api_register_disallow">Denegar el acceso</string>
@@ -383,17 +426,28 @@
<string name="key_list_empty_button_create">crear tu propia clave</string>
<string name="key_list_empty_button_import">importar claves</string>
<!--Key view-->
+ <string name="key_view_action_edit">Editar esta clave</string>
<string name="key_view_action_encrypt">Cifrar hacia este contacto</string>
<string name="key_view_action_certify">Certificar la clave de este contacto</string>
<string name="key_view_tab_main">Información</string>
<string name="key_view_tab_certs">Certificaciones</string>
<!--Navigation Drawer-->
- <string name="nav_contacts">Contactos</string>
- <string name="nav_encrypt">Cifrar</string>
- <string name="nav_decrypt">Descifrar</string>
+ <string name="nav_contacts">Claves</string>
+ <string name="nav_encrypt">Firmar y cifrar</string>
+ <string name="nav_decrypt">Descifrar y verificar</string>
<string name="nav_import">Importar claves</string>
<string name="nav_secret_keys">Mis claves</string>
<string name="nav_apps">Aplicaciones registradas</string>
<string name="drawer_open">Abrir el Navigation Drawer</string>
<string name="drawer_close">Cerrar el Navigation Drawer</string>
+ <string name="edit">Editar</string>
+ <string name="my_keys">Mis claves</string>
+ <string name="label_secret_key">Claves secretas</string>
+ <string name="secret_key_yes">disponible</string>
+ <string name="secret_key_no">no disponible</string>
+ <string name="section_uids_to_sign">IDs de usuario para firmar</string>
+ <string name="progress_re_adding_certs">Nueva aplicación de certificados</string>
+ <!--hints-->
+ <string name="encrypt_content_edit_text_hint">Escribe aquí el mensaje que quieras cifrar y/o firmar...</string>
+ <string name="decrypt_content_edit_text_hint">Introduce aquí el texto cifrado para descifrarlo y/o verificarlo...</string>
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-et/strings.xml b/OpenPGP-Keychain/src/main/res/values-et/strings.xml
new file mode 100644
index 000000000..62741da09
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values-et/strings.xml
@@ -0,0 +1,119 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <string name="title_manage_public_keys">Kontaktid</string>
+ <string name="title_manage_secret_keys">Salajased võtmed</string>
+ <string name="title_select_recipients">Vali avalik võti</string>
+ <string name="title_select_secret_key">Vali salajane võti</string>
+ <string name="title_encrypt">Krüpteeri</string>
+ <string name="title_decrypt">Dekrüpteeri</string>
+ <string name="title_authentication">Salasõne</string>
+ <string name="title_create_key">Loo võti</string>
+ <string name="title_edit_key">Muuda võtit</string>
+ <string name="title_preferences">Seaded</string>
+ <string name="title_api_registered_apps">Registreeritud rakendused</string>
+ <string name="title_key_server_preference">Võtmeserveri seaded</string>
+ <string name="title_set_passphrase">Määra salasõne</string>
+ <string name="title_send_email">Saada kiri...</string>
+ <string name="title_import_keys">Impordi võtmeid</string>
+ <string name="title_export_key">Ekspordi võti</string>
+ <string name="title_export_keys">Ekspordi võtmed</string>
+ <string name="title_key_not_found">Võtit ei leitud</string>
+ <string name="title_key_server_query">Päri võtmeserverist</string>
+ <string name="title_send_key">Lae võtmeserverisse</string>
+ <string name="title_unknown_signature_key">Võõras allkirjastamise võti</string>
+ <string name="title_help">Abi</string>
+ <!--section-->
+ <string name="section_user_ids">Kasutaja ID-d</string>
+ <string name="section_keys">Võtmed</string>
+ <string name="section_general">Üldine</string>
+ <string name="section_defaults">Vaikeseaded</string>
+ <!--button-->
+ <string name="btn_sign">Allkirjasta</string>
+ <string name="btn_decrypt">Dekrüpteeri</string>
+ <string name="btn_select_encrypt_keys">Vali saajad</string>
+ <string name="btn_save">Salvesta</string>
+ <string name="btn_do_not_save">Katkesta</string>
+ <string name="btn_delete">Kustuta</string>
+ <string name="btn_search">Otsi</string>
+ <string name="btn_export_to_server">Saada võtmeserverisse</string>
+ <string name="btn_next">Järgmine</string>
+ <string name="btn_back">Tagasi</string>
+ <!--menu-->
+ <string name="menu_preferences">Seaded</string>
+ <string name="menu_delete_key">Kustuta võti</string>
+ <string name="menu_create_key">Loo võti</string>
+ <string name="menu_search">Otsi</string>
+ <string name="menu_key_server">Võtmeserver...</string>
+ <string name="menu_update_key">Uuenda võtmeserverist</string>
+ <string name="menu_export_key_to_server">Saada võtmeserverisse</string>
+ <string name="menu_share">Jaga...</string>
+ <string name="menu_sign_key">Allkirjasta võti</string>
+ <!--label-->
+ <string name="label_sign">Allkirjasta</string>
+ <string name="label_message">Sõnum</string>
+ <string name="label_file">Fail</string>
+ <string name="label_no_passphrase">Salasõnet pole</string>
+ <string name="label_passphrase">Salasõne</string>
+ <string name="label_passphrase_again">Uuesti</string>
+ <string name="label_algorithm">Algoritm</string>
+ <string name="label_select_public_keys">Saajad</string>
+ <string name="label_delete_after_encryption">Kustuta peale šifreerimist</string>
+ <string name="label_hash_algorithm">Räsialgoritm</string>
+ <string name="label_asymmetric">Avalik võti</string>
+ <string name="label_symmetric">Salasõne</string>
+ <string name="label_passphrase_cache_ttl">Salasõne puhverdus</string>
+ <string name="label_key_servers">Võtmeserverid</string>
+ <string name="label_creation">Loodud</string>
+ <string name="label_expiry">Aegub</string>
+ <string name="label_usage">Kasutusvaldkond</string>
+ <string name="label_key_size">Võtmepikkus</string>
+ <string name="label_name">Nimi</string>
+ <string name="label_comment">Kommentaar</string>
+ <string name="label_email">E-mail</string>
+ <string name="unknown_status"></string>
+ <string name="expired">aegunud</string>
+ <string name="fingerprint">Sõrmejälg:</string>
+ <string name="secret_key">Salajane võti:</string>
+ <!--choice-->
+ <string name="choice_15secs">15 sekundit</string>
+ <string name="choice_1min">1 minut</string>
+ <string name="choice_3mins">3 minutit</string>
+ <string name="choice_5mins">5 minutit</string>
+ <string name="choice_10mins">10 minutit</string>
+ <string name="choice_20mins">20 minutit</string>
+ <string name="choice_40mins">40 minutit</string>
+ <string name="choice_1hour">1 tund</string>
+ <string name="choice_2hours">2 tundi</string>
+ <string name="choice_4hours">4 tundi</string>
+ <string name="choice_8hours">8 tundi</string>
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+ <string name="filemanager_title_open">Ava...</string>
+ <string name="warning">Hoiatus</string>
+ <string name="error">Viga</string>
+ <string name="error_message">Viga: %s</string>
+ <!--key flags-->
+ <!--sentences-->
+ <string name="wrong_passphrase">Vale salasõne</string>
+ <string name="set_a_passphrase">Määra enne salasõne.</string>
+ <string name="passphrases_do_not_match">Salasõned ei ühti.</string>
+ <string name="passphrase_for_symmetric_encryption">Sümmeetriline krüpteering</string>
+ <!--errors
+ no punctuation, all lowercase,
+ they will be put after "error_message", e.g. "Error: file not found"-->
+ <!--progress dialogs, usually ending in '…'-->
+ <!--action strings-->
+ <!--key bit length selections-->
+ <!--compression-->
+ <!--Help-->
+ <!--Import-->
+ <!--Intent labels-->
+ <!--Remote API-->
+ <!--Share-->
+ <!--Key list-->
+ <!--Key view-->
+ <!--Navigation Drawer-->
+ <!--hints-->
+</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml b/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml
index 6bb115049..7b71d3ecf 100644
--- a/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml
@@ -7,6 +7,7 @@
<!--label-->
<string name="unknown_status"></string>
<!--choice-->
+ <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
@@ -23,4 +24,5 @@
<!--Key list-->
<!--Key view-->
<!--Navigation Drawer-->
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml
index d99bbcd7c..e7d8b0b7f 100644
--- a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml
@@ -48,6 +48,7 @@
<string name="btn_certify">Certifier</string>
<string name="btn_decrypt">Déchiffrer</string>
<string name="btn_decrypt_verify">Déchiffrer et vérifier</string>
+ <string name="btn_decrypt_verify_clipboard">À partir du presse-papiers</string>
<string name="btn_select_encrypt_keys">Choisir les destinataires</string>
<string name="btn_encrypt_file">Chiffrer le fichier</string>
<string name="btn_save">Enregistrer</string>
@@ -55,8 +56,8 @@
<string name="btn_delete">Supprimer</string>
<string name="btn_no_date">Aucune</string>
<string name="btn_okay">OK</string>
- <string name="btn_change_passphrase">Changer la phrase de passe</string>
- <string name="btn_set_passphrase">Définir la phrase de passe</string>
+ <string name="btn_change_passphrase">Changer la nouvelle phrase de passe</string>
+ <string name="btn_set_passphrase">Définir la nouvelle phrase de passe</string>
<string name="btn_search">Rechercher</string>
<string name="btn_export_to_server">Téléverser vers le serveur de clefs</string>
<string name="btn_next">Suivant</string>
@@ -64,6 +65,8 @@
<string name="btn_clipboard">Presse-papiers</string>
<string name="btn_share">Partager avec...</string>
<string name="btn_lookup_key">Rechercher la clef</string>
+ <string name="btn_encryption_advanced_settings_show">Afficher les paramètres avancés</string>
+ <string name="btn_encryption_advanced_settings_hide">Masquer les paramètres avancés</string>
<!--menu-->
<string name="menu_preferences">Paramètres</string>
<string name="menu_help">Aide</string>
@@ -71,16 +74,18 @@
<string name="menu_import_from_qr_code">Importer depuis un code QR</string>
<string name="menu_import">Importer</string>
<string name="menu_import_from_nfc">Importer avec NFC</string>
- <string name="menu_export_keys">Exporter toutes les clefs</string>
+ <string name="menu_export_public_keys">Exporter toutes les clefs publiques</string>
+ <string name="menu_export_secret_keys">Exporter toutes les clefs secrètes</string>
<string name="menu_export_key">Exporter vers un fichier</string>
<string name="menu_delete_key">Supprimer la clef</string>
<string name="menu_create_key">Créer une clef</string>
<string name="menu_create_key_expert">Créer une clef (expert)</string>
<string name="menu_search">Rechercher</string>
- <string name="menu_key_server">Importer depuis le serveur de clefs</string>
+ <string name="menu_import_from_key_server">Serveur de clefs</string>
+ <string name="menu_key_server">Serveur de clefs...</string>
<string name="menu_update_key">Mettre à jour depuis le serveur de clefs</string>
<string name="menu_export_key_to_server">Téléverser vers le serveur de clefs</string>
- <string name="menu_share">Partager</string>
+ <string name="menu_share">Partager...</string>
<string name="menu_share_title_fingerprint">Partager l\'empreinte...</string>
<string name="menu_share_title">Partager la clef entière...</string>
<string name="menu_share_default_fingerprint">avec...</string>
@@ -94,6 +99,8 @@
<string name="menu_key_edit_cancel">Annuler</string>
<string name="menu_encrypt_to">Chiffrer vers...</string>
<string name="menu_select_all">Tout sélectionner</string>
+ <string name="menu_add_keys">Ajouter des clefs</string>
+ <string name="menu_export_keys">Exporter des clefs</string>
<!--label-->
<string name="label_sign">Signer</string>
<string name="label_message">Message</string>
@@ -125,8 +132,6 @@
<string name="label_name">Nom</string>
<string name="label_comment">Commentaire</string>
<string name="label_email">Courriel</string>
- <string name="label_sign_user_id">Signer l\'ID utilisateur</string>
- <string name="label_sign_email">Signer le courriel</string>
<string name="label_send_key">Téléverser la clef vers le serveur de clefs choisi après certification</string>
<string name="label_fingerprint">Empreinte</string>
<string name="select_keys_button_default">Choisir</string>
@@ -138,12 +143,17 @@
<string name="user_id_no_name">&lt;aucun nom&gt;</string>
<string name="none">&lt;aucune&gt;</string>
<string name="no_key">&lt;pas de clef&gt;</string>
+ <string name="no_email">&lt;aucun courriel&gt;</string>
<string name="unknown_status"></string>
<string name="can_encrypt">peut chiffrer</string>
<string name="can_sign">peut signer</string>
<string name="expired">expiré</string>
<string name="revoked">révoquée</string>
<string name="user_id">ID utilisateur</string>
+ <plurals name="n_contacts">
+ <item quantity="one">1 contact</item>
+ <item quantity="other">%d contacts</item>
+ </plurals>
<plurals name="n_key_servers">
<item quantity="one">%d serveur de clefs</item>
<item quantity="other">%d serveurs de clefs</item>
@@ -152,9 +162,6 @@
<string name="secret_key">Clef secrète :</string>
<!--choice-->
<string name="choice_none">Aucune</string>
- <string name="choice_sign_only">Signer seulement</string>
- <string name="choice_encrypt_only">Chiffrer seulement</string>
- <string name="choice_sign_and_encrypt">Signer et chiffrer</string>
<string name="choice_15secs">15 s</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 min</string>
@@ -174,21 +181,26 @@
<string name="warning">Avertissement</string>
<string name="error">Erreur</string>
<string name="error_message">Erreur : %s</string>
+ <!--key flags-->
+ <string name="flag_certify">Certifier</string>
+ <string name="flag_sign">Signer</string>
+ <string name="flag_encrypt">Chiffrer</string>
+ <string name="flag_authenticate">Authentifier</string>
<!--sentences-->
<string name="wrong_passphrase">Phrase de passe erronée</string>
<string name="using_clipboard_content">Utiliser le contenu du presse-papiers.</string>
<string name="set_a_passphrase">Définir d\'abord une phrase de passe.</string>
<string name="no_filemanager_installed">Aucun gestionnaire de fichiers compatible installé.</string>
<string name="passphrases_do_not_match">Les phrases de passe ne correspondent pas.</string>
- <string name="passphrase_must_not_be_empty">Les phrases de passe vides ne sont pas autorisées.</string>
+ <string name="passphrase_must_not_be_empty">Veuillez saisir une phrase de passe</string>
<string name="passphrase_for_symmetric_encryption">Chriffrement symétrique.</string>
<string name="passphrase_for">Saisir une phrase de passe pour « %s »</string>
<string name="file_delete_confirmation">Êtes-vous sûr de vouloir supprimer\n%s ?</string>
<string name="file_delete_successful">Supprimé avec succès.</string>
<string name="no_file_selected">Choisir d\'abord un fichier.</string>
- <string name="decryption_successful">Déchiffré avec succès.</string>
- <string name="encryption_successful">Chiffré avec succès.</string>
- <string name="encryption_to_clipboard_successful">Chiffré vers le presse-papiers avec succès.</string>
+ <string name="decryption_successful">Déchiffré et/ou vérifié avec succès</string>
+ <string name="encryption_successful">Signé et/ou chiffré avec succès</string>
+ <string name="encryption_to_clipboard_successful">Signé et/ou chiffré vers le presse-papiers avec succès</string>
<string name="enter_passphrase_twice">Saisir la phrase de passe deux fois.</string>
<string name="select_encryption_key">Choisir au moins une clef de chiffrement.</string>
<string name="select_encryption_or_signature_key">Choisir au moins une clef de chiffrement ou de signature.</string>
@@ -199,6 +211,11 @@
<string name="key_deletion_confirmation">Voulez-vous vraiment supprimer la clef %s ?\nVous ne pourrez pas la restituer !</string>
<string name="key_deletion_confirmation_multi">Voulez-vous vraiment supprimer toutes les clefs choisies ?\nCeci est irréversible !</string>
<string name="secret_key_deletion_confirmation">Voulez-vous vraiment supprimer la clef SECRÈTE %s ?\nVous ne pourrez pas la restituer !</string>
+ <string name="ask_save_changed_key">Vous avez apporté des changements au trousseau, voulez-vous l\'enregistrer ?</string>
+ <string name="ask_empty_id_ok">Vous avez ajouté un ID utilisateur vide, êtes-vous certain de vouloir continuer?</string>
+ <string name="public_key_deletetion_confirmation">Voulez-vous vraiment supprimer la clef PUBLIQUE « %s » ?\nVous ne pourrez pas la restituer !</string>
+ <string name="secret_key_delete_text">Supprimer les clefs privées ?</string>
+ <string name="also_export_secret_keys">Exporter aussi les clefs secrètes?</string>
<plurals name="keys_added_and_updated_1">
<item quantity="one">%d clef ajoutée avec succès</item>
<item quantity="other">%d clefs ajoutées avec succès</item>
@@ -220,6 +237,7 @@
<string name="keys_exported">%d clefs exportées avec succès.</string>
<string name="no_keys_exported">Aucune clef exportée.</string>
<string name="key_creation_el_gamal_info">Note : seules les sous-clefs prennent en charge ElGamal, et pour ElGamal la taille de clef la plus proche de 1 536, 2 048, 3 072, 4 096 ou 8 192 sera utilisée.</string>
+ <string name="key_creation_weak_rsa_info">Note : générer des clefs RSA d\'une longueur de 1024 bits ou moins est considéré non sécuritaire et est désactivé pour la génération de nouvelles clefs.</string>
<string name="key_not_found">Clef %08X introuvable.</string>
<plurals name="keys_found">
<item quantity="one">%d clef trouvée.</item>
@@ -251,6 +269,7 @@
<string name="error_master_key_must_not_be_el_gamal">la clef maîtresse ne peut être une clef ElGama</string>
<string name="error_unknown_algorithm_choice">choix d\'algorhitme inconnu</string>
<string name="error_user_id_needs_a_name">vous devez spécifier un nom</string>
+ <string name="error_user_id_no_email">aucun courriel trouvé</string>
<string name="error_user_id_needs_an_email_address">vous devez spécifier une adresse courriel</string>
<string name="error_key_needs_a_user_id">vous avez besoin d\'au moins un ID utilisateur</string>
<string name="error_main_user_id_must_not_be_empty">l\'ID utilisateur principal ne doit pas être vide</string>
@@ -271,6 +290,7 @@
<string name="error_nfc_needed">NFC n\'est pas disponible sur votre appareil !</string>
<string name="error_nothing_import">Rien à importer !</string>
<string name="error_expiry_must_come_after_creation">la date d\'expiration doit venir après la date de création</string>
+ <string name="error_save_first">veuillez d\'abord enregistrer le trousseau</string>
<string name="error_can_not_delete_contact">vous ne pouvez pas supprimer ce contact car c\'est le vôtre.</string>
<string name="error_can_not_delete_contacts">vous ne pouvez pas supprimer les contacts suivants car c\'est les vôtres.\n%s</string>
<string name="error_keyserver_insufficient_query">Requête serveur insuffisante</string>
@@ -286,13 +306,13 @@
<item quantity="one">une partie du fichier chargé est un objet OpenPGP valide mais pas une clef OpenPGP</item>
<item quantity="other">certaines parties du fichier chargé sont des objets OpenPGP valides mais pas des clefs OpenPGP</item>
</plurals>
+ <string name="error_change_something_first">Vous devez apporter des changements au trousseau avant de pouvoir l\'enregistrer</string>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">fait.</string>
- <string name="progress_cancel">annuler</string>
+ <string name="progress_done">Terminé.</string>
+ <string name="progress_cancel">Annuler</string>
<string name="progress_saving">sauvegarde...</string>
<string name="progress_importing">importation...</string>
<string name="progress_exporting">exportation...</string>
- <string name="progress_generating">génération de la clef, ceci peut prendre jusqu\'à 3 minutes...</string>
<string name="progress_building_key">assemblage de la clef...</string>
<string name="progress_preparing_master_key">préparation de la clef maîtresse...</string>
<string name="progress_certifying_master_key">certification de la clef maîtresse...</string>
@@ -303,6 +323,10 @@
<item quantity="one">exportation de la clef...</item>
<item quantity="other">exportation des clefs...</item>
</plurals>
+ <plurals name="progress_generating">
+ <item quantity="one">génération de la clef, ceci peut prendre jusqu\'à 3 min...</item>
+ <item quantity="other">génération des clefs, ceci peut prendre jusqu\'à 3 min...</item>
+ </plurals>
<string name="progress_extracting_signature_key">extraction de la clef de signature...</string>
<string name="progress_extracting_key">extraction de la clef...</string>
<string name="progress_preparing_streams">préparation des flux...</string>
@@ -333,6 +357,7 @@
<string name="compression_very_slow">très lent</string>
<!--Help-->
<string name="help_tab_start">Commencer</string>
+ <string name="help_tab_faq">FAQ</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Journal des changements</string>
<string name="help_tab_about">À propos de</string>
@@ -360,15 +385,22 @@
<string name="intent_send_decrypt">Déchiffrer avec OpenKeychain</string>
<!--Remote API-->
<string name="api_no_apps">Aucune application enregistrée !\n\nLes applications tierces peuvent demander l\'accès à OpenKeychain. Après avoir autorisé l\'accès, elles seront listées ici.</string>
+ <string name="api_settings_show_info">Afficher les informations avancées</string>
+ <string name="api_settings_hide_info">Masquer les informations avancées</string>
<string name="api_settings_show_advanced">Afficher les paramètres avancés</string>
<string name="api_settings_hide_advanced">Masquer les paramètres avancés</string>
<string name="api_settings_no_key">Aucune clef choisie</string>
<string name="api_settings_select_key">Choisir une clef</string>
+ <string name="api_settings_create_key">Créer une nouvelle clef pour ce compte</string>
<string name="api_settings_save">Enregistrer</string>
<string name="api_settings_cancel">Annuler</string>
<string name="api_settings_revoke">Révoquer l\'accès</string>
+ <string name="api_settings_delete_account">Supprimer le compte</string>
<string name="api_settings_package_name">Nom du paquet</string>
<string name="api_settings_package_signature">SHA-256 de la signature du paquet</string>
+ <string name="api_settings_accounts">Comptes</string>
+ <string name="api_settings_accounts_empty">Aucun compte n\'est attaché à cette application.</string>
+ <string name="api_create_account_text">L\'application demande la création d\'un nouveau compte. Veuillez choisir un clef privée existante ou en créer une.\nLes applications sont restreintes à l\'utilisation de clefs choisies ici.</string>
<string name="api_register_text">L\'application affichée demande l\'accès à OpenKeychain.\nPermettre l\'accès ?\n\nAvertissement : si vous ne savez pas pourquoi cet écran est apparu, refusez l\'accès ! Vous pourrez révoquer l\'accès plus tard en utilisant l\'écran « Applications enregistrées ».</string>
<string name="api_register_allow">Permettre l\'accès</string>
<string name="api_register_disallow">Enlever l\'accès</string>
@@ -394,17 +426,28 @@
<string name="key_list_empty_button_create">créer votre propre clef</string>
<string name="key_list_empty_button_import">Importer des clefs.</string>
<!--Key view-->
+ <string name="key_view_action_edit">Modifier cette clef</string>
<string name="key_view_action_encrypt">Chiffrer vers ce contact</string>
<string name="key_view_action_certify">Certifier la clef de ce contact</string>
<string name="key_view_tab_main">Infos</string>
<string name="key_view_tab_certs">Certifications</string>
<!--Navigation Drawer-->
- <string name="nav_contacts">Contacts</string>
- <string name="nav_encrypt">Chiffrer</string>
- <string name="nav_decrypt">Déchiffrer</string>
+ <string name="nav_contacts">Clefs</string>
+ <string name="nav_encrypt">Signer et chiffrer</string>
+ <string name="nav_decrypt">Déchiffrer et vérifier</string>
<string name="nav_import">Importer les clefs</string>
<string name="nav_secret_keys">Mes clefs</string>
<string name="nav_apps">Applis enregistrées</string>
<string name="drawer_open">Ouvrir le tiroir de navigation</string>
<string name="drawer_close">Fermer le tiroir de navigation</string>
+ <string name="edit">Modifier</string>
+ <string name="my_keys">Mes clefs</string>
+ <string name="label_secret_key">Clef secrète</string>
+ <string name="secret_key_yes">disponible</string>
+ <string name="secret_key_no">non disponible</string>
+ <string name="section_uids_to_sign">ID utilisateur pour signer</string>
+ <string name="progress_re_adding_certs">Nouvel application des certificats</string>
+ <!--hints-->
+ <string name="encrypt_content_edit_text_hint">Écrire ici le message à chiffrer et/ou signer...</string>
+ <string name="decrypt_content_edit_text_hint">Saisir le cryptogramme à déchiffrer et/ou à vérifier ici...</string>
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml b/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml
index f9e7074da..ff46c9606 100644
--- a/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml
@@ -48,6 +48,7 @@
<string name="btn_certify">Certifica</string>
<string name="btn_decrypt">Decodifica</string>
<string name="btn_decrypt_verify">Decodifica e Verifica</string>
+ <string name="btn_decrypt_verify_clipboard">Dagli Appunti</string>
<string name="btn_select_encrypt_keys">Seleziona Destinatari</string>
<string name="btn_encrypt_file">Codifica File</string>
<string name="btn_save">Salva</string>
@@ -55,8 +56,8 @@
<string name="btn_delete">Elimina</string>
<string name="btn_no_date">Nessuno</string>
<string name="btn_okay">Okay</string>
- <string name="btn_change_passphrase">Cambia Frase Di Accesso</string>
- <string name="btn_set_passphrase">Imposta Frase di Accesso</string>
+ <string name="btn_change_passphrase">Cambia Nuova Frase di Accesso</string>
+ <string name="btn_set_passphrase">Imposta Nuova Frase di Accesso</string>
<string name="btn_search">Cerca</string>
<string name="btn_export_to_server">Carica sul Server delle Chiavi</string>
<string name="btn_next">Prossimo</string>
@@ -64,6 +65,8 @@
<string name="btn_clipboard">Appunti</string>
<string name="btn_share">Condividi con...</string>
<string name="btn_lookup_key">Chiave di ricerca</string>
+ <string name="btn_encryption_advanced_settings_show">Mostra impostazioni avanzate</string>
+ <string name="btn_encryption_advanced_settings_hide">Nascondi impostazioni avanzate</string>
<!--menu-->
<string name="menu_preferences">Impostazioni</string>
<string name="menu_help">Aiuto</string>
@@ -71,16 +74,17 @@
<string name="menu_import_from_qr_code">Importa da Codice QR</string>
<string name="menu_import">Importa</string>
<string name="menu_import_from_nfc">Importa tramite NFC</string>
- <string name="menu_export_keys">Esporta tutte le chiavi</string>
+ <string name="menu_export_secret_keys">Esporta tutte le chiavi segrete</string>
<string name="menu_export_key">Esporta su un file</string>
<string name="menu_delete_key">Cancella chiave</string>
<string name="menu_create_key">Crea chiave</string>
- <string name="menu_create_key_expert">Crea chiave (esperto)</string>
+ <string name="menu_create_key_expert">Crea chiave (avanzato)</string>
<string name="menu_search">Cerca</string>
- <string name="menu_key_server">Importa dal server delle chiavi</string>
+ <string name="menu_import_from_key_server">Server delle Chiavi</string>
+ <string name="menu_key_server">Server delle Chiavi...</string>
<string name="menu_update_key">Aggiorna dal server delle chiavi</string>
<string name="menu_export_key_to_server">Carica chiave nel server</string>
- <string name="menu_share">Condividi</string>
+ <string name="menu_share">Condividi...</string>
<string name="menu_share_title_fingerprint">Condivi impronta...</string>
<string name="menu_share_title">Condividi intera chiave...</string>
<string name="menu_share_default_fingerprint">con..</string>
@@ -100,7 +104,7 @@
<string name="label_file">File</string>
<string name="label_no_passphrase">Nessuna Frase di Accesso</string>
<string name="label_passphrase">Frase di Accesso</string>
- <string name="label_passphrase_again">Ancora</string>
+ <string name="label_passphrase_again">Di nuovo</string>
<string name="label_algorithm">Algortimo</string>
<string name="label_ascii_armor">Armatura ASCII</string>
<string name="label_select_public_keys">Destinatari</string>
@@ -125,8 +129,6 @@
<string name="label_name">Nome</string>
<string name="label_comment">Commento</string>
<string name="label_email">Email</string>
- <string name="label_sign_user_id">Firma ID Utente</string>
- <string name="label_sign_email">Firma email</string>
<string name="label_send_key">Carica chiave nel server delle chiavi selezionati dopo la certificazione</string>
<string name="label_fingerprint">Impronta</string>
<string name="select_keys_button_default">Seleziona</string>
@@ -138,12 +140,17 @@
<string name="user_id_no_name">&lt;nessun nome&gt;</string>
<string name="none">&lt;nessuno&gt;</string>
<string name="no_key">&lt;nessuna chiave&gt;</string>
+ <string name="no_email">&lt;Nessuna Email&gt;</string>
<string name="unknown_status"></string>
<string name="can_encrypt">puo\'; codificare</string>
<string name="can_sign">puo\' firmare</string>
<string name="expired">scaduto</string>
<string name="revoked">revocato</string>
<string name="user_id">ID Utente</string>
+ <plurals name="n_contacts">
+ <item quantity="one">1 contatto</item>
+ <item quantity="other">%d contatti</item>
+ </plurals>
<plurals name="n_key_servers">
<item quantity="one">%d server delle chiavi</item>
<item quantity="other">%d server delle chiavi</item>
@@ -152,9 +159,6 @@
<string name="secret_key">Chiave Privata:</string>
<!--choice-->
<string name="choice_none">Nessuno</string>
- <string name="choice_sign_only">Firma soltanto</string>
- <string name="choice_encrypt_only">Codifica soltanto</string>
- <string name="choice_sign_and_encrypt">Firma e Codifica</string>
<string name="choice_15secs">15 sec</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 min</string>
@@ -174,21 +178,22 @@
<string name="warning">Attenzione</string>
<string name="error">Errore</string>
<string name="error_message">Errore: %s</string>
+ <!--key flags-->
+ <string name="flag_certify">Certifica</string>
+ <string name="flag_sign">Firma</string>
+ <string name="flag_encrypt">Codifica</string>
+ <string name="flag_authenticate">Convalida</string>
<!--sentences-->
<string name="wrong_passphrase">Frase di Accesso errata</string>
<string name="using_clipboard_content">Utilizzo il contenuto degli appunti.</string>
<string name="set_a_passphrase">Imposta prima una frase di accesso.</string>
<string name="no_filemanager_installed">Nessun gestore file compatibile installato.</string>
<string name="passphrases_do_not_match">Le frasi di accesso non corrispondono.</string>
- <string name="passphrase_must_not_be_empty">Frasi di accesso vuote non consentite.</string>
<string name="passphrase_for_symmetric_encryption">Codifica Simmetrica.</string>
<string name="passphrase_for">Inserisci la frase di accesso per \'%s\'</string>
<string name="file_delete_confirmation">Sei sicuro di voler cancellare\n%s?</string>
<string name="file_delete_successful">Eliminato correttamente.</string>
<string name="no_file_selected">Seleziona un file prima.</string>
- <string name="decryption_successful">Decodificato correttamente.</string>
- <string name="encryption_successful">Codificato correttamente.</string>
- <string name="encryption_to_clipboard_successful">Codificato correttamente negli appunti.</string>
<string name="enter_passphrase_twice">Inserisci la frase di accesso due volte.</string>
<string name="select_encryption_key">Seleziona almeno una chiave di codifica.</string>
<string name="select_encryption_or_signature_key">Seleziona almeno una chiave di codifica o di firma.</string>
@@ -199,6 +204,10 @@
<string name="key_deletion_confirmation">Vuoi veramente eliminare la chiave \'%s\'?\nNon potrai annullare!</string>
<string name="key_deletion_confirmation_multi">Vuoi veramente eliminare le chiavi selezionate?\nNon potrai annullare!</string>
<string name="secret_key_deletion_confirmation">Vuoi veramente eliminare la chiave PRIVATA \'%s\'?\nNon potrai annullare!</string>
+ <string name="ask_save_changed_key">Hai apportato modifiche al tuo portachiavi, vuoi salvarlo?</string>
+ <string name="public_key_deletetion_confirmation">Vuoi veramente eliminare la chiave PUBBLICA \'%s\'?\nNon potrai annullare!</string>
+ <string name="secret_key_delete_text">Eliminare le Chiavi Segrete?</string>
+ <string name="also_export_secret_keys">Esportare anche le chiavi segrete?</string>
<plurals name="keys_added_and_updated_1">
<item quantity="one">%d chiave aggiunta correttamente</item>
<item quantity="other">%d chiavi aggiunte correttamente</item>
@@ -220,6 +229,7 @@
<string name="keys_exported">%d chiavi esportate correttamente.</string>
<string name="no_keys_exported">Nessuna chiave esportata.</string>
<string name="key_creation_el_gamal_info">Nota: solo le sottochiavi supportano ElGamal, e per ElGamal verra\' usata la grandezza chiave piu\' vicina a 1536, 2048, 3072, 4096 o 8192.</string>
+ <string name="key_creation_weak_rsa_info">Nota: la generazione di chiavi RSA con lunghezza pari a 1024 bit o inferiore è considerata non sicura ed è disabilitata per la generazione di nuove chiavi.</string>
<string name="key_not_found">Impossibile trovare la chiave %08X.</string>
<plurals name="keys_found">
<item quantity="one">Trovata %d chiave.</item>
@@ -251,6 +261,7 @@
<string name="error_master_key_must_not_be_el_gamal">La chiave principale non puo\' essere ElGamal</string>
<string name="error_unknown_algorithm_choice">opzione algoritmo sconosciuta</string>
<string name="error_user_id_needs_a_name">devi specificare un nome</string>
+ <string name="error_user_id_no_email">Nessuna email trovata</string>
<string name="error_user_id_needs_an_email_address">devi specificare un indirizzo email</string>
<string name="error_key_needs_a_user_id">necessario almeno un id utente</string>
<string name="error_main_user_id_must_not_be_empty">id utente principale non puo\' essere vuoto</string>
@@ -271,6 +282,7 @@
<string name="error_nfc_needed">NFC non disponibile nel tuo dispositivo!</string>
<string name="error_nothing_import">Niente da importare!</string>
<string name="error_expiry_must_come_after_creation">La data di scadenza deve essere postuma quella di creazione</string>
+ <string name="error_save_first">si prega di salvare il portachiavi primo</string>
<string name="error_can_not_delete_contact">Non è possibile eliminare questo contatto, perché è il proprio.</string>
<string name="error_can_not_delete_contacts">Non è possibile eliminare i seguenti contatti perché sono i propri:\n%s</string>
<string name="error_keyserver_insufficient_query">Query di server insufficiente</string>
@@ -287,12 +299,11 @@
<item quantity="other">parti del file caricato sono oggetti OpenPGP validi, ma non chavi OpenPGP</item>
</plurals>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">fatto.</string>
- <string name="progress_cancel">cancella</string>
+ <string name="progress_done">Fatto.</string>
+ <string name="progress_cancel">Annulla</string>
<string name="progress_saving">salvataggio...</string>
<string name="progress_importing">importazione...</string>
<string name="progress_exporting">esportazione...</string>
- <string name="progress_generating">generazione chiave, richiede fino a 3 minuti...</string>
<string name="progress_building_key">fabbricazione chiave...</string>
<string name="progress_preparing_master_key">preparazione chiave principale...</string>
<string name="progress_certifying_master_key">certificazione chiave principale...</string>
@@ -303,6 +314,10 @@
<item quantity="one">esportazione chiave...</item>
<item quantity="other">esportazione chiavi...</item>
</plurals>
+ <plurals name="progress_generating">
+ <item quantity="one">generazione chiave, sono necessari fino a 3 minuti...</item>
+ <item quantity="other">generazione chiavi, sono necessari fino a 3 minuti...</item>
+ </plurals>
<string name="progress_extracting_signature_key">estrazione chiavi di firma...</string>
<string name="progress_extracting_key">estrazione chiave...</string>
<string name="progress_preparing_streams">preparazione flussi...</string>
@@ -333,6 +348,7 @@
<string name="compression_very_slow">molto lento</string>
<!--Help-->
<string name="help_tab_start">Inizia</string>
+ <string name="help_tab_faq">FAQ</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Novita\'</string>
<string name="help_tab_about">Info</string>
@@ -359,16 +375,23 @@
<string name="intent_send_encrypt">Codifica con OpenKeychain</string>
<string name="intent_send_decrypt">Decodifica con OpenKeychain</string>
<!--Remote API-->
- <string name="api_no_apps">Nessuna app registrata!\n\nApp di terza parti possono richiedere accesso a OpenKeychain. Dopo aver concesso l\'accesso, saranno elencate qui.</string>
+ <string name="api_no_apps">Nessuna app registrata!\n\nApp di terze parti possono richiedere l\'accesso a OpenKeychain. Dopo aver concesso l\'accesso, le app saranno elencate qui.</string>
+ <string name="api_settings_show_info">Mostra informazioni dettagliate</string>
+ <string name="api_settings_hide_info">Nascondi informazioni dettagliate</string>
<string name="api_settings_show_advanced">Mostra impostazioni avanzate</string>
<string name="api_settings_hide_advanced">Nascondi impostazioni avanzate</string>
<string name="api_settings_no_key">Nessuna chiave selezionata</string>
<string name="api_settings_select_key">Seleziona chiave</string>
+ <string name="api_settings_create_key">Crea una nuova chiave per questo account</string>
<string name="api_settings_save">Salva</string>
<string name="api_settings_cancel">Annulla</string>
<string name="api_settings_revoke">Revoca accesso</string>
+ <string name="api_settings_delete_account">Cancella account</string>
<string name="api_settings_package_name">Nome Pacchetto</string>
<string name="api_settings_package_signature">SHA-256 della Firma del Pacchetto</string>
+ <string name="api_settings_accounts">Account</string>
+ <string name="api_settings_accounts_empty">Nessun account collegato a questa applicazione</string>
+ <string name="api_create_account_text">L\'applicazione richiede la creazione di un nuovo account. Si prega di selezionare una chiave privata esistente o crearne una nuova.\nLe applicazioni sono limitate all\'utilizzo delle chiavi selezionate qui!</string>
<string name="api_register_text">Le app visualizzate hanno richiesto l\'accesso a OpenKeychain.\nPermetti accesso?\n\nATTENZIONE: Se non sai perche\' questo schermata e\' apparsa, nega l\'accesso! Puoi revocare l\'accesso dopo, usando la schermata \'App Registrate\'.</string>
<string name="api_register_allow">Permetti accesso</string>
<string name="api_register_disallow">Nega accesso</string>
@@ -394,17 +417,23 @@
<string name="key_list_empty_button_create">creazione della tua chiave</string>
<string name="key_list_empty_button_import">importazione chiavi.</string>
<!--Key view-->
+ <string name="key_view_action_edit">Modifica chiave</string>
<string name="key_view_action_encrypt">Codifica a questo contatto</string>
<string name="key_view_action_certify">Certifica la chiave di questo contatto</string>
<string name="key_view_tab_main">Info</string>
<string name="key_view_tab_certs">Certificazioni</string>
<!--Navigation Drawer-->
- <string name="nav_contacts">Contatti</string>
- <string name="nav_encrypt">Codifica</string>
- <string name="nav_decrypt">Decodifica</string>
- <string name="nav_import">Importare Chiavi</string>
+ <string name="nav_import">Importa Chiavi</string>
<string name="nav_secret_keys">Le Mie Chiavi</string>
<string name="nav_apps">App Registrate</string>
<string name="drawer_open">Apri drawer di navigazione</string>
<string name="drawer_close">Chiudi drawer di navigazione</string>
+ <string name="edit">Modifica</string>
+ <string name="my_keys">Le Mie Chiavi</string>
+ <string name="label_secret_key">Chiave Segreta</string>
+ <string name="secret_key_yes">disponibile</string>
+ <string name="secret_key_no">non disponibile</string>
+ <string name="section_uids_to_sign">ID Utente da firmare</string>
+ <string name="progress_re_adding_certs">Riapplicazione certificati</string>
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-ja/strings.xml b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml
index e5ee5ecc0..eceefb8a3 100644
--- a/OpenPGP-Keychain/src/main/res/values-ja/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml
@@ -41,13 +41,14 @@
<string name="section_certification_key">あなたの鍵を証明に利用します</string>
<string name="section_upload_key">鍵のアップロード</string>
<string name="section_key_server">鍵サーバ</string>
- <string name="section_encrypt_and_or_sign">暗号化と/もしくは署名</string>
+ <string name="section_encrypt_and_or_sign">暗号化/署名</string>
<string name="section_decrypt_verify">復号化と検証</string>
<!--button-->
<string name="btn_sign">署名</string>
<string name="btn_certify">検証</string>
<string name="btn_decrypt">復号化</string>
<string name="btn_decrypt_verify">復号化と検証</string>
+ <string name="btn_decrypt_verify_clipboard">クリップボードから</string>
<string name="btn_select_encrypt_keys">受信者の選択</string>
<string name="btn_encrypt_file">ファイル暗号化</string>
<string name="btn_save">保存</string>
@@ -55,15 +56,17 @@
<string name="btn_delete">削除</string>
<string name="btn_no_date">無し</string>
<string name="btn_okay">OK</string>
- <string name="btn_change_passphrase">パスフレーズの変更</string>
- <string name="btn_set_passphrase">パスフレーズの設定</string>
+ <string name="btn_change_passphrase">新しいパスフレーズに変更</string>
+ <string name="btn_set_passphrase">新しいパスフレーズを設定</string>
<string name="btn_search">検索</string>
<string name="btn_export_to_server">鍵サーバへアップロード</string>
<string name="btn_next">次</string>
<string name="btn_back">戻る</string>
<string name="btn_clipboard">クリップボード</string>
- <string name="btn_share">共有...</string>
+ <string name="btn_share">...で共有</string>
<string name="btn_lookup_key">鍵検出</string>
+ <string name="btn_encryption_advanced_settings_show">拡張設定を表示</string>
+ <string name="btn_encryption_advanced_settings_hide">拡張設定を隠す</string>
<!--menu-->
<string name="menu_preferences">設定</string>
<string name="menu_help">ヘルプ</string>
@@ -71,16 +74,18 @@
<string name="menu_import_from_qr_code">QRコードからインポート</string>
<string name="menu_import">インポート</string>
<string name="menu_import_from_nfc">NFCからインポート</string>
- <string name="menu_export_keys">すべての鍵のエクスポート</string>
+ <string name="menu_export_public_keys">すべての公開鍵のエクスポート</string>
+ <string name="menu_export_secret_keys">すべての秘密鍵のエクスポート</string>
<string name="menu_export_key">ファイルへのエクスポート</string>
<string name="menu_delete_key">鍵の削除</string>
<string name="menu_create_key">鍵の生成</string>
<string name="menu_create_key_expert">鍵の生成(上級)</string>
<string name="menu_search">検索</string>
- <string name="menu_key_server">鍵サーバからのインポート</string>
+ <string name="menu_import_from_key_server">鍵サーバ</string>
+ <string name="menu_key_server">鍵サーバ...</string>
<string name="menu_update_key">鍵サーバからの更新</string>
<string name="menu_export_key_to_server">鍵サーバへのアップロード</string>
- <string name="menu_share">共有</string>
+ <string name="menu_share">共有...</string>
<string name="menu_share_title_fingerprint">指紋の共有...</string>
<string name="menu_share_title">すべての鍵の共有...</string>
<string name="menu_share_default_fingerprint">...(指紋)</string>
@@ -94,6 +99,8 @@
<string name="menu_key_edit_cancel">キャンセル</string>
<string name="menu_encrypt_to">暗号化...</string>
<string name="menu_select_all">すべて選択</string>
+ <string name="menu_add_keys">鍵の追加</string>
+ <string name="menu_export_keys">複数鍵のエクスポート</string>
<!--label-->
<string name="label_sign">署名</string>
<string name="label_message">メッセージ</string>
@@ -125,8 +132,6 @@
<string name="label_name">名前</string>
<string name="label_comment">コメント</string>
<string name="label_email">Eメールアドレス</string>
- <string name="label_sign_user_id">署名ユーザーID</string>
- <string name="label_sign_email">メールを署名</string>
<string name="label_send_key">証明後選択した鍵サーバに鍵をアップロード</string>
<string name="label_fingerprint">指紋</string>
<string name="select_keys_button_default">選択</string>
@@ -137,12 +142,16 @@
<string name="user_id_no_name">&lt;名前なし&gt;</string>
<string name="none">&lt;無し&gt;</string>
<string name="no_key">&lt;鍵無し&gt;</string>
+ <string name="no_email">&lt;メールなし&gt;</string>
<string name="unknown_status"></string>
<string name="can_encrypt">暗号化可能</string>
<string name="can_sign">署名可能</string>
<string name="expired">期限切れ</string>
<string name="revoked">破棄</string>
<string name="user_id">ユーザーID</string>
+ <plurals name="n_contacts">
+ <item quantity="other">%d個の連絡先</item>
+ </plurals>
<plurals name="n_key_servers">
<item quantity="other">%d の鍵サーバ</item>
</plurals>
@@ -150,9 +159,6 @@
<string name="secret_key">秘密鍵:</string>
<!--choice-->
<string name="choice_none">無し</string>
- <string name="choice_sign_only">署名のみ</string>
- <string name="choice_encrypt_only">暗号化のみ</string>
- <string name="choice_sign_and_encrypt">署名と暗号化</string>
<string name="choice_15secs">15秒</string>
<string name="choice_1min">1分</string>
<string name="choice_3mins">3分</string>
@@ -172,21 +178,26 @@
<string name="warning">注意</string>
<string name="error">エラー</string>
<string name="error_message">エラー: %s</string>
+ <!--key flags-->
+ <string name="flag_certify">検証</string>
+ <string name="flag_sign">署名</string>
+ <string name="flag_encrypt">暗号化</string>
+ <string name="flag_authenticate">証明</string>
<!--sentences-->
<string name="wrong_passphrase">良くないパスフレーズ</string>
<string name="using_clipboard_content">クリップボードの内容を使う。</string>
<string name="set_a_passphrase">最初にパスフレーズを設定してください。</string>
<string name="no_filemanager_installed">互換性のないファイルマネージャがインストールされています。</string>
<string name="passphrases_do_not_match">パスフレーズが一致しません。</string>
- <string name="passphrase_must_not_be_empty">空のパスフレーズは受け付けません。</string>
+ <string name="passphrase_must_not_be_empty">パスフレーズを入れてください。</string>
<string name="passphrase_for_symmetric_encryption">対称暗号。</string>
<string name="passphrase_for">\'%s\' にパスフレーズを入れてください。</string>
<string name="file_delete_confirmation">%s を削除してもかまいませんか?</string>
<string name="file_delete_successful">削除に成功しました。</string>
<string name="no_file_selected">最初にファイルを選択してください。</string>
- <string name="decryption_successful">復号化に成功しました。</string>
- <string name="encryption_successful">暗号化に成功しました。</string>
- <string name="encryption_to_clipboard_successful">クリップボードの中身の暗号化に成功しました。</string>
+ <string name="decryption_successful">復号化/検証に成功しました。</string>
+ <string name="encryption_successful">署名/暗号化に成功しました。</string>
+ <string name="encryption_to_clipboard_successful">クリップボードの中身の署名/暗号化に成功しました。</string>
<string name="enter_passphrase_twice">もう一度パスフレーズを入れてください。</string>
<string name="select_encryption_key">少なくとも1つの暗号化鍵を選択して下さい。</string>
<string name="select_encryption_or_signature_key">少なくとも1つの暗号化鍵か署名鍵を選択して下さい。</string>
@@ -197,6 +208,11 @@
<string name="key_deletion_confirmation">鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません!</string>
<string name="key_deletion_confirmation_multi">選択したすべての鍵を本当に削除してよいですか?\nこれは元に戻せません。</string>
<string name="secret_key_deletion_confirmation">秘密鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません!</string>
+ <string name="ask_save_changed_key">あなたは鍵輪に変更を加えました、これを保存しますか?</string>
+ <string name="ask_empty_id_ok">あなたは空のユーザーIDを追加しました、このまま続けますか?</string>
+ <string name="public_key_deletetion_confirmation">公開鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません!</string>
+ <string name="secret_key_delete_text">秘密鍵を削除しますか?</string>
+ <string name="also_export_secret_keys">秘密鍵もエクスポートしますか?</string>
<plurals name="keys_added_and_updated_1">
<item quantity="other">%d の鍵を追加しました</item>
</plurals>
@@ -214,6 +230,7 @@
<string name="keys_exported">%d の鍵をエクスポートしました。</string>
<string name="no_keys_exported">鍵をエクスポートしていません。</string>
<string name="key_creation_el_gamal_info">備考: 副鍵として ElGamalだけがサポートされ, ElGamal は鍵サイズとして1536, 2048, 3072, 4096, 8192 だけが使えます。</string>
+ <string name="key_creation_weak_rsa_info">付記: 長さ1024bitかそれ以下で生成されたRSA鍵は安全とはみなされず、新な鍵の生成は無効にされています。</string>
<string name="key_not_found">鍵 %08X は見付かりませんでした。</string>
<plurals name="keys_found">
<item quantity="other">%d の鍵を発見。</item>
@@ -243,6 +260,7 @@
<string name="error_master_key_must_not_be_el_gamal">主鍵を ElGamal にすることはできません</string>
<string name="error_unknown_algorithm_choice">未知のアルゴリズムを選択しています</string>
<string name="error_user_id_needs_a_name">名前を特定する必要があります</string>
+ <string name="error_user_id_no_email">メールが見付かりません</string>
<string name="error_user_id_needs_an_email_address">Eメールアドレスを特定する必要があります</string>
<string name="error_key_needs_a_user_id">最低でも1つのユーザIDが必要です</string>
<string name="error_main_user_id_must_not_be_empty">主ユーザIDは空にすることはできません</string>
@@ -263,6 +281,7 @@
<string name="error_nfc_needed">あなたのデバイスにはNFCが存在しません!</string>
<string name="error_nothing_import">インポートするものがありません!</string>
<string name="error_expiry_must_come_after_creation">期限日時は生成日時より後である必要があります</string>
+ <string name="error_save_first">まず鍵輪を保存してください</string>
<string name="error_can_not_delete_contact">この連絡先はあなたなので削除できません。</string>
<string name="error_can_not_delete_contacts">この連絡先はあなたなので削除できません。:\n%s</string>
<string name="error_keyserver_insufficient_query">サーバへのクエリーが不足しています</string>
@@ -276,13 +295,13 @@
<plurals name="error_import_non_pgp_part">
<item quantity="other">読み込んだファイルのOpenPGPオブジェクト部分は正しいですが、OpenPGPの鍵ではありません</item>
</plurals>
+ <string name="error_change_something_first">あなたは鍵輪を保存する前に変更を加えなくてはなりません</string>
<!--progress dialogs, usually ending in '…'-->
<string name="progress_done">完了。</string>
<string name="progress_cancel">キャンセル</string>
<string name="progress_saving">保存...</string>
<string name="progress_importing">インポート...</string>
<string name="progress_exporting">エクスポート...</string>
- <string name="progress_generating">鍵の生成、3分ほどかかります...</string>
<string name="progress_building_key">鍵の構築中...</string>
<string name="progress_preparing_master_key">主鍵の準備中...</string>
<string name="progress_certifying_master_key">主鍵の検証中...</string>
@@ -292,6 +311,9 @@
<plurals name="progress_exporting_key">
<item quantity="other">鍵のエクスポート...</item>
</plurals>
+ <plurals name="progress_generating">
+ <item quantity="other">鍵の生成中、最大3分ほどかかります...</item>
+ </plurals>
<string name="progress_extracting_signature_key">署名鍵の取り出し中...</string>
<string name="progress_extracting_key">鍵の取り出し中...</string>
<string name="progress_preparing_streams">ストリームの準備中...</string>
@@ -311,7 +333,7 @@
<!--action strings-->
<string name="hint_public_keys">公開鍵の検索</string>
<string name="hint_secret_keys">秘密鍵の検索</string>
- <string name="action_share_key_with">鍵の共有...</string>
+ <string name="action_share_key_with">...で鍵の共有</string>
<!--key bit length selections-->
<string name="key_size_512">512</string>
<string name="key_size_1024">1024</string>
@@ -322,6 +344,7 @@
<string name="compression_very_slow">とても遅い</string>
<!--Help-->
<string name="help_tab_start">開始</string>
+ <string name="help_tab_faq">FAQ</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Changelog</string>
<string name="help_tab_about">これについて</string>
@@ -348,15 +371,22 @@
<string name="intent_send_decrypt">OpenKeychainで復号化</string>
<!--Remote API-->
<string name="api_no_apps">登録されていないアプリケーション!\n\nサードパーティアプリケーションはOpenKeychainにアクセスを要求できます。アクセスを与えた後、それらはここにリストされます。</string>
+ <string name="api_settings_show_info">詳細情報を表示</string>
+ <string name="api_settings_hide_info">詳細情報を非表示</string>
<string name="api_settings_show_advanced">拡張設定を表示</string>
- <string name="api_settings_hide_advanced">拡張設定を非表示</string>
+ <string name="api_settings_hide_advanced">拡張設定を隠す</string>
<string name="api_settings_no_key">鍵が選択されていない</string>
<string name="api_settings_select_key">鍵の選択</string>
+ <string name="api_settings_create_key">このアカウントで新しい鍵を生成</string>
<string name="api_settings_save">保存</string>
<string name="api_settings_cancel">キャンセル</string>
<string name="api_settings_revoke">破棄されたアクセス</string>
+ <string name="api_settings_delete_account">アカウントを削除</string>
<string name="api_settings_package_name">パッケージ名</string>
<string name="api_settings_package_signature">パッケージの署名 SHA-256</string>
+ <string name="api_settings_accounts">アカウント</string>
+ <string name="api_settings_accounts_empty">このアプリケーションに接続されてるアカウントはありません。</string>
+ <string name="api_create_account_text">このアプリケーションは新しいアカウントの生成を要求しています。すでにある秘密鍵を選択するか、新しく生成してください。\nここであなたが選択する鍵の使い道についてアプリケーションには制約があります!</string>
<string name="api_register_text">表示されているアプリケーションはOpenKeychainへのアクセスを要求しています。\nアクセスを許可しますか?\n\n注意: もしなぜスクリーンに表れたかわからないなら、アクセスを許可しないでください! あなたは\'登録済みアプリケーション\'スクリーンを使って、以降のアクセスを破棄するこもできます。</string>
<string name="api_register_allow">許可されたアクセス</string>
<string name="api_register_disallow">許可されないアクセス</string>
@@ -381,17 +411,28 @@
<string name="key_list_empty_button_create">あなた所有の鍵を作る</string>
<string name="key_list_empty_button_import">鍵のインポート。</string>
<!--Key view-->
+ <string name="key_view_action_edit">この鍵の編集</string>
<string name="key_view_action_encrypt">この連絡先を暗号化</string>
<string name="key_view_action_certify">この連絡先の鍵を検証</string>
<string name="key_view_tab_main">情報</string>
<string name="key_view_tab_certs">証明</string>
<!--Navigation Drawer-->
- <string name="nav_contacts">連絡先</string>
- <string name="nav_encrypt">暗号化</string>
- <string name="nav_decrypt">復号化</string>
+ <string name="nav_contacts">鍵</string>
+ <string name="nav_encrypt">署名と暗号化</string>
+ <string name="nav_decrypt">復号化と検証</string>
<string name="nav_import">鍵のインポート</string>
<string name="nav_secret_keys">自分の鍵</string>
<string name="nav_apps">登録済みのアプリ</string>
<string name="drawer_open">ナビゲーションドロワーを開く</string>
<string name="drawer_close">ナビゲーションドロワーを閉める</string>
+ <string name="edit">編集</string>
+ <string name="my_keys">自分の鍵</string>
+ <string name="label_secret_key">秘密鍵</string>
+ <string name="secret_key_yes">存在する</string>
+ <string name="secret_key_no">存在しない</string>
+ <string name="section_uids_to_sign">署名に使うユーザーID</string>
+ <string name="progress_re_adding_certs">検証を再適用する</string>
+ <!--hints-->
+ <string name="encrypt_content_edit_text_hint">ここに書いたメッセージを暗号化/署名..</string>
+ <string name="decrypt_content_edit_text_hint">ここに入力された暗号化テキストを復号化/検証...</string>
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-large/dimens.xml b/OpenPGP-Keychain/src/main/res/values-large/dimens.xml
new file mode 100644
index 000000000..192a4bb99
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values-large/dimens.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="drawer_content_padding">240dp</dimen>
+</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml b/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml
index de6ba554d..9cb9a4756 100644
--- a/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml
@@ -36,8 +36,6 @@
<string name="btn_delete">Verwijderen</string>
<string name="btn_no_date">Geen</string>
<string name="btn_okay">OK</string>
- <string name="btn_change_passphrase">Wachtwoord wijzigen</string>
- <string name="btn_set_passphrase">Wachtwoord instellen</string>
<string name="btn_search">Zoeken</string>
<string name="btn_next">Volgende</string>
<string name="btn_back">Terug</string>
@@ -46,7 +44,6 @@
<string name="menu_import_from_file">Importeren uit bestand</string>
<string name="menu_import_from_qr_code">Importeren met QR-code</string>
<string name="menu_import_from_nfc">Importeren met NFC</string>
- <string name="menu_export_keys">Alle sleutels exporteren</string>
<string name="menu_export_key">Exporteren naar bestand</string>
<string name="menu_delete_key">Sleutel verwijderen</string>
<string name="menu_create_key">Sleutel aanmaken</string>
@@ -91,9 +88,6 @@
<string name="secret_key">Privésleutel:</string>
<!--choice-->
<string name="choice_none">Geen</string>
- <string name="choice_sign_only">Alleen ondertekenen</string>
- <string name="choice_encrypt_only">Alleen versleutelen</string>
- <string name="choice_sign_and_encrypt">Ondertekenen en versleutelen</string>
<string name="choice_15secs">15 sec.</string>
<string name="choice_1min">1 min.</string>
<string name="choice_3mins">3 min.</string>
@@ -112,21 +106,18 @@
<string name="warning">Waarschuwing</string>
<string name="error">Fout</string>
<string name="error_message">Fout: %s</string>
+ <!--key flags-->
<!--sentences-->
<string name="wrong_passphrase">Wachtwoord verkeerd.</string>
<string name="using_clipboard_content">Gebruikmaken van klembordinhoud.</string>
<string name="set_a_passphrase">Stel eerst een wachtwoord in.</string>
<string name="no_filemanager_installed">Geen compatibele bestandsbeheerder geïnstalleerd.</string>
<string name="passphrases_do_not_match">De wachtwoorden komen niet overeen.</string>
- <string name="passphrase_must_not_be_empty">Lege wachtwoorden zijn niet toegestaand.</string>
<string name="passphrase_for_symmetric_encryption">Symmetrische versleuteling.</string>
<string name="passphrase_for">Voer het wachtwoord in voor \'%s\'</string>
<string name="file_delete_confirmation">Weer u zeker dat u het volgende wilt verwijderen:\n%s?</string>
<string name="file_delete_successful">Succesvol verwijderd.</string>
<string name="no_file_selected">Selecteer eerst een bestand.</string>
- <string name="decryption_successful">Succesvol ontsleuteld.</string>
- <string name="encryption_successful">Succesvol versleuteld.</string>
- <string name="encryption_to_clipboard_successful">Succesvol versleuteld naar klembord.</string>
<string name="enter_passphrase_twice">Voer het wachtwoord tweemaal in.</string>
<string name="select_encryption_key">Selecteer ten minste één versleutelingssleutel.</string>
<string name="select_encryption_or_signature_key">Selecter ten minste één versleutelings-/ondertekeningssleutel.</string>
@@ -170,7 +161,6 @@
<string name="error_nfc_needed">Uw apparaat biedt geen ondersteuning voor NFC</string>
<string name="error_nothing_import">Niets te importeren</string>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">gereed.</string>
<string name="progress_saving">opslaan...</string>
<string name="progress_importing">importeren...</string>
<string name="progress_exporting">exporteren...</string>
@@ -236,4 +226,5 @@
<!--Key list-->
<!--Key view-->
<!--Navigation Drawer-->
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-pl/strings.xml b/OpenPGP-Keychain/src/main/res/values-pl/strings.xml
new file mode 100644
index 000000000..336f0bff7
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values-pl/strings.xml
@@ -0,0 +1,468 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <string name="title_manage_public_keys">Kontakty</string>
+ <string name="title_manage_secret_keys">Klucze prywatne</string>
+ <string name="title_select_recipients">Wybierz Klucz Publiczny</string>
+ <string name="title_select_secret_key">Wybierz Klucz Prywatny</string>
+ <string name="title_encrypt">Zaszyfruj</string>
+ <string name="title_decrypt">Odszyfruj</string>
+ <string name="title_authentication">Hasło</string>
+ <string name="title_create_key">Utwórz Klucz</string>
+ <string name="title_edit_key">Edytuj Klucz</string>
+ <string name="title_preferences">Właściwości</string>
+ <string name="title_api_registered_apps">Zarejestrowane Aplikacje</string>
+ <string name="title_key_server_preference">Właściwości serwera kluczy</string>
+ <string name="title_change_passphrase">Zmień hasło</string>
+ <string name="title_set_passphrase">Ustaw hasło</string>
+ <string name="title_send_email">Wyślij maila...</string>
+ <string name="title_send_file">Wyślij plik...</string>
+ <string name="title_encrypt_to_file">Zaszyfruj do pliku</string>
+ <string name="title_decrypt_to_file">Odszyfruj do pliku</string>
+ <string name="title_import_keys">Importuj klucze</string>
+ <string name="title_export_key">Eksportuj klucz</string>
+ <string name="title_export_keys">Eksportuj klucze</string>
+ <string name="title_key_not_found">Nie znaleziono klucza</string>
+ <string name="title_key_server_query">Wyślij zapytanie do serwera kluczy</string>
+ <string name="title_send_key">Wyślij do serwera kluczy</string>
+ <string name="title_unknown_signature_key">Nieznany klucz podpisu</string>
+ <string name="title_certify_key">Certyfikuj klucz</string>
+ <string name="title_key_details">Szczegóły klucza</string>
+ <string name="title_help">Pomoc</string>
+ <!--section-->
+ <string name="section_user_ids">Identyfikator użytkownika</string>
+ <string name="section_keys">Klucze</string>
+ <string name="section_general">Ogólne</string>
+ <string name="section_defaults">Domyślne</string>
+ <string name="section_advanced">Zaawansowane</string>
+ <string name="section_master_key">Klucz główny</string>
+ <string name="section_master_user_id">Główny identyfikator użytkownika</string>
+ <string name="section_actions">Działania</string>
+ <string name="section_certification_key">Twój klucz użyty do certyfikacji</string>
+ <string name="section_upload_key">Wyślij klucz</string>
+ <string name="section_key_server">Serwer kluczy</string>
+ <string name="section_encrypt_and_or_sign">Zaszyfruj i/lub podpisz</string>
+ <string name="section_decrypt_verify">Deszyfruj i weryfikuj</string>
+ <!--button-->
+ <string name="btn_sign">Podpisz</string>
+ <string name="btn_certify">Certyfikuj</string>
+ <string name="btn_decrypt">Odszyfruj</string>
+ <string name="btn_decrypt_verify">Deszyfruj i weryfikuj</string>
+ <string name="btn_decrypt_verify_clipboard">Ze schowka</string>
+ <string name="btn_select_encrypt_keys">Wybierz odbiorców</string>
+ <string name="btn_encrypt_file">Zaszyfruj plik</string>
+ <string name="btn_save">Zapisz</string>
+ <string name="btn_do_not_save">Anuluj</string>
+ <string name="btn_delete">Usuń</string>
+ <string name="btn_no_date">Żaden</string>
+ <string name="btn_okay">Ok</string>
+ <string name="btn_change_passphrase">Zmień nowe hasło</string>
+ <string name="btn_set_passphrase">Ustaw nowe hasło</string>
+ <string name="btn_search">Wyszukaj</string>
+ <string name="btn_export_to_server">Wyślij do serwera kluczy</string>
+ <string name="btn_next">Dalej</string>
+ <string name="btn_back">Wstecz</string>
+ <string name="btn_clipboard">Schowek</string>
+ <string name="btn_share">Podziel się z...</string>
+ <string name="btn_lookup_key">Klucz wyszukiwania</string>
+ <string name="btn_encryption_advanced_settings_show">Pokaż zaawanowane ustawienia</string>
+ <string name="btn_encryption_advanced_settings_hide">Ukryj zaawansowane ustawienia</string>
+ <!--menu-->
+ <string name="menu_preferences">Ustawienia</string>
+ <string name="menu_help">Pomoc</string>
+ <string name="menu_import_from_file">Zaimportuj z pliku</string>
+ <string name="menu_import_from_qr_code">Zaimportuj z kodu QR</string>
+ <string name="menu_import">Import</string>
+ <string name="menu_import_from_nfc">Zaimportuj przy użyciu NFC</string>
+ <string name="menu_export_public_keys">Eksportuj wszystkie klucze publiczne</string>
+ <string name="menu_export_secret_keys">Eksportuj wszystkie prywatne klucze</string>
+ <string name="menu_export_key">Eksportuj do pliku</string>
+ <string name="menu_delete_key">Usuń klucz</string>
+ <string name="menu_create_key">Stwórz klucz</string>
+ <string name="menu_create_key_expert">Stwórz klucz (tryb zaawansowany)</string>
+ <string name="menu_search">Znajdź</string>
+ <string name="menu_import_from_key_server">Serwer kluczy</string>
+ <string name="menu_key_server">Serwer kluczy...</string>
+ <string name="menu_update_key">Aktualizuj z serwera kluczy</string>
+ <string name="menu_export_key_to_server">Wyślij do serwera kluczy</string>
+ <string name="menu_share">Udostepnij...</string>
+ <string name="menu_share_title_fingerprint">Udostepnij odcisk...</string>
+ <string name="menu_share_title">Udostępnij cały klucz...</string>
+ <string name="menu_share_default_fingerprint">z...</string>
+ <string name="menu_share_default">z...</string>
+ <string name="menu_share_qr_code">za pomocą kodu QR</string>
+ <string name="menu_share_qr_code_fingerprint">za pomocą kodu QR</string>
+ <string name="menu_share_nfc">za pomocą NFC</string>
+ <string name="menu_copy_to_clipboard">Kopiuj do schowka</string>
+ <string name="menu_sign_key">Klucz podpisu</string>
+ <string name="menu_beam_preferences">Ustawienia Beam</string>
+ <string name="menu_key_edit_cancel">Anuluj</string>
+ <string name="menu_encrypt_to">Zaszyfruj do...</string>
+ <string name="menu_select_all">Wybierz wszystko</string>
+ <string name="menu_add_keys">Dodaj klucze</string>
+ <string name="menu_export_keys">Eksportuj klucze</string>
+ <!--label-->
+ <string name="label_sign">Podpis</string>
+ <string name="label_message">Wiadomość</string>
+ <string name="label_file">Plik</string>
+ <string name="label_no_passphrase">Brak hasła</string>
+ <string name="label_passphrase">Hasło</string>
+ <string name="label_passphrase_again">Ponów</string>
+ <string name="label_algorithm">Algorytm</string>
+ <string name="label_ascii_armor">ASCII Armor</string>
+ <string name="label_select_public_keys">Odbiorcy</string>
+ <string name="label_delete_after_encryption">Usuń po zaszyfrowaniu</string>
+ <string name="label_delete_after_decryption">Usuń po odszyfrowaniu</string>
+ <string name="label_share_after_encryption">Udostępnij po zaszyfrowaniu</string>
+ <string name="label_encryption_algorithm">Algorytm szyfrujący</string>
+ <string name="label_hash_algorithm">Algorytm funkcji skrótu</string>
+ <string name="label_asymmetric">Klucz publiczny</string>
+ <string name="label_symmetric">Hasło</string>
+ <string name="label_passphrase_cache_ttl">Bufor haseł</string>
+ <string name="label_message_compression">Kompresja wiadomości</string>
+ <string name="label_file_compression">Kompresja plików</string>
+ <string name="label_force_v3_signature">Wymuś stare podpisy OpenPGPv3</string>
+ <string name="label_key_servers">Serwery kluczy</string>
+ <string name="label_key_id">Identyfikator klucza</string>
+ <string name="label_creation">Utworzenia</string>
+ <string name="label_expiry">Wygaśnięcia</string>
+ <string name="label_usage">Wykorzystanie</string>
+ <string name="label_key_size">Rozmiar klucza</string>
+ <string name="label_main_user_id">Identyfikator głównego użytkownika</string>
+ <string name="label_name">Imię</string>
+ <string name="label_comment">Komentarz</string>
+ <string name="label_email">Adres email</string>
+ <string name="label_send_key">Wyślij klucz do serwera kluczy po certyfikacji</string>
+ <string name="label_fingerprint">Odcisk</string>
+ <string name="select_keys_button_default">Wybierz</string>
+ <string name="expiry_date_dialog_title">Ustaw datę wygaśnięcia</string>
+ <plurals name="select_keys_button">
+ <item quantity="one">wybrano %d</item>
+ <item quantity="few">wybrano %d</item>
+ <item quantity="other">wybrano %d</item>
+ </plurals>
+ <string name="user_id_no_name">&lt;bez nazwy&gt;</string>
+ <string name="none">&lt;żaden&gt;</string>
+ <string name="no_key">&lt;brak klucza&gt;</string>
+ <string name="no_email">&lt;Brak adresu email&gt;</string>
+ <string name="unknown_status"></string>
+ <string name="can_encrypt">może szyfrować</string>
+ <string name="can_sign">może podpisywać</string>
+ <string name="expired">wygasły</string>
+ <string name="revoked">unieważniony</string>
+ <string name="user_id">Identyfikator użytkownika</string>
+ <plurals name="n_contacts">
+ <item quantity="one">1 kontakt</item>
+ <item quantity="few">%d kontakty</item>
+ <item quantity="other">%d kontaktów</item>
+ </plurals>
+ <plurals name="n_key_servers">
+ <item quantity="one">%d serwer kluczy</item>
+ <item quantity="few">%d serwerów kluczy</item>
+ <item quantity="other">%d serwerów kluczy</item>
+ </plurals>
+ <string name="fingerprint">Odcisk:</string>
+ <string name="secret_key">Klucz prywatny:</string>
+ <!--choice-->
+ <string name="choice_none">Brak</string>
+ <string name="choice_15secs">15 sekund</string>
+ <string name="choice_1min">1 minuta</string>
+ <string name="choice_3mins">3 minuty</string>
+ <string name="choice_5mins">5 minut</string>
+ <string name="choice_10mins">10 minut</string>
+ <string name="choice_20mins">20 minut</string>
+ <string name="choice_40mins">40 minut</string>
+ <string name="choice_1hour">1 godzina</string>
+ <string name="choice_2hours">2 godziny</string>
+ <string name="choice_4hours">4 godziny</string>
+ <string name="choice_8hours">8 godzin</string>
+ <string name="choice_forever">na zawsze</string>
+ <string name="dsa">DSA</string>
+ <string name="elgamal">ElGamal</string>
+ <string name="rsa">RSA</string>
+ <string name="filemanager_title_open">Otwórz...</string>
+ <string name="warning">Ostrzeżenie</string>
+ <string name="error">Błąd</string>
+ <string name="error_message">Błąd: %s</string>
+ <!--key flags-->
+ <string name="flag_certify">Certyfikuj</string>
+ <string name="flag_sign">Podpisz</string>
+ <string name="flag_encrypt">Zaszyfruj</string>
+ <string name="flag_authenticate">Autentykuj</string>
+ <!--sentences-->
+ <string name="wrong_passphrase">Nieprawidłowe hasło.</string>
+ <string name="using_clipboard_content">Użycie zawartości schowka.</string>
+ <string name="set_a_passphrase">Najpierw ustaw hasło.</string>
+ <string name="no_filemanager_installed">Nie zainstalowano żadnego kompatybilnego menadżera plików.</string>
+ <string name="passphrases_do_not_match">Hasła nie pasują do siebie</string>
+ <string name="passphrase_must_not_be_empty">Podaj hasło.</string>
+ <string name="passphrase_for_symmetric_encryption">Szyfrowanie symetryczne.</string>
+ <string name="passphrase_for">Podaj hasło dla \'%s\'</string>
+ <string name="file_delete_confirmation">Czy jesteś pewien że chcesz usunąć\n%s?</string>
+ <string name="file_delete_successful">Usunięto pomyślnie.</string>
+ <string name="no_file_selected">Najpierw wskaż plik.</string>
+ <string name="decryption_successful">Pomyślnie deszyfrowano i/lub zweryfikowano.</string>
+ <string name="encryption_successful">Pomyślnie podpisano i/lub zaszyfrowano.</string>
+ <string name="encryption_to_clipboard_successful">Pomyslnie podpisano i/lub zaszyfrowano do schowka.</string>
+ <string name="enter_passphrase_twice">Podaj hasło dwukrotnie.</string>
+ <string name="select_encryption_key">Wybierz co najmniej jeden klucz szyfrujący.</string>
+ <string name="select_encryption_or_signature_key">Wybierz co najmniej jeden klucz szyfrujący lub klucz podpisujący.</string>
+ <string name="specify_file_to_encrypt_to">Wskaż, do którego pliku zapisać zaszyfrowane dane.\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje.</string>
+ <string name="specify_file_to_decrypt_to">Wskaż, do którego pliku zapisać odszyfrowane dane.\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje.</string>
+ <string name="specify_file_to_export_to">Wskaż, do którego pliku wyeksportować dane.\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje.</string>
+ <string name="specify_file_to_export_secret_keys_to">Wskaż, do którego pliku zapisać eksportowane dane.\nOSTRZEŻENIE: Masz zamiar zapisać klucze PRYWATNE (tajne)\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje.</string>
+ <string name="key_deletion_confirmation">Czy na pewno chcesz usunąć klucz \'%s\'?\nNie można cofnąć tej operacji!</string>
+ <string name="key_deletion_confirmation_multi">Czy na pewno chcesz usunąć wszystkie zaznaczone klucze?\nTej operacji nie można cofnąć!</string>
+ <string name="secret_key_deletion_confirmation">Czy na pewno chcesz usunąć klucz prywatny \'%s\'?\nNie można cofnąć tej operacji!</string>
+ <string name="ask_save_changed_key">Zostały dokonane zmiany w pęku kluczy, czy chcesz je zachować?</string>
+ <string name="ask_empty_id_ok">Dodałeś pusty identyfikator użytkownika, czy na pewno chcesz kontynuować?</string>
+ <string name="public_key_deletetion_confirmation">Czy na pewno chcesz usunąć klucz publiczny \'%s\'?\nNie można cofnąć tej operacji!</string>
+ <string name="secret_key_delete_text">Usunąć klucze prywatne?</string>
+ <string name="also_export_secret_keys">Czy wyeksportować również klucze prywatne?</string>
+ <plurals name="keys_added_and_updated_1">
+ <item quantity="one">Pomyślnie dodano %d klucz</item>
+ <item quantity="few">Pomyślnie dodano %d kluczy</item>
+ <item quantity="other">Pomyślnie dodano %d kluczy</item>
+ </plurals>
+ <plurals name="keys_added_and_updated_2">
+ <item quantity="one">i zaktualizowano %d klucz.</item>
+ <item quantity="few">i zaktualizowano %d kluczy.</item>
+ <item quantity="other">i zaktualizowano %d kluczy.</item>
+ </plurals>
+ <plurals name="keys_added">
+ <item quantity="one">Pomyślnie dodano %d klucz.</item>
+ <item quantity="few">Pomyślnie dodano %d kluczy.</item>
+ <item quantity="other">Pomyślnie dodano %d kluczy.</item>
+ </plurals>
+ <plurals name="keys_updated">
+ <item quantity="one">Pomyślnie zaktualizowano %d klucz.</item>
+ <item quantity="few">Pomyślnie zaktualizowano %d kluczy.</item>
+ <item quantity="other">Pomyślnie zaktualizowano %d kluczy.</item>
+ </plurals>
+ <string name="no_keys_added_or_updated">Nie dodano ani zaktualizowano żadnych kluczy.</string>
+ <string name="key_exported">Pomyślnie wyeksportowano 1 klucz.</string>
+ <string name="keys_exported">Pomyślnie wyeksportowano %d kluczy.</string>
+ <string name="no_keys_exported">Nie wyeksportowano żadnych kluczy.</string>
+ <string name="key_creation_el_gamal_info">Uwaga: algorytm EnGamal jest obsługiwany tylko przez podklucze i użyty zostanie najbliższy rozmiar klucza z podanych: 1536, 2048, 3072, 4096, 8192.</string>
+ <string name="key_creation_weak_rsa_info">Uwaga: generowanie klucza RSA o długości 1024 bity i mniejszej jest uważane za niebezpieczne i wyłączone dla tworzenia nowych kluczy.</string>
+ <string name="key_not_found">Nie można znaleźć klucza %08X.</string>
+ <plurals name="keys_found">
+ <item quantity="one">Znaleziono %d klucz.</item>
+ <item quantity="few">Znaleziono %d kluczy.</item>
+ <item quantity="other">Znaleziono %d kluczy.</item>
+ </plurals>
+ <string name="unknown_signature">Nieznany podpis, naciśnij przycisk, aby wyszukać brakujący klucz.</string>
+ <plurals name="bad_keys_encountered">
+ <item quantity="one">Zignorowano %d niepoprawny klucz prywatny. Prawdopodobnie został wyeksportowany przy uzyciu opcji\n --export-secret-subkeys\nUpewnij się że eksportujesz go z opcją\n --export-secret-keys\nktóra jest poprawna.</item>
+ <item quantity="few">Zignorowano %d niepoprawnych kluczy prywatnych. Prawdopodobnie zostały wyeksportowane przy uzyciu opcji\n --export-secret-subkeys\nUpewnij się że eksportujesz je z opcją\n --export-secret-keys\nktóra jest poprawna.</item>
+ <item quantity="other">zignorowano %d niepoprawnych kluczy prywatnych. Prawdopodobnie zostały wyeksportowane przy uzyciu opcji\n --export-secret-subkeys\nUpewnij się że eksportujesz je z opcją\n --export-secret-keys\nktóra jest poprawna.</item>
+ </plurals>
+ <string name="key_send_success">Pomyślnie wysłano klucz na serwer</string>
+ <string name="key_sign_success">Pomyślnie podpisano klucz</string>
+ <string name="list_empty">Lista jest pusta!</string>
+ <string name="nfc_successfull">Pomyślnie wysłano klucz przez NFC!</string>
+ <string name="key_copied_to_clipboard">Klucz został skopiowany do schowka!</string>
+ <string name="key_has_already_been_signed">Klucz został już wcześniej podpisany!</string>
+ <string name="select_key_to_sign">Wybierz klucz, który zostanie użyty do podpisania!</string>
+ <string name="key_too_big_for_sharing">Klucz ma za duży rozmiar by być udostępniony w ten sposób!</string>
+ <!--errors
+ no punctuation, all lowercase,
+ they will be put after "error_message", e.g. "Error: file not found"-->
+ <string name="error_file_delete_failed">usuwanie \'%s\' zakończone niepowodzeniem</string>
+ <string name="error_file_not_found">plik nie znaleziony</string>
+ <string name="error_no_secret_key_found">nie znaleziono pasującego klucza prywatnego</string>
+ <string name="error_no_known_encryption_found">napotkano nieznany rodzaj szyfrowania</string>
+ <string name="error_external_storage_not_ready">zewnętrzne urządzenie jest niegotowe</string>
+ <string name="error_invalid_email">nieprawidłowy adres email \'%s\'</string>
+ <string name="error_key_size_minimum512bit">klucz musi mieć rozmiar co najmniej 512 bitów</string>
+ <string name="error_master_key_must_not_be_el_gamal">klucz EnGamal nie może być kluczem głównym</string>
+ <string name="error_unknown_algorithm_choice">wybrano nieznany algorytm</string>
+ <string name="error_user_id_needs_a_name">musisz wskazać imię</string>
+ <string name="error_user_id_no_email">nie znaleziono adresu email</string>
+ <string name="error_user_id_needs_an_email_address">musisz wskazać adres email</string>
+ <string name="error_key_needs_a_user_id">potrzeba co najmniej jednego identyfikatora użytkownika</string>
+ <string name="error_main_user_id_must_not_be_empty">główny identyfikator użytkownika nie może być pusty</string>
+ <string name="error_key_needs_master_key">potrzeba co najmniej klucza głównego</string>
+ <string name="error_no_encryption_keys_or_passphrase">nie podano hasła ani klucza szyfrującego</string>
+ <string name="error_signature_failed">podpisywanie nie powiodło się</string>
+ <string name="error_no_signature_passphrase">nie podano hasła</string>
+ <string name="error_no_signature_key">nie podano klucza podpisu</string>
+ <string name="error_invalid_data">nieprawidłowe dane</string>
+ <string name="error_corrupt_data">uszkodzone dane</string>
+ <string name="error_integrity_check_failed">Sprawdzanie spójności zakończone niepowodzeniem! Dane były modyfikowane!</string>
+ <string name="error_no_symmetric_encryption_packet">nie znaleziono pakietu z szyfrowaniem symatrycznym</string>
+ <string name="error_wrong_passphrase">nieprawidłowe hasło</string>
+ <string name="error_saving_keys">błąd przy zapisywaniu kluczy</string>
+ <string name="error_could_not_extract_private_key">nie można wyodrębnić klucza prywatnego</string>
+ <string name="error_only_files_are_supported">Dane binarne pozbawione pliku nie są obsługiwane. To jest wspierane tylko dla akcji ACTION_ENCRYPT_STREAM_AND_RETURN.</string>
+ <string name="error_jelly_bean_needed">Potrzebujesz Androida 4.1 Jelly Bean, aby korzystać z Android NFC Beam!</string>
+ <string name="error_nfc_needed">NCF jest niedostępne na twoim urządzeniu</string>
+ <string name="error_nothing_import">Nie ma nic do zaimportowania!</string>
+ <string name="error_expiry_must_come_after_creation">data wygaśnięcia musi być późniejsza niż data stworzenia</string>
+ <string name="error_save_first">zapisz najpierw pęk kluczy</string>
+ <string name="error_can_not_delete_contact">nie możesz usunąć tego kontaktu, ponieważ należy do ciebie.</string>
+ <string name="error_can_not_delete_contacts">nie możesz usunąć tych kontaktów, ponieważ należą do ciebie:\n%s</string>
+ <string name="error_keyserver_insufficient_query">Niewystarczające zapytanie do serwera</string>
+ <string name="error_keyserver_query">Odpytywanie serwera zakończone niepowodzeniem</string>
+ <string name="error_keyserver_too_many_responses">Za dużo odpowiedzi</string>
+ <string name="error_import_file_no_content">Plik jest pusty</string>
+ <string name="error_generic_report_bug">Wystąpił błąd ogólny, proszę zgłoś go autorom OpenKeychain.</string>
+ <plurals name="error_can_not_delete_info">
+ <item quantity="one">Usuń go z ekranu \'Moje klucze\'!</item>
+ <item quantity="few">Usuń je z ekranu \'Moje klucze\'!</item>
+ <item quantity="other">Usuń je z ekranu \'Moje klucze\'!</item>
+ </plurals>
+ <plurals name="error_import_non_pgp_part">
+ <item quantity="one">Część wczytanego pliku jest poprawnym obiektem OpenPGP, ale nie jest kluczem OpenPGP</item>
+ <item quantity="few">Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP</item>
+ <item quantity="other">Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP</item>
+ </plurals>
+ <string name="error_change_something_first">Musisz dokonać zmian w pęku kluczy zanim będziesz mógł go zachować</string>
+ <!--progress dialogs, usually ending in '…'-->
+ <string name="progress_done">Gotowe.</string>
+ <string name="progress_cancel">Anuluj</string>
+ <string name="progress_saving">zapisywanie...</string>
+ <string name="progress_importing">importowanie...</string>
+ <string name="progress_exporting">eksportowanie...</string>
+ <string name="progress_building_key">budowanie klucza...</string>
+ <string name="progress_preparing_master_key">przygotowywanie klucza glównego...</string>
+ <string name="progress_certifying_master_key">podpisywanie klucza głównego...</string>
+ <string name="progress_building_master_key">budowanie głównego zbioru kluczy...</string>
+ <string name="progress_adding_sub_keys">dodawanie podkluczy...</string>
+ <string name="progress_saving_key_ring">zapisywanie klucza...</string>
+ <plurals name="progress_exporting_key">
+ <item quantity="one">eksportowanie klucza...</item>
+ <item quantity="few">eksportowanie kluczy...</item>
+ <item quantity="other">eksportowanie kluczy...</item>
+ </plurals>
+ <plurals name="progress_generating">
+ <item quantity="one">generowanie klucza, może to potrwać do 3 minut...</item>
+ <item quantity="few">generowanie kluczy, może to potrwać do 3 minut...</item>
+ <item quantity="other">generowanie kluczy, może to potrwać do 3 minut...</item>
+ </plurals>
+ <string name="progress_extracting_signature_key">wyodrębnianie klucza podpisu...</string>
+ <string name="progress_extracting_key">wyodrębnianie klucza...</string>
+ <string name="progress_preparing_streams">przygotowywanie strumieni...</string>
+ <string name="progress_encrypting">szyfrowanie danych...</string>
+ <string name="progress_decrypting">deszyfrowywanie danych...</string>
+ <string name="progress_preparing_signature">przygotowywanie podpisu...</string>
+ <string name="progress_generating_signature">generowanie podpisu...</string>
+ <string name="progress_processing_signature">przetwarzanie podpisu...</string>
+ <string name="progress_verifying_signature">weryfikowanie podpisu...</string>
+ <string name="progress_signing">podpisywanie...</string>
+ <string name="progress_reading_data">czytanie danych...</string>
+ <string name="progress_finding_key">szukanie klucza...</string>
+ <string name="progress_decompressing_data">dekompresja danych...</string>
+ <string name="progress_verifying_integrity">weryfikacja spójności...</string>
+ <string name="progress_deleting_securely">usuwanie \'%s\' bezpiecznie…</string>
+ <string name="progress_querying">odpytywanie...</string>
+ <!--action strings-->
+ <string name="hint_public_keys">Wyszukaj klucze publiczne</string>
+ <string name="hint_secret_keys">Wyszukaj klucze prywatne</string>
+ <string name="action_share_key_with">Udostępnij klucz...</string>
+ <!--key bit length selections-->
+ <string name="key_size_512">512</string>
+ <string name="key_size_1024">1024</string>
+ <string name="key_size_2048">2048</string>
+ <string name="key_size_4096">4096</string>
+ <!--compression-->
+ <string name="compression_fast">szybka</string>
+ <string name="compression_very_slow">bardzo wolna</string>
+ <!--Help-->
+ <string name="help_tab_start">Początek</string>
+ <string name="help_tab_faq">FAQ</string>
+ <string name="help_tab_nfc_beam">NFC Beam</string>
+ <string name="help_tab_changelog">Dziennik zmian</string>
+ <string name="help_tab_about">O programie</string>
+ <string name="help_about_version">Wersja:</string>
+ <!--Import-->
+ <string name="import_import">Zaimportuj wybrane klucze</string>
+ <string name="import_sign_and_upload">Importuj, podpisz i wyślij wybrane klucze</string>
+ <string name="import_from_clipboard">Importuj ze schowka</string>
+ <plurals name="import_qr_code_missing">
+ <item quantity="one">Brakuje kodu QR o identyfikatorze %s</item>
+ <item quantity="few">Brakuje kodów QR o identyfikatorach %s</item>
+ <item quantity="other">Brakuje kodów QR o identyfikatorach %s</item>
+ </plurals>
+ <string name="import_qr_code_start_with_one">Zacznij od kodu QR o identyfikatorze 1</string>
+ <string name="import_qr_code_wrong">Kod QR zniekształcony! Spróbuj jeszcze raz!</string>
+ <string name="import_qr_code_finished">Skanowanie kodu QR zakończone!</string>
+ <string name="import_qr_code_too_short_fingerprint">Odcisk klucza zawarty w tym kodzie QR jest za krótki (&lt; 16 znaków)</string>
+ <string name="import_qr_scan_button">Odczytaj kod QR przy pomocy \'Barcode Scanner\'</string>
+ <string name="import_nfc_text">Aby odbierać klucze przez NFC, urządzenie musi być odblokowane.</string>
+ <string name="import_nfc_help_button">Pomoc</string>
+ <string name="import_clipboard_button">Odczytaj klucz ze schowka</string>
+ <!--Intent labels-->
+ <string name="intent_decrypt_file">Deszyfruj plik korzystając z OpenKeychain</string>
+ <string name="intent_import_key">Importuj klucz korzystając z OpenKeychain</string>
+ <string name="intent_send_encrypt">Zaszyfruj korzystając z OpenKeychain</string>
+ <string name="intent_send_decrypt">Deszyfruj korzystając z OpenKeychain</string>
+ <!--Remote API-->
+ <string name="api_no_apps">Brak zarejestrowanych aplikacji!\n\nZewnętrzne aplikacje mogą żądać dostępu do OpenKeychain. Po przyznaniu dostępu, będa wyświetlone tutaj.</string>
+ <string name="api_settings_show_info">Pokaż zaawansowane informacje</string>
+ <string name="api_settings_hide_info">Ukryj zaawansowane informacje</string>
+ <string name="api_settings_show_advanced">Pokaż zaawanowane ustawienia</string>
+ <string name="api_settings_hide_advanced">Ukryj zaawansowane ustawienia</string>
+ <string name="api_settings_no_key">Nie wybrano klucza</string>
+ <string name="api_settings_select_key">Wybierz klucz</string>
+ <string name="api_settings_create_key">Utwórz nowy klucz dla tego konta</string>
+ <string name="api_settings_save">Zapisz</string>
+ <string name="api_settings_cancel">Anuluj</string>
+ <string name="api_settings_revoke">Odwołaj dostęp</string>
+ <string name="api_settings_delete_account">Usuń konto</string>
+ <string name="api_settings_package_name">Nazwa paczki</string>
+ <string name="api_settings_package_signature">Skrót SHA-256 podpisu paczki</string>
+ <string name="api_settings_accounts">Konta</string>
+ <string name="api_settings_accounts_empty">Nie przypisano żadnych kont do tej aplikacji</string>
+ <string name="api_create_account_text">Aplikacja prosi o zgodę na utworzenie nowego konta. Wskaż istniejący klucz prywatny lub wygeneruj nowy.\nAplikacje mogą używać wyłącznie klucze które tutaj wskażesz!</string>
+ <string name="api_register_text">Wyświetlona aplikacja prosi o dostęp do OpenKeychain.\nZezwolić?\n\nOSTRZEZENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezwalaj na dostęp! Możesz to również zrobić później, korzystając z ekranu \'Zarejestrowane aplikacje\'.</string>
+ <string name="api_register_allow">Zezwól na dostęp</string>
+ <string name="api_register_disallow">Odmów dostępu</string>
+ <string name="api_register_error_select_key">Wybierz klucz!</string>
+ <string name="api_select_pub_keys_missing_text">Nie znaleziono kluczy publiczych dla tych identyfikatorów użytkownika:</string>
+ <string name="api_select_pub_keys_dublicates_text">Więcej niż jeden klucz publiczny istnieje dla tych identyfikatorów użytkownika:</string>
+ <string name="api_select_pub_keys_text">Proszę przejrzeć listę adresatów!</string>
+ <string name="api_error_wrong_signature">Sprawdzanie podpisu zakończone niepowodzeniem! Czy zainstalowałeś tę aplikację z innego źródła? Jeżeli jesteś pewien, że nie jest to atak, odwołaj rejestrację teg aplikacji w OpenKeychain, a następnie zarejestruj ją ponownie.</string>
+ <!--Share-->
+ <string name="share_qr_code_dialog_title">Udostępnij przez kod QR</string>
+ <string name="share_qr_code_dialog_start">Przejdź przez wszystkiego kody QR korzystając z przycisku \'Nastepny\' i skanuj je pojedynczo.</string>
+ <string name="share_qr_code_dialog_fingerprint_text">Odcisk:</string>
+ <string name="share_qr_code_dialog_progress">Kod QR o identyfikatorze %1$d z %2$d</string>
+ <string name="share_nfc_dialog">Udostępnij przez NFC</string>
+ <!--Key list-->
+ <plurals name="key_list_selected_keys">
+ <item quantity="one">1 klucz wybrany.</item>
+ <item quantity="few">%d kluczy wybranych.</item>
+ <item quantity="other">%d kluczy wybranych.</item>
+ </plurals>
+ <string name="key_list_empty_text1">Żadne klucze nie są jeszcze dostępne...</string>
+ <string name="key_list_empty_text2">Możesz zacząć od</string>
+ <string name="key_list_empty_text3">lub</string>
+ <string name="key_list_empty_button_create">tworzenie własnego klucza</string>
+ <string name="key_list_empty_button_import">importowanie kluczy.</string>
+ <!--Key view-->
+ <string name="key_view_action_edit">Edytuj ten klucz</string>
+ <string name="key_view_action_encrypt">Zaszyfruj do tego kontaktu</string>
+ <string name="key_view_action_certify">Certyfikuj klucz tego kontaktu</string>
+ <string name="key_view_tab_main">Informacje</string>
+ <string name="key_view_tab_certs">Certyfikaty</string>
+ <!--Navigation Drawer-->
+ <string name="nav_contacts">Klucze</string>
+ <string name="nav_encrypt">Podpisz i zaszyfruj</string>
+ <string name="nav_decrypt">Deszyfruj i weryfikuj</string>
+ <string name="nav_import">Importuj klucze</string>
+ <string name="nav_secret_keys">Moje klucze</string>
+ <string name="nav_apps">Zarejestrowane aplikacje</string>
+ <string name="drawer_open">Otwórz panel nawigacji</string>
+ <string name="drawer_close">Zamknij panel nawigacji</string>
+ <string name="edit">Edytuj</string>
+ <string name="my_keys">Moje klucze</string>
+ <string name="label_secret_key">Klucz prywatny</string>
+ <string name="secret_key_yes">dostępny</string>
+ <string name="secret_key_no">niedostepny</string>
+ <string name="section_uids_to_sign">Identyfikator użytkownika do podpisu</string>
+ <string name="progress_re_adding_certs">Ponowne stosowanie certyfikatów</string>
+ <!--hints-->
+ <string name="encrypt_content_edit_text_hint">Wpisz tutaj wiadomość do zaszyfrowania i/lub podpisania...</string>
+ <string name="decrypt_content_edit_text_hint">Wpisz tutaj tekst do zaszyfrowania i/lub zweryfikowania...</string>
+</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml b/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml
index 6bb115049..7b71d3ecf 100644
--- a/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml
@@ -7,6 +7,7 @@
<!--label-->
<string name="unknown_status"></string>
<!--choice-->
+ <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
@@ -23,4 +24,5 @@
<!--Key list-->
<!--Key view-->
<!--Navigation Drawer-->
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml
index 22f676ccb..8ee6d95ca 100644
--- a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml
@@ -16,6 +16,7 @@
<string name="title_change_passphrase">Изменить пароль</string>
<string name="title_set_passphrase">Задать пароль</string>
<string name="title_send_email">Отправить...</string>
+ <string name="title_send_file">Отправить файл</string>
<string name="title_encrypt_to_file">Зашифровать в файл</string>
<string name="title_decrypt_to_file">Расшифровать в файл</string>
<string name="title_import_keys">Импорт ключей</string>
@@ -47,6 +48,7 @@
<string name="btn_certify">Сертифицировать</string>
<string name="btn_decrypt">Расшифровать</string>
<string name="btn_decrypt_verify">Расшифровать и проверить</string>
+ <string name="btn_decrypt_verify_clipboard">Из буфера обмена</string>
<string name="btn_select_encrypt_keys">Выбрать получателей</string>
<string name="btn_encrypt_file">Зашифровать файл</string>
<string name="btn_save">Сохранить</string>
@@ -54,8 +56,8 @@
<string name="btn_delete">Удалить</string>
<string name="btn_no_date">Нет</string>
<string name="btn_okay">Да</string>
- <string name="btn_change_passphrase">Изменить пароль</string>
- <string name="btn_set_passphrase">Задать пароль</string>
+ <string name="btn_change_passphrase">Изменить новый пароль</string>
+ <string name="btn_set_passphrase">Задать новый пароль</string>
<string name="btn_search">Поиск</string>
<string name="btn_export_to_server">Загрузить на сервер ключей</string>
<string name="btn_next">Далее</string>
@@ -63,6 +65,8 @@
<string name="btn_clipboard">Буфер обмена</string>
<string name="btn_share">Поделиться...</string>
<string name="btn_lookup_key">Найти ключ</string>
+ <string name="btn_encryption_advanced_settings_show">Показать расширенные настройки</string>
+ <string name="btn_encryption_advanced_settings_hide">Скрыть расширенные настройки</string>
<!--menu-->
<string name="menu_preferences">Настройки</string>
<string name="menu_help">Помощь</string>
@@ -70,13 +74,15 @@
<string name="menu_import_from_qr_code">Импорт из QR кода</string>
<string name="menu_import">Импорт</string>
<string name="menu_import_from_nfc">Импорт из NFC</string>
- <string name="menu_export_keys">Экспорт всех ключей</string>
+ <string name="menu_export_public_keys">Экспорт всех открытых ключей</string>
+ <string name="menu_export_secret_keys">Экспорт всех секретных ключей</string>
<string name="menu_export_key">Экспорт в файл</string>
<string name="menu_delete_key">Удалить ключ</string>
<string name="menu_create_key">Создать ключ</string>
<string name="menu_create_key_expert">Создать ключ (эксперт)</string>
<string name="menu_search">Поиск</string>
- <string name="menu_key_server">Импорт с сервера ключей</string>
+ <string name="menu_import_from_key_server">Сервер ключей</string>
+ <string name="menu_key_server">Сервер ключей...</string>
<string name="menu_update_key">Обновить с сервера ключей</string>
<string name="menu_export_key_to_server">Загрузить на сервер ключей</string>
<string name="menu_share">Отправить...</string>
@@ -93,6 +99,8 @@
<string name="menu_key_edit_cancel">Отмена</string>
<string name="menu_encrypt_to">Зашифровать....</string>
<string name="menu_select_all">Выбрать все</string>
+ <string name="menu_add_keys">Добавить ключи</string>
+ <string name="menu_export_keys">Экспорт ключей</string>
<!--label-->
<string name="label_sign">Подписать</string>
<string name="label_message">Сообщение</string>
@@ -105,6 +113,7 @@
<string name="label_select_public_keys">Получатели</string>
<string name="label_delete_after_encryption">Удалить после шифрования</string>
<string name="label_delete_after_decryption">Удалить после расшифровки</string>
+ <string name="label_share_after_encryption">Отправить после шифрования</string>
<string name="label_encryption_algorithm">Алгоритм шифрования</string>
<string name="label_hash_algorithm">Hash-алгоритм</string>
<string name="label_asymmetric">Публичный ключ</string>
@@ -135,11 +144,18 @@
<string name="user_id_no_name">&lt;нет имени&gt;</string>
<string name="none">&lt;нет&gt;</string>
<string name="no_key">&lt;нет ключа&gt;</string>
+ <string name="no_email">&lt;нет email&gt;</string>
<string name="unknown_status"></string>
<string name="can_encrypt">шифрование</string>
<string name="can_sign">подпись</string>
<string name="expired">просрочен</string>
<string name="revoked">отозван</string>
+ <string name="user_id">ID пользователя</string>
+ <plurals name="n_contacts">
+ <item quantity="one">1 контакт</item>
+ <item quantity="few">%d контактов</item>
+ <item quantity="other">%d контактов</item>
+ </plurals>
<plurals name="n_key_servers">
<item quantity="one">%d сервер ключей</item>
<item quantity="few">%d серверов ключей</item>
@@ -149,9 +165,6 @@
<string name="secret_key">Секретный ключ:</string>
<!--choice-->
<string name="choice_none">Нет</string>
- <string name="choice_sign_only">Только подпись</string>
- <string name="choice_encrypt_only">Только шифрование</string>
- <string name="choice_sign_and_encrypt">Шифрование и подпись</string>
<string name="choice_15secs">15 секунд</string>
<string name="choice_1min">1 минуту</string>
<string name="choice_3mins">3 минуты</string>
@@ -171,21 +184,25 @@
<string name="warning">Внимание</string>
<string name="error">Ошибка</string>
<string name="error_message">Ошибка: %s</string>
+ <!--key flags-->
+ <string name="flag_certify">Сертифицировать</string>
+ <string name="flag_sign">Подписать</string>
+ <string name="flag_encrypt">Зашифровать</string>
<!--sentences-->
<string name="wrong_passphrase">Неправ. пароль</string>
<string name="using_clipboard_content">Следить за буфером обмена</string>
<string name="set_a_passphrase">Сначала задайте пароль</string>
<string name="no_filemanager_installed">Нет совместимого менеджера файлов.</string>
<string name="passphrases_do_not_match">Пароли не совпадают.</string>
- <string name="passphrase_must_not_be_empty">Пустой пароль недопустим.</string>
+ <string name="passphrase_must_not_be_empty">Пожалуйста, введите пароль.</string>
<string name="passphrase_for_symmetric_encryption">Симметричное шифрование.</string>
<string name="passphrase_for">Введите пароль для\n\'%s\'</string>
<string name="file_delete_confirmation">Вы уверены, что хотите удалить\n%s ?</string>
<string name="file_delete_successful">Удалено.</string>
<string name="no_file_selected">Сначала выберите файл.</string>
- <string name="decryption_successful">Расшифровано.</string>
- <string name="encryption_successful">Зашифровано.</string>
- <string name="encryption_to_clipboard_successful">Зашифровано в буфер обмена.</string>
+ <string name="decryption_successful">Расшифровано и/или проверено.</string>
+ <string name="encryption_successful">Подписано и/или зашифровано.</string>
+ <string name="encryption_to_clipboard_successful">Подписано и/или зашифровано в буфер обмена.</string>
<string name="enter_passphrase_twice">Дважды введите пароль.</string>
<string name="select_encryption_key">Укажите хотя бы один ключ.</string>
<string name="select_encryption_or_signature_key">Выберите хотя бы один ключ для шифрования или подписи.</string>
@@ -196,6 +213,9 @@
<string name="key_deletion_confirmation">Вы уверены, что ходите удалить ключ \'%s\'?\nЭто действие нельзя отменить!</string>
<string name="key_deletion_confirmation_multi">Вы уверены, что хотите удалить ВСЕ выбранные ключи?\nЭто действие нельзя отменить!</string>
<string name="secret_key_deletion_confirmation">Вы уверены, что ходите удалить СЕКРЕТНЫЙ ключ \'%s\'?\nЭто действие нельзя отменить!</string>
+ <string name="public_key_deletetion_confirmation">Вы правда хотите удалить ПУБЛИЧНЫЙ ключ \'%s\'?\nЭто нельзя отменить!</string>
+ <string name="secret_key_delete_text">Удалить секретные ключи?</string>
+ <string name="also_export_secret_keys">Экспортировать секретные ключи?</string>
<plurals name="keys_added_and_updated_1">
<item quantity="one">Успешно добавлено %d ключ</item>
<item quantity="few">Успешно добавлено %d ключей</item>
@@ -221,6 +241,7 @@
<string name="keys_exported">Экспортировано %d ключей.</string>
<string name="no_keys_exported">Ключи не были экспортированы.</string>
<string name="key_creation_el_gamal_info">Инфо: ElGamal подходит только для дополнительных ключей. При создании ключа будет использован ближайший из размеров: 1536, 2048, 3072, 4096, или 8192.</string>
+ <string name="key_creation_weak_rsa_info">Внимание: создание ключей RSA длиной 1024 бита и менее признано небезопасным. Данная возможность отключена.</string>
<string name="key_not_found">Не удается найти ключ %08X.</string>
<plurals name="keys_found">
<item quantity="one">Найден %d ключ.</item>
@@ -254,6 +275,7 @@
<string name="error_master_key_must_not_be_el_gamal">ключ ElGamal не может быть основным</string>
<string name="error_unknown_algorithm_choice">выбран неизвестный алгоритм</string>
<string name="error_user_id_needs_a_name">необходимо указать имя</string>
+ <string name="error_user_id_no_email">email не найден</string>
<string name="error_user_id_needs_an_email_address">необходимо указать email</string>
<string name="error_key_needs_a_user_id">необходим хотя бы один id пользователя</string>
<string name="error_main_user_id_must_not_be_empty">основная запись пользователя не может быть пустой</string>
@@ -279,18 +301,24 @@
<string name="error_keyserver_insufficient_query">Ограничение запроса сервера</string>
<string name="error_keyserver_query">Сбой запроса сервера ключей</string>
<string name="error_keyserver_too_many_responses">Слишком много ответов</string>
+ <string name="error_import_file_no_content">Файл пуст</string>
+ <string name="error_generic_report_bug">Выявлена ошибка. Пожалуйста, сообщите о ней разработчику.</string>
<plurals name="error_can_not_delete_info">
<item quantity="one">Пожалуйста, удалите его в разделе \'Мои ключи\'!</item>
<item quantity="few">Пожалуйста, удалите их в разделе \'Мои ключи\'!</item>
<item quantity="other">Пожалуйста, удалите их в разделе \'Мои ключи\'!</item>
</plurals>
+ <plurals name="error_import_non_pgp_part">
+ <item quantity="one">часть загруженного файла содержит данные OpenPGP, но это не ключ</item>
+ <item quantity="few">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
+ <item quantity="other">части загруженного файла содержат данные OpenPGP, но это не ключ</item>
+ </plurals>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">готово.</string>
- <string name="progress_cancel">отмена</string>
+ <string name="progress_done">Готово.</string>
+ <string name="progress_cancel">Отмена</string>
<string name="progress_saving">сохранение...</string>
<string name="progress_importing">импорт...</string>
<string name="progress_exporting">экспорт...</string>
- <string name="progress_generating">создание ключа. это может занять до 3 минут...</string>
<string name="progress_building_key">создание ключа...</string>
<string name="progress_preparing_master_key">подготовка основного ключа...</string>
<string name="progress_certifying_master_key">сертификация основного ключа...</string>
@@ -302,6 +330,11 @@
<item quantity="few">экспорт ключей...</item>
<item quantity="other">экспорт ключей...</item>
</plurals>
+ <plurals name="progress_generating">
+ <item quantity="one">создание ключа. это может занять до 3 минут...</item>
+ <item quantity="few">создание ключей. это может занять до 3 минут...</item>
+ <item quantity="other">создание ключей. это может занять до 3 минут...</item>
+ </plurals>
<string name="progress_extracting_signature_key">извлечение подписи ключа...</string>
<string name="progress_extracting_key">извлечение ключа...</string>
<string name="progress_preparing_streams">подготовка к передаче...</string>
@@ -332,6 +365,7 @@
<string name="compression_very_slow">очень медленно</string>
<!--Help-->
<string name="help_tab_start">Начать</string>
+ <string name="help_tab_faq">ЧаВо</string>
<string name="help_tab_nfc_beam">NFC Beam</string>
<string name="help_tab_changelog">Изменения</string>
<string name="help_tab_about">О программе</string>
@@ -360,6 +394,8 @@
<string name="intent_send_decrypt">OpenKeychain: Расшифровать</string>
<!--Remote API-->
<string name="api_no_apps">Нет связанных программ!\n\nСторонние программы могут запросить доступ к OpenKeychain, после чего они будут отражаться здесь.</string>
+ <string name="api_settings_show_info">Показать подробную информацию</string>
+ <string name="api_settings_hide_info">Скрыть подробную информацию</string>
<string name="api_settings_show_advanced">Показать расширенные настройки</string>
<string name="api_settings_hide_advanced">Скрыть расширенные настройки</string>
<string name="api_settings_no_key">Ключ не выбран</string>
@@ -395,17 +431,26 @@
<string name="key_list_empty_button_create">создать свой ключ</string>
<string name="key_list_empty_button_import">Импортировать ключи</string>
<!--Key view-->
+ <string name="key_view_action_edit">Изменить ключ</string>
<string name="key_view_action_encrypt">Зашифровать для этого получателя</string>
<string name="key_view_action_certify">Сертифицировать ключ этого контакта</string>
<string name="key_view_tab_main">Информация</string>
<string name="key_view_tab_certs">Сертификация</string>
<!--Navigation Drawer-->
- <string name="nav_contacts">Контакты</string>
- <string name="nav_encrypt">Зашифровать</string>
- <string name="nav_decrypt">Расшифровать</string>
+ <string name="nav_contacts">Ключи</string>
+ <string name="nav_encrypt">Подписать и зашифровать</string>
+ <string name="nav_decrypt">Расшифровать и проверить</string>
<string name="nav_import">Импорт ключей</string>
<string name="nav_secret_keys">Мои ключи</string>
<string name="nav_apps">Связанные приложения</string>
<string name="drawer_open">Открыть панель навигации</string>
<string name="drawer_close">Закрыть панель навигации</string>
+ <string name="edit">Изменить</string>
+ <string name="my_keys">Мои ключи</string>
+ <string name="label_secret_key">Секретный ключ</string>
+ <string name="secret_key_yes">доступен</string>
+ <string name="secret_key_no">не доступен</string>
+ <string name="section_uids_to_sign">Подписываемые ID пользователя</string>
+ <!--hints-->
+ <string name="encrypt_content_edit_text_hint">Напишите сообщение здесь, что бы зашифровать и/или подписать...</string>
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml b/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml
index 6bb115049..7b71d3ecf 100644
--- a/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml
@@ -7,6 +7,7 @@
<!--label-->
<string name="unknown_status"></string>
<!--choice-->
+ <!--key flags-->
<!--sentences-->
<!--errors
no punctuation, all lowercase,
@@ -23,4 +24,5 @@
<!--Key list-->
<!--Key view-->
<!--Navigation Drawer-->
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-tr/strings.xml b/OpenPGP-Keychain/src/main/res/values-tr/strings.xml
index 5bb5225b5..db1d438dd 100644
--- a/OpenPGP-Keychain/src/main/res/values-tr/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-tr/strings.xml
@@ -80,10 +80,10 @@
<string name="warning">Uyarı</string>
<string name="error">Hata</string>
<string name="error_message">Hata: %s</string>
+ <!--key flags-->
<!--sentences-->
<string name="file_delete_successful">Başarıyla silindi.</string>
<string name="no_file_selected">Önce bir dosya seçin.</string>
- <string name="encryption_successful">Başarıyla şifrelendi.</string>
<string name="key_not_found">Anahtar %08X bulunamadı.</string>
<string name="key_sign_success">Anahtar başarıyla imzalandı</string>
<string name="list_empty">Liste boş!</string>
@@ -95,7 +95,6 @@
<string name="error_key_size_minimum512bit">anahtar uzunluğu en az 512bit olmalı</string>
<string name="error_corrupt_data">bozuk veri</string>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">bitti.</string>
<string name="progress_saving">kaydediliyor...</string>
<string name="progress_importing">alıyor...</string>
<string name="progress_exporting">veriyor...</string>
@@ -135,4 +134,5 @@
<!--Key list-->
<!--Key view-->
<!--Navigation Drawer-->
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml
index 7ccb661d3..da9509822 100644
--- a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml
@@ -48,6 +48,7 @@
<string name="btn_certify">Сертифікувати</string>
<string name="btn_decrypt">Розшифрувати</string>
<string name="btn_decrypt_verify">Розшифрувати і Перевірити</string>
+ <string name="btn_decrypt_verify_clipboard">З буфера обміну</string>
<string name="btn_select_encrypt_keys">Вибрати одержувачів</string>
<string name="btn_encrypt_file">Шифрувати файл</string>
<string name="btn_save">Зберегти</string>
@@ -55,8 +56,8 @@
<string name="btn_delete">Вилучити</string>
<string name="btn_no_date">Жоден</string>
<string name="btn_okay">Гаразд</string>
- <string name="btn_change_passphrase">Змінити парольну фразу</string>
- <string name="btn_set_passphrase">Задати парольну фразу</string>
+ <string name="btn_change_passphrase">Змінити нову парольну фразу</string>
+ <string name="btn_set_passphrase">Задати нову парольну фразу</string>
<string name="btn_search">Пошук</string>
<string name="btn_export_to_server">Завантажити на сервер ключів</string>
<string name="btn_next">Далі</string>
@@ -64,6 +65,8 @@
<string name="btn_clipboard">Буфер обміну</string>
<string name="btn_share">Поділитися через…</string>
<string name="btn_lookup_key">Шукати ключ</string>
+ <string name="btn_encryption_advanced_settings_show">Показати додаткові налаштування</string>
+ <string name="btn_encryption_advanced_settings_hide">Приховати додаткові налаштування</string>
<!--menu-->
<string name="menu_preferences">Параметри</string>
<string name="menu_help">Довідка</string>
@@ -71,16 +74,18 @@
<string name="menu_import_from_qr_code">Імпорт з штрих-коду</string>
<string name="menu_import">Імпорт</string>
<string name="menu_import_from_nfc">Імпорт з NFC</string>
- <string name="menu_export_keys">Експортувати усі ключі</string>
+ <string name="menu_export_public_keys">Експортувати усі публічні ключі</string>
+ <string name="menu_export_secret_keys">Експортувати усі секретні ключі</string>
<string name="menu_export_key">Експорт до файлу</string>
<string name="menu_delete_key">Вилучити ключ</string>
<string name="menu_create_key">Створити ключ</string>
<string name="menu_create_key_expert">Створити ключ (експерт)</string>
<string name="menu_search">Пошук</string>
- <string name="menu_key_server">Імпорт з сервера ключів</string>
+ <string name="menu_import_from_key_server">Сервер ключів</string>
+ <string name="menu_key_server">Сервер ключів…</string>
<string name="menu_update_key">Оновити з сервера ключів</string>
<string name="menu_export_key_to_server">Завантажити на сервер ключів</string>
- <string name="menu_share">Поділитися</string>
+ <string name="menu_share">Поділитися…</string>
<string name="menu_share_title_fingerprint">Поділитися відбитком…</string>
<string name="menu_share_title">Поділитися цілим ключем…</string>
<string name="menu_share_default_fingerprint">з…</string>
@@ -94,6 +99,8 @@
<string name="menu_key_edit_cancel">Скасувати</string>
<string name="menu_encrypt_to">Зашифрувати…</string>
<string name="menu_select_all">Вибрати усе</string>
+ <string name="menu_add_keys">Додати ключі</string>
+ <string name="menu_export_keys">Експортувати ключі</string>
<!--label-->
<string name="label_sign">Підпис</string>
<string name="label_message">Повідомлення</string>
@@ -125,8 +132,6 @@
<string name="label_name">Назва</string>
<string name="label_comment">Коментар</string>
<string name="label_email">Ел. пошта</string>
- <string name="label_sign_user_id">Ід підпису користувача</string>
- <string name="label_sign_email">Підписати листа</string>
<string name="label_send_key">Завантажити ключ до вибраного сервера ключів після сертифікації</string>
<string name="label_fingerprint">Відбиток</string>
<string name="select_keys_button_default">Вибрати</string>
@@ -139,12 +144,18 @@
<string name="user_id_no_name">&lt;без імені&gt;</string>
<string name="none">&lt;жоден&gt;</string>
<string name="no_key">&lt;без ключа&gt;</string>
+ <string name="no_email">&lt;Немає ел. пошти&gt;</string>
<string name="unknown_status"></string>
<string name="can_encrypt">можна зашифрувати</string>
<string name="can_sign">можна підписати</string>
<string name="expired">закінчився</string>
<string name="revoked">скасовано</string>
<string name="user_id">ІД користувача</string>
+ <plurals name="n_contacts">
+ <item quantity="one">1 контакт</item>
+ <item quantity="few">%d контакти</item>
+ <item quantity="other">%d контактів</item>
+ </plurals>
<plurals name="n_key_servers">
<item quantity="one">%d сервер ключів</item>
<item quantity="few">%d сервери ключів</item>
@@ -154,9 +165,6 @@
<string name="secret_key">Секретний ключ:</string>
<!--choice-->
<string name="choice_none">Жоден</string>
- <string name="choice_sign_only">Підписати лише</string>
- <string name="choice_encrypt_only">Шифрувати тільки</string>
- <string name="choice_sign_and_encrypt">Шифрувати і розшифрувати</string>
<string name="choice_15secs">15 секунд</string>
<string name="choice_1min">1 хв</string>
<string name="choice_3mins">3 хв</string>
@@ -176,21 +184,26 @@
<string name="warning">Попередження</string>
<string name="error">Помилка</string>
<string name="error_message">Помилка: %s</string>
+ <!--key flags-->
+ <string name="flag_certify">Сертифікувати</string>
+ <string name="flag_sign">Підписати</string>
+ <string name="flag_encrypt">Зашифрувати</string>
+ <string name="flag_authenticate">Перевірити справжність</string>
<!--sentences-->
<string name="wrong_passphrase">Невірна парольна фраза.</string>
<string name="using_clipboard_content">Використання вмісту буфера обміну.</string>
<string name="set_a_passphrase">Спершу задайте парольну фразу.</string>
<string name="no_filemanager_installed">Нема встановленого сумісного менеджера файлів.</string>
<string name="passphrases_do_not_match">Парольні фрази не збігаються.</string>
- <string name="passphrase_must_not_be_empty">Порожні парольні фрази не дозволені.</string>
+ <string name="passphrase_must_not_be_empty">Будь ласка, введіть парольну фразу.</string>
<string name="passphrase_for_symmetric_encryption">Симетричне шифрування.</string>
<string name="passphrase_for">Введіть парольну фразу для \'%s\'</string>
<string name="file_delete_confirmation">Ви справді хочете вилучити\n%s?</string>
<string name="file_delete_successful">Успішно вилучено.</string>
<string name="no_file_selected">Виберіть спершу файл.</string>
- <string name="decryption_successful">Успішно розшифровано.</string>
- <string name="encryption_successful">Успішно зашифровано.</string>
- <string name="encryption_to_clipboard_successful">Успішно зашифровано до буфера обміну.</string>
+ <string name="decryption_successful">Успішно розшифровано та/або перевірено.</string>
+ <string name="encryption_successful">Успішно підписано та/або перевірено.</string>
+ <string name="encryption_to_clipboard_successful">Успішно підписано та/або зашифровано до буфера обміну.</string>
<string name="enter_passphrase_twice">Введіть двічі парольну фразу.</string>
<string name="select_encryption_key">Виберіть принаймні один ключ шифрування.</string>
<string name="select_encryption_or_signature_key">Виберіть принаймні один ключ шифрування або ключ підпису.</string>
@@ -201,6 +214,11 @@
<string name="key_deletion_confirmation">Ви справді хочете вилучити ключ \'%s\'?\nВи не зможете це відмінити!</string>
<string name="key_deletion_confirmation_multi">Ви справді хочете вилучити усі вибрані ключі?\nВи не зможете це відмінити!</string>
<string name="secret_key_deletion_confirmation">Ви справді хочете вилучити секретний ключ \'%s\'?\nВи не зможете це відмінити!</string>
+ <string name="ask_save_changed_key">Ви внесли зміни до в\'язки ключів, ви б хотіли. Волієте їх зберегти?</string>
+ <string name="ask_empty_id_ok">Ви вже додали порожній ідентифікатор користувача. Справді хочете продовжити?</string>
+ <string name="public_key_deletetion_confirmation">Справді волієте вилучити ВІДКРИТИЙ ключ \'%s\'?\nВи е зможете відмінити цю дію!</string>
+ <string name="secret_key_delete_text">Видалити секретні ключі?</string>
+ <string name="also_export_secret_keys">Також експортувати секретні ключі?</string>
<plurals name="keys_added_and_updated_1">
<item quantity="one">Успішно додано %d ключ</item>
<item quantity="few">Успішно додано %d ключі</item>
@@ -226,6 +244,7 @@
<string name="keys_exported">Успішно експортовано %d ключів.</string>
<string name="no_keys_exported">Жодного ключа не експортовано.</string>
<string name="key_creation_el_gamal_info">Примітка: тільки підключі підтримують ElGamal, а для ElGamal буде використаний найближчий розмір ключа з 1536, 2048, 3072, 4096, або 8192.</string>
+ <string name="key_creation_weak_rsa_info">Примітка: генерація ключа RSA з довжиною 1024 біти і менше вважається небезпечною і вона вимкнена для генерації нових ключів.</string>
<string name="key_not_found">Не можливо знайти ключ %08X.</string>
<plurals name="keys_found">
<item quantity="one">Знайдено %d ключ.</item>
@@ -259,6 +278,7 @@
<string name="error_master_key_must_not_be_el_gamal">основний ключ не може бути ключем ElGamal</string>
<string name="error_unknown_algorithm_choice">вибір невідомого алгоритму</string>
<string name="error_user_id_needs_a_name">вам потрібно вказати назву</string>
+ <string name="error_user_id_no_email">жодного листа не знайдено</string>
<string name="error_user_id_needs_an_email_address">вам потрібно вказати електронну адресу</string>
<string name="error_key_needs_a_user_id">потрібний хоча б один ІД користувача</string>
<string name="error_main_user_id_must_not_be_empty">ІД основного користувача не має бути порожнім</string>
@@ -279,6 +299,7 @@
<string name="error_nfc_needed">NFC недоступний на вашому пристрої!</string>
<string name="error_nothing_import">Нема що імпортувати!</string>
<string name="error_expiry_must_come_after_creation">дата завершення дії має йти після дати створення</string>
+ <string name="error_save_first">спершу збережіть в\'язку ключів</string>
<string name="error_can_not_delete_contact">ви не можете вилучити цей контакт, тому що він ваш власний.</string>
<string name="error_can_not_delete_contacts">ви не можете вилучити наступні контакти, тому що вони - ваші власні:\n%s</string>
<string name="error_keyserver_insufficient_query">Запит обмеженого сервера</string>
@@ -296,13 +317,13 @@
<item quantity="few">частини завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
<item quantity="other">частин завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP</item>
</plurals>
+ <string name="error_change_something_first">Вам потрібно внести зміни до в\'язки ключів перед тим, як зможете їх зберегти.</string>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">готово.</string>
- <string name="progress_cancel">cкасувати</string>
+ <string name="progress_done">Готово.</string>
+ <string name="progress_cancel">Скасувати</string>
<string name="progress_saving">збереження…</string>
<string name="progress_importing">імпортується…</string>
<string name="progress_exporting">експортується…</string>
- <string name="progress_generating">генерується ключ, вона може тривати до 3 хвилин…</string>
<string name="progress_building_key">будується ключ…</string>
<string name="progress_preparing_master_key">підготовка основного ключа…</string>
<string name="progress_certifying_master_key">сертифікація основного ключа…</string>
@@ -314,6 +335,11 @@
<item quantity="few">експортуються ключі…</item>
<item quantity="other">експортуються ключі…</item>
</plurals>
+ <plurals name="progress_generating">
+ <item quantity="one">генерується ключ, це може тривати до 3 хвилини</item>
+ <item quantity="few">генеруються ключі, це може тривати до 3 хвилини</item>
+ <item quantity="other">генеруються ключі, це може тривати до 3 хвилини</item>
+ </plurals>
<string name="progress_extracting_signature_key">видобування ключа підпису…</string>
<string name="progress_extracting_key">видобувається ключа…</string>
<string name="progress_preparing_streams">підготовка потоків…</string>
@@ -344,6 +370,7 @@
<string name="compression_very_slow">дуже повільне</string>
<!--Help-->
<string name="help_tab_start">Початок</string>
+ <string name="help_tab_faq">ЧАП</string>
<string name="help_tab_nfc_beam">NFC промінь</string>
<string name="help_tab_changelog">Журнал змін</string>
<string name="help_tab_about">Про</string>
@@ -372,15 +399,22 @@
<string name="intent_send_decrypt">Розшифрувати з OpenKeychain</string>
<!--Remote API-->
<string name="api_no_apps">Нема зареєстрованих програм!\n\nСтороні програми можуть вимагати доступ до OpenPGP Keychain. Після надання доступу вони будуть наведені тут.</string>
+ <string name="api_settings_show_info">Показати додаткову інформацію</string>
+ <string name="api_settings_hide_info">Приховати додаткову інформацію</string>
<string name="api_settings_show_advanced">Показати додаткові налаштування</string>
<string name="api_settings_hide_advanced">Приховати додаткові налаштування</string>
<string name="api_settings_no_key">Не вибрано ключа</string>
<string name="api_settings_select_key">Вибрати ключ</string>
+ <string name="api_settings_create_key">Створити новий ключ для цього профілю</string>
<string name="api_settings_save">Зберегти</string>
<string name="api_settings_cancel">Скасувати</string>
<string name="api_settings_revoke">Відкликати доступ</string>
+ <string name="api_settings_delete_account">Видалити профіль</string>
<string name="api_settings_package_name">Назва пакунку</string>
<string name="api_settings_package_signature">SHA-256 підписку пакунку</string>
+ <string name="api_settings_accounts">Облікові записи</string>
+ <string name="api_settings_accounts_empty">Немає облікового запису приєднаного до цієї програми.</string>
+ <string name="api_create_account_text">Ця програма вимагає створення нового профілю. Будь ласка, виберіть наявний приватний ключ або створіть інший.\nПрограми обмежені використання ключів, які ви тут оберете!</string>
<string name="api_register_text">Показана програма запитує доступ до OpenPGP Keychain.\nДозволити доступ?\n\nУВАГА: якщо ви не знаєте, чому цей екран появився, не дозволяйте доступ! Ви можете відкликати доступ пізніше, використовуючи екран \'Зареєстровані програми\'.</string>
<string name="api_register_allow">Дозволити доступ</string>
<string name="api_register_disallow">Не дозволити доступ</string>
@@ -407,17 +441,28 @@
<string name="key_list_empty_button_create">створюється ваш власний ключ</string>
<string name="key_list_empty_button_import">імпортуюся ключі.</string>
<!--Key view-->
+ <string name="key_view_action_edit">Редагувати цей ключ</string>
<string name="key_view_action_encrypt">Зашифрувати у цей контакт</string>
<string name="key_view_action_certify">Сертифікувати ключ цього контакту</string>
<string name="key_view_tab_main">Інформація</string>
<string name="key_view_tab_certs">Сертифікати</string>
<!--Navigation Drawer-->
- <string name="nav_contacts">Контакти</string>
- <string name="nav_encrypt">Зашифрувати</string>
- <string name="nav_decrypt">Розшифрувати</string>
+ <string name="nav_contacts">Ключі</string>
+ <string name="nav_encrypt">Підписати і зашифрувати</string>
+ <string name="nav_decrypt">Розшифрувати і Перевірити</string>
<string name="nav_import">Імпортувати ключі</string>
<string name="nav_secret_keys">Мої ключі</string>
<string name="nav_apps">Зареєстровані програми</string>
<string name="drawer_open">Відкрити панель навігації</string>
<string name="drawer_close">Закрити панель навігації</string>
+ <string name="edit">Редагувати</string>
+ <string name="my_keys">Мої ключі</string>
+ <string name="label_secret_key">Секретний ключ</string>
+ <string name="secret_key_yes">доступний</string>
+ <string name="secret_key_no">недоступний</string>
+ <string name="section_uids_to_sign">ІД користувача для реєстрації</string>
+ <string name="progress_re_adding_certs">Перезастосування сертифікатів</string>
+ <!--hints-->
+ <string name="encrypt_content_edit_text_hint">Напишіть повідомлення для шифрування та/або підпису…</string>
+ <string name="decrypt_content_edit_text_hint">Уведіть зашифрований текст тут для його розшифрування та/або перевірки…</string>
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml b/OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000..7b71d3ecf
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,28 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources>
+ <!--title-->
+ <!--section-->
+ <!--button-->
+ <!--menu-->
+ <!--label-->
+ <string name="unknown_status"></string>
+ <!--choice-->
+ <!--key flags-->
+ <!--sentences-->
+ <!--errors
+ no punctuation, all lowercase,
+ they will be put after "error_message", e.g. "Error: file not found"-->
+ <!--progress dialogs, usually ending in '…'-->
+ <!--action strings-->
+ <!--key bit length selections-->
+ <!--compression-->
+ <!--Help-->
+ <!--Import-->
+ <!--Intent labels-->
+ <!--Remote API-->
+ <!--Share-->
+ <!--Key list-->
+ <!--Key view-->
+ <!--Navigation Drawer-->
+ <!--hints-->
+</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values-zh/strings.xml b/OpenPGP-Keychain/src/main/res/values-zh/strings.xml
index 80413d589..d569050fa 100644
--- a/OpenPGP-Keychain/src/main/res/values-zh/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values-zh/strings.xml
@@ -1,6 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--title-->
+ <string name="title_manage_public_keys">聯絡人</string>
<string name="title_select_recipients">选择公钥</string>
<string name="title_select_secret_key">选择私钥</string>
<string name="title_encrypt">加密</string>
@@ -50,7 +51,6 @@
<string name="menu_create_key">创建密钥</string>
<string name="menu_create_key_expert">创建密钥(专家)</string>
<string name="menu_search">搜索</string>
- <string name="menu_share">分享</string>
<string name="menu_copy_to_clipboard">复制到剪贴板</string>
<string name="menu_sign_key">签署密钥</string>
<string name="menu_key_edit_cancel">取消</string>
@@ -79,9 +79,6 @@
<string name="expired">过期了</string>
<!--choice-->
<string name="choice_none">没有</string>
- <string name="choice_sign_only">仅签署</string>
- <string name="choice_encrypt_only">仅加密</string>
- <string name="choice_sign_and_encrypt">签署并加密</string>
<string name="choice_15secs">15秒</string>
<string name="choice_1min">1分钟</string>
<string name="choice_3mins">3分钟</string>
@@ -97,17 +94,14 @@
<string name="filemanager_title_open">打开...</string>
<string name="warning">警告</string>
<string name="error">错误</string>
+ <!--key flags-->
<!--sentences-->
<string name="set_a_passphrase">先设置密钥</string>
<string name="no_filemanager_installed">安装了不匹配的文件管理器</string>
<string name="passphrases_do_not_match">密钥不匹配</string>
- <string name="passphrase_must_not_be_empty">不允许空的密钥</string>
<string name="passphrase_for_symmetric_encryption">对称加密</string>
<string name="file_delete_successful">删除成功</string>
<string name="no_file_selected">先选择一个文件</string>
- <string name="decryption_successful">解密成功</string>
- <string name="encryption_successful">加密成功</string>
- <string name="encryption_to_clipboard_successful">成功地加密到了剪贴板</string>
<string name="enter_passphrase_twice">输入两次密钥</string>
<string name="select_encryption_key">选择至少一个加密密钥</string>
<string name="select_encryption_or_signature_key">选择至少一个加密密钥或者签名密钥</string>
@@ -135,7 +129,6 @@
<string name="error_corrupt_data">损坏的数据</string>
<string name="error_wrong_passphrase">错误的密语</string>
<!--progress dialogs, usually ending in '…'-->
- <string name="progress_done">完成。</string>
<string name="progress_saving">保存...</string>
<string name="progress_importing">导入中...</string>
<string name="progress_exporting">导出中...</string>
@@ -183,8 +176,7 @@
<string name="key_list_empty_text3">或者</string>
<!--Key view-->
<!--Navigation Drawer-->
- <string name="nav_encrypt">加密</string>
- <string name="nav_decrypt">解密</string>
<string name="nav_import">导入密钥</string>
<string name="nav_secret_keys">我的密钥</string>
+ <!--hints-->
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/values/arrays.xml b/OpenPGP-Keychain/src/main/res/values/arrays.xml
index 5244de419..c84c2648d 100644
--- a/OpenPGP-Keychain/src/main/res/values/arrays.xml
+++ b/OpenPGP-Keychain/src/main/res/values/arrays.xml
@@ -36,7 +36,7 @@
<item>@string/key_size_4096</item>
</string-array>
<string-array name="import_action_list" translatable="false">
- <item>@string/menu_key_server</item>
+ <item>@string/menu_import_from_key_server</item>
<item>@string/menu_import_from_file</item>
<item>@string/menu_import_from_qr_code</item>
<item>@string/import_from_clipboard</item>
diff --git a/OpenPGP-Keychain/src/main/res/values/attr.xml b/OpenPGP-Keychain/src/main/res/values/attr.xml
new file mode 100644
index 000000000..86622b3e0
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values/attr.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <declare-styleable name="FoldableLinearLayout">
+ <attr name="foldedLabel" format="string" />
+ <attr name="unFoldedLabel" format="string" />
+ <attr name="foldedIcon" format="string" />
+ <attr name="unFoldedIcon" format="string" />
+ </declare-styleable>
+
+</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/values/dimens.xml b/OpenPGP-Keychain/src/main/res/values/dimens.xml
new file mode 100644
index 000000000..e1a7749f0
--- /dev/null
+++ b/OpenPGP-Keychain/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <dimen name="drawer_size">240dp</dimen>
+ <dimen name="drawer_content_padding">0dp</dimen>
+</resources> \ No newline at end of file
diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml
index 350c57002..55971003b 100644
--- a/OpenPGP-Keychain/src/main/res/values/strings.xml
+++ b/OpenPGP-Keychain/src/main/res/values/strings.xml
@@ -51,6 +51,7 @@
<string name="btn_certify">Certify</string>
<string name="btn_decrypt">Decrypt</string>
<string name="btn_decrypt_verify">Decrypt and Verify</string>
+ <string name="btn_decrypt_verify_clipboard">From Clipboard</string>
<string name="btn_select_encrypt_keys">Select Recipients</string>
<string name="btn_encrypt_file">Encrypt File</string>
<string name="btn_save">Save</string>
@@ -58,8 +59,8 @@
<string name="btn_delete">Delete</string>
<string name="btn_no_date">None</string>
<string name="btn_okay">Okay</string>
- <string name="btn_change_passphrase">Change Passphrase</string>
- <string name="btn_set_passphrase">Set Passphrase</string>
+ <string name="btn_change_passphrase">Change New Passphrase</string>
+ <string name="btn_set_passphrase">Set New Passphrase</string>
<string name="btn_search">Search</string>
<string name="btn_export_to_server">Upload To Keyserver</string>
<string name="btn_next">Next</string>
@@ -77,17 +78,18 @@
<string name="menu_import_from_qr_code">Import from QR Code</string>
<string name="menu_import">Import</string>
<string name="menu_import_from_nfc">Import from NFC</string>
- <string name="menu_export_keys">Export all keys</string>
+ <string name="menu_export_public_keys">Export all public keys</string>
<string name="menu_export_secret_keys">Export all secret keys</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_search">Search</string>
- <string name="menu_key_server">Import from keyserver</string>
+ <string name="menu_import_from_key_server">Keyserver</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">Share…</string>
<string name="menu_share_title_fingerprint">Share fingerprint…</string>
<string name="menu_share_title">Share whole key…</string>
<string name="menu_share_default_fingerprint">with…</string>
@@ -101,6 +103,8 @@
<string name="menu_key_edit_cancel">Cancel</string>
<string name="menu_encrypt_to">Encrypt to…</string>
<string name="menu_select_all">Select all</string>
+ <string name="menu_add_keys">Add keys</string>
+ <string name="menu_export_keys">Export keys</string>
<!-- label -->
<string name="label_sign">Sign</string>
@@ -117,8 +121,8 @@
<string name="label_share_after_encryption">Share After Encryption</string>
<string name="label_encryption_algorithm">Encryption Algorithm</string>
<string name="label_hash_algorithm">Hash Algorithm</string>
- <string name="label_asymmetric">Public Key</string>
- <string name="label_symmetric">Passphrase</string>
+ <string name="label_asymmetric">with Public Key</string>
+ <string name="label_symmetric">with Passphrase</string>
<string name="label_passphrase_cache_ttl">Passphrase Cache</string>
<string name="label_message_compression">Message Compression</string>
<string name="label_file_compression">File Compression</string>
@@ -133,8 +137,6 @@
<string name="label_name">Name</string>
<string name="label_comment">Comment</string>
<string name="label_email">Email</string>
- <string name="label_sign_user_id">Sign User Id</string>
- <string name="label_sign_email">Sign email</string>
<string name="label_send_key">Upload key to selected keyserver after certification</string>
<string name="label_fingerprint">Fingerprint</string>
<string name="select_keys_button_default">Select</string>
@@ -171,9 +173,6 @@
<!-- choice -->
<string name="choice_none">None</string>
- <string name="choice_sign_only">Sign only</string>
- <string name="choice_encrypt_only">Encrypt only</string>
- <string name="choice_sign_and_encrypt">Sign and Encrypt</string>
<string name="choice_15secs">15 secs</string>
<string name="choice_1min">1 min</string>
<string name="choice_3mins">3 mins</string>
@@ -194,21 +193,27 @@
<string name="error">Error</string>
<string name="error_message">Error: %s</string>
+ <!-- key flags -->
+ <string name="flag_certify">Certify</string>
+ <string name="flag_sign">Sign</string>
+ <string name="flag_encrypt">Encrypt</string>
+ <string name="flag_authenticate">Authenticate</string>
+
<!-- sentences -->
<string name="wrong_passphrase">Wrong passphrase.</string>
<string name="using_clipboard_content">Using clipboard content.</string>
<string name="set_a_passphrase">Set a passphrase first.</string>
<string name="no_filemanager_installed">No compatible file manager installed.</string>
<string name="passphrases_do_not_match">The passphrases didn\'t match.</string>
- <string name="passphrase_must_not_be_empty">Empty passphrases are not allowed.</string>
+ <string name="passphrase_must_not_be_empty">Please enter a passphrase.</string>
<string name="passphrase_for_symmetric_encryption">Symmetric encryption.</string>
<string name="passphrase_for">Enter passphrase for \'%s\'</string>
<string name="file_delete_confirmation">Are you sure you want to delete\n%s?</string>
<string name="file_delete_successful">Successfully deleted.</string>
<string name="no_file_selected">Select a file first.</string>
- <string name="decryption_successful">Successfully decrypted.</string>
- <string name="encryption_successful">Successfully encrypted.</string>
- <string name="encryption_to_clipboard_successful">Successfully encrypted to clipboard.</string>
+ <string name="decryption_successful">Successfully decrypted and/or verified.</string>
+ <string name="encryption_successful">Successfully signed and/or encrypted.</string>
+ <string name="encryption_to_clipboard_successful">Successfully signed and/or encrypted to clipboard.</string>
<string name="enter_passphrase_twice">Enter the passphrase twice.</string>
<string name="select_encryption_key">Select at least one encryption key.</string>
<string name="select_encryption_or_signature_key">Select at least one encryption key or a signature key.</string>
@@ -219,6 +224,11 @@
<string name="key_deletion_confirmation">Do you really want to delete the key \'%s\'?\nYou can\'t undo this!</string>
<string name="key_deletion_confirmation_multi">Do you really want to delete all selected keys?\nYou can\'t undo this!</string>
<string name="secret_key_deletion_confirmation">Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this!</string>
+ <string name="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 user ID, 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="secret_key_delete_text">Delete Secret Keys ?</string>
+ <string name="also_export_secret_keys">Also export secret keys?</string>
<plurals name="keys_added_and_updated_1">
<item quantity="one">Successfully added %d key</item>
@@ -242,6 +252,7 @@
<string name="keys_exported">Successfully exported %d keys.</string>
<string name="no_keys_exported">No keys exported.</string>
<string name="key_creation_el_gamal_info">Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used.</string>
+ <string name="key_creation_weak_rsa_info">Note: generating RSA key with length 1024-bit and less is considered unsafe and it\'s disabled for generating new keys.</string>
<string name="key_not_found">Couldn\'t find key %08X.</string>
<plurals name="keys_found">
@@ -301,6 +312,7 @@
<string name="error_nfc_needed">NFC is not available on your device!</string>
<string name="error_nothing_import">Nothing to import!</string>
<string name="error_expiry_must_come_after_creation">expiry date must come after creation date</string>
+ <string name="error_save_first">please save the keyring first</string>
<string name="error_can_not_delete_contact">you can not delete this contact because it is your own.</string>
<string name="error_can_not_delete_contacts">you can not delete the following contacts because they are your own:\n%s</string>
<string name="error_keyserver_insufficient_query">Insufficient server query</string>
@@ -316,6 +328,7 @@
<item quantity="one">part of the loaded file is a valid OpenPGP object but not a OpenPGP key</item>
<item quantity="other">parts of the loaded file are valid OpenPGP objects but not OpenPGP keys</item>
</plurals>
+ <string name="error_change_something_first">You must make changes to the keyring before you can save it</string>
<!-- progress dialogs, usually ending in '…' -->
<string name="progress_done">Done.</string>
@@ -393,7 +406,7 @@
<string name="import_qr_code_start_with_one">Please start with QR Code with ID 1</string>
<string name="import_qr_code_wrong">QR Code malformed! Please try again!</string>
<string name="import_qr_code_finished">QR Code scanning finished!</string>
- <string name="import_qr_code_too_short_fingerprint">Fingerprint contained in this QR Code is too short (&lt; 16 characters)</string>
+ <string name="import_qr_code_too_short_fingerprint">Fingerprint is too short (&lt; 16 characters)</string>
<string name="import_qr_scan_button">Scan QR Code with \'Barcode Scanner\'</string>
<string name="import_nfc_text">To receive keys via NFC, the device needs to be unlocked.</string>
<string name="import_nfc_help_button">Help</string>
@@ -407,15 +420,22 @@
<!-- Remote API -->
<string name="api_no_apps">No registered applications!\n\nThird-party applications can request access to OpenKeychain. After granting access, they will be listed here.</string>
+ <string name="api_settings_show_info">Show advanced information</string>
+ <string name="api_settings_hide_info">Hide advanced information</string>
<string name="api_settings_show_advanced">Show advanced settings</string>
<string name="api_settings_hide_advanced">Hide advanced settings</string>
<string name="api_settings_no_key">No key selected</string>
<string name="api_settings_select_key">Select key</string>
+ <string name="api_settings_create_key">Create new key for this account</string>
<string name="api_settings_save">Save</string>
<string name="api_settings_cancel">Cancel</string>
<string name="api_settings_revoke">Revoke access</string>
+ <string name="api_settings_delete_account">Delete account</string>
<string name="api_settings_package_name">Package Name</string>
<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 application.</string>
+ <string name="api_create_account_text">The application requests the creation of a new account. Please select an existing private key or create a new one.\nApplications are restricted to the usage of keys you select here!</string>
<string name="api_register_text">The displayed application requests access to OpenKeychain.\nAllow access?\n\nWARNING: If you do not know why this screen appeared, disallow access! You can revoke access later using the \'Registered Applications\' screen.</string>
<string name="api_register_allow">Allow access</string>
<string name="api_register_disallow">Disallow access</string>
@@ -452,9 +472,9 @@
<string name="key_view_tab_certs">Certifications</string>
<!-- Navigation Drawer -->
- <string name="nav_contacts">Contacts</string>
- <string name="nav_encrypt">Encrypt</string>
- <string name="nav_decrypt">Decrypt</string>
+ <string name="nav_contacts">Keys</string>
+ <string name="nav_encrypt">Sign and Encrypt</string>
+ <string name="nav_decrypt">Decrypt and Verify</string>
<string name="nav_import">Import Keys</string>
<string name="nav_secret_keys">My Keys</string>
<string name="nav_apps">Registered Apps</string>
@@ -484,4 +504,8 @@
<string name="certs_list_known">Show by known public keys</string>
<string name="certs_list_all">Show all certificates</string>
+ <!-- hints -->
+ <string name="encrypt_content_edit_text_hint">Write message here to encrypt and/or sign…</string>
+ <string name="decrypt_content_edit_text_hint">Enter ciphertext here to decrypt and/or verify…</string>
+
</resources>
diff --git a/OpenPGP-Keychain/src/main/res/xml/adv_preferences.xml b/OpenPGP-Keychain/src/main/res/xml/adv_preferences.xml
index 2705bd22f..03f93b051 100644
--- a/OpenPGP-Keychain/src/main/res/xml/adv_preferences.xml
+++ b/OpenPGP-Keychain/src/main/res/xml/adv_preferences.xml
@@ -35,7 +35,7 @@
android:title="@string/label_file_compression" />
<CheckBoxPreference
- android:key="defaultAsciiArmour"
+ android:key="defaultAsciiArmor"
android:persistent="false"
android:title="@string/label_ascii_armor" />
</PreferenceCategory>
@@ -45,4 +45,4 @@
android:persistent="false"
android:title="@string/label_force_v3_signature" />
</PreferenceCategory>
-</PreferenceScreen> \ No newline at end of file
+</PreferenceScreen>